pax_global_header00006660000000000000000000000064147524027470014526gustar00rootroot0000000000000052 comment=fda906dc9e74c91eb4bfc3efc019db0c6dbc1dac statistics-release-1.7.3/000077500000000000000000000000001475240274700153465ustar00rootroot00000000000000statistics-release-1.7.3/.gitattributes000066400000000000000000000000231475240274700202340ustar00rootroot00000000000000docs export-ignore statistics-release-1.7.3/CONTRIBUTING.md000066400000000000000000000126421475240274700176040ustar00rootroot00000000000000# Contribution Guidelines #### 1. License and Documentation The **statistics** package is distributed under [GNU General Public License (GPL)](https://www.gnu.org/licenses/gpl-3.0.en.html) with few minor exceptions. Some functions are in the Public Domain. If you are submitting a few function, it should be licensed under GPLv3+ with the following header (use appropriate year, name, etc.) as shown below: ``` ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, ## see . ``` New functions should be properly documented with the embedded help file in [Texinfo](https://www.gnu.org/software/texinfo/) format. This part should be placed outside (before) the function's main body and after the License block. The Texinfo manual can be found [here](https://www.gnu.org/software/texinfo/manual/texinfo/) although reading through existing function files should do the trick. ``` ## -*- texinfo -*- ## @deftypefn {Function File} @var{p} = anova1 (@var{x}) ## ## Help file goes here. ## ## @end deftypefn ``` **Note:** the texinfo is not printed on the command window screen as it appears in the source file. Type e.g. `help anova1` to display the help document in `anova1` function in order to review it. #### 2. Demos Although examples of using a function can go into the help documentation, it is always useful to have example embedded as demos, which the user can invoke with the `demo` command. ``` e.g. >> demo anova1 ``` These should go at the end of the file after the function or any other helper functions that might be present in the file. A small demo sample is shown below. ``` %!demo %! x = meshgrid (1:6); %! x = x + normrnd (0, 1, 6, 6); %! anova1 (x, [], 'off'); ``` Note: demos can also contain comments which are printed when a demo is ran and may be very useful for the user to understand the function's usage. Embed comments in the usual manner after the `%!` starting characters in each line. For example: ``` %!demo %! x = [46 64 83 105 123 150 150]; %! c = [0 0 0 0 0 0 1]; %! f = [1 1 1 1 1 1 4]; %! ## Subtract 30.92 from x to simulate a 3 parameter wbl with gamma = 30.92 %! wblplot (x - 30.92, c, f, 0.05); ``` #### 3. BISTs (testing suite) It is also **very important** that function files contain a testing suite that will test for correct output, properly catching errors etc. BISTs should go at the bottom the file using `%!` starting characters. An example of such a testing suite is shown below. ``` %!error chi2gof () %!error chi2gof ([2,3;3,4]) %!error chi2gof ([1,2,3,4], "nbins", 3, "ctrs", [2,3,4]) %!error chi2gof ([1,2,3,4], "expected", [3,2,2], "nbins", 5) %!test %! x = [1 2 1 3 2 4 3 2 4 3 2 2]; %! [h, p, stats] = chi2gof (x); %! assert (h, 0); %! assert (p, NaN); %! assert (stats.chi2stat, 0.1205375022748029, 1e-14); ``` It is best practice to append tests during development, since it will save time debugging and it will certainly help catching marginal errors that would otherwise have quickly come back as bug reports. It also **important** appending tests when fixing a bug or implementing some new functionality into an existing function. Please, add BISTs, they help a lot with the maintenance :wink::innocent: of the statistics package. There is not such as a thing as too many tests in a function. :metal::v: #### 4. Coding style The coding style of GNU Octave should be used. In general, limit the lines at 80 characters long. - Use `LF` (unix) for end of lines, and NOT `CRLF` (windows). - Use `##` for comments. Don't use `%` or `%%` as in Matlab. - Use `!` instead of `~` for logical NOT. ``` a != 0; b(! isnnan (a)) = []; ``` - **Don't use tabs!** Indent the bodies of statement blocks with 2 spaces. - When calling functions, put spaces after commas and before the calling parentheses, as shown below: ``` x = max (sin (y+3), 2); ``` - An exception are matrix or cell constructors. ``` >> a = [sin(x), cos(x)]; >> b = {sin(x), cos(x)}; ``` **Note:** spaces in the above example would result in a parse error! - For an indexing expression, do not put a space after the identifier (this differentiates indexing and function calls nicely). - When indexing, the space after a comma is not necessary if index expressions are simple, but add a space for complex expressions as shown below: ``` A(:,i,j) A([1:i-1; i+1:n] ``` - Always use a specific end-of-block statement (like endif, endswitch) rather than the generic end. - Enclose the if, while, until, and switch conditions in parentheses. ``` if (isvector (a)) s = sum (a); endif ``` Do not do this, however, with the iteration counter portion of a for statement! ``` for i = 1:n b(i) = sum (a(:,1)); end ``` **That's about it all I suppose!** Just keep more or less consistent with what's in the other function files of the statistics packages and you should be fine :smile: statistics-release-1.7.3/COPYING000066400000000000000000001045121475240274700164040ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .statistics-release-1.7.3/DESCRIPTION000066400000000000000000000005561475240274700170620ustar00rootroot00000000000000Name: statistics Version: 1.7.3 Date: 2025-2-10 Author: various authors Maintainer: Andreas Bertsatos Title: Statistics Description: The Statistics package for GNU Octave. Categories: Statistics Depends: octave (>= 7.2.0) License: GPLv3+ Url: https://github.com/gnu-octave/statistics Tracker: https://github.com/gnu-octave/statistics/issues statistics-release-1.7.3/INDEX000066400000000000000000000121641475240274700161440ustar00rootroot00000000000000statistics >> Statistics Clustering cluster clusterdata cmdscale confusionmat cophenet crossval editDistance evalclusters inconsistent kmeans knnsearch linkage mahal mhsample optimalleaforder pdist pdist2 procrustes rangesearch slicesample squareform Classification Classes ClassificationDiscriminant ClassificationGAM ClassificationKNN ClassificationNeuralNetwork ClassificationPartitionedModel ClassificationSVM CompactClassificationDiscriminant CompactClassificationGAM CompactClassificationNeuralNetwork CompactClassificationSVM ConfusionMatrixChart Clustering Classes CalinskiHarabaszEvaluation ClusterCriterion DaviesBouldinEvaluation GapEvaluation SilhouetteEvaluation CVpartition @cvpartition/cvpartition @cvpartition/display @cvpartition/get @cvpartition/repartition @cvpartition/set @cvpartition/test @cvpartition/training Regression Classes RegressionGAM Data Manipulation combnk crosstab datasample fillmissing grp2idx ismissing isoutlier normalise_distribution rmmissing standardizeMissing tabulate Descriptive Statistics cdfcalc cl_multinom geomean grpstats harmmean jackknife mad mean median nanmax nanmin nansum std trimmean var Distribution Classes BetaDistribution BinomialDistribution BirnbaumSaundersDistribution BurrDistribution ExponentialDistribution ExtremeValueDistribution GammaDistribution GeneralizedExtremeValueDistribution GeneralizedParetoDistribution HalfNormalDistribution InverseGaussianDistribution LogisticDistribution LoglogisticDistribution LognormalDistribution LoguniformDistribution MultinomialDistribution NakagamiDistribution NegativeBinomialDistribution NormalDistribution PiecewiseLinearDistribution PoissonDistribution RayleighDistribution RicianDistribution tLocationScaleDistribution TriangularDistribution UniformDistribution WeibullDistribution Distribution Fitting betafit betalike binofit binolike bisafit bisalike burrfit burrlike evfit evlike expfit explike gamfit gamlike geofit gevfit_lmom gevfit gevlike gpfit gplike gumbelfit gumbellike hnfit hnlike invgfit invglike logifit logilike loglfit logllike lognfit lognlike nakafit nakalike nbinfit nbinlike normfit normlike poissfit poisslike raylfit rayllike ricefit ricelike tlsfit tlslike unidfit unifit wblfit wbllike Distribution Functions betacdf betainv betapdf betarnd binocdf binoinv binopdf binornd bisacdf bisainv bisapdf bisarnd burrcdf burrinv burrpdf burrrnd bvncdf bvtcdf cauchycdf cauchyinv cauchypdf cauchyrnd chi2cdf chi2inv chi2pdf chi2rnd copulacdf copulapdf copularnd evcdf evinv evpdf evrnd expcdf expinv exppdf exprnd fcdf finv fpdf frnd gamcdf gaminv gampdf gamrnd geocdf geoinv geopdf geornd gevcdf gevinv gevpdf gevrnd gpcdf gpinv gppdf gprnd gumbelcdf gumbelinv gumbelpdf gumbelrnd hncdf hninv hnpdf hnrnd hygecdf hygeinv hygepdf hygernd invgcdf invginv invgpdf invgrnd iwishpdf iwishrnd jsucdf jsupdf laplacecdf laplaceinv laplacepdf laplacernd logicdf logiinv logipdf logirnd loglcdf loglinv loglpdf loglrnd logncdf logninv lognpdf lognrnd mnpdf mnrnd mvncdf mvnpdf mvnrnd mvtcdf mvtpdf mvtrnd mvtcdfqmc nakacdf nakainv nakapdf nakarnd nbincdf nbininv nbinpdf nbinrnd ncfcdf ncfinv ncfpdf ncfrnd nctcdf nctinv nctpdf nctrnd ncx2cdf ncx2inv ncx2pdf ncx2rnd normcdf norminv normpdf normrnd plcdf plinv plpdf plrnd poisscdf poissinv poisspdf poissrnd raylcdf raylinv raylpdf raylrnd ricecdf riceinv ricepdf ricernd tcdf tinv tpdf trnd tlscdf tlsinv tlspdf tlsrnd tricdf triinv tripdf trirnd unidcdf unidinv unidpdf unidrnd unifcdf unifinv unifpdf unifrnd vmcdf vminv vmpdf vmrnd wblcdf wblinv wblpdf wblrnd wienrnd wishpdf wishrnd Distribution Statistics betastat binostat bisastat burrstat chi2stat evstat expstat fstat gamstat geostat gevstat gpstat hnstat hygestat invgstat logistat loglstat lognstat nakastat nbinstat ncfstat nctstat ncx2stat normstat plstat poisstat raylstat ricestat tlsstat tristat tstat unidstat unifstat wblstat Distribution Wrappers cdf fitdist icdf makedist mle pdf random Experimental Design fullfact ff2n sigma_pts x2fx Machine Learning fcnnpredict fcnntrain hmmestimate hmmgenerate hmmviterbi svmpredict svmtrain Model Fitting fitcdiscr fitcgam fitcknn fitcnet fitcsvm fitgmdist fitlm fitrgam Hypothesis Testing adtest anova1 anova2 anovan bartlett_test barttest binotest chi2gof chi2test correlation_test fishertest friedman hotelling_t2test hotelling_t2test2 kruskalwallis kstest kstest2 levene_test manova1 mcnemar_test multcompare ranksum regression_ftest regression_ttest runstest sampsizepwr signrank signtest tiedrank ttest ttest2 vartest vartest2 vartestn ztest ztest2 I/O libsvmread libsvmwrite loadmodel Plotting bar3 bar3h boxplot cdfplot confusionchart dendrogram ecdf einstein gscatter histfit hist3 manovacluster normplot ppplot qqplot silhouette violin wblplot Regression canoncorr cholcov dcov glmfit glmval logistic_regression mnrfit monotone_smooth pca pcacov pcares plsregress princomp regress regress_gp ridge stepwisefit Transforms logit probit statistics-release-1.7.3/NEWS000066400000000000000000000073051475240274700160520ustar00rootroot00000000000000 Summary of important user-visible changes for statistics 1.7.0: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** fcnnpredict ** fcnntrain ** fitcdiscr ** fitcgam ** fitcnet ** fitcsvm ** loadmodel ** signrank New classdefs: ============== ** ClassificationDiscriminant ** ClassificationGAM ** ClassificationNeuralNetwork ** ClassificationPartitionedModel ** ClassificationSVM ** CompactClassificationDiscriminant ** CompactClassificationGAM ** CompactClassificationNeuralNetwork ** CompactClassificationSVM Improvements: ============= ** BinomialDistribution: fixed computations on truncated distribution methods (github issue #128) ** ClassificationKNN: new methods and various bug fixes ** finv: fix erratic output for very large DF2 (savannah bug #66034) ** fitcknn: support for cross-validation ** NegativeBinomialDistribution: fixed computations on truncated distribution methods (github issue #128) ** plot method for probability distribution objects updated to support 'cdf' and 'probability' PlotType options (github issue #129) ** PoissonDistribution: fixed computations on truncated distribution methods (github issue #128) Summary of important user-visible changes for statistics 1.7.1: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package Improvements: ============= ** ClassificationPartitionedModel: add input validation for cvpartition object (github issue #160) ** ClassificationNeuralNetwork: use named variable for timing training (github issue #169) ** kstest: add support for probability distribution objects, fix previous regression related to failing test (github issues #164, #165, #167), add more BISTs and a DEMO ** RegressionGAM: add iteration limit to the private fitGAM function to avoid infinite loop with Netlib BLAS/LAPACK (github issue #160) Summary of important user-visible changes for statistics 1.7.2: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package Improvements: ============= ** fcnnpredict, fcnntrain: various bug fixes, allow MacOS users to enable support for OpenMP (github issues #168, #171, #172) ** editDistance: use correct index types to avoid compiler warnings (github issue #171) Summary of important user-visible changes for statistics 1.7.3: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** glmval Improvements: ============= ** glmfit: fixed output and updated functionality and compatibility statistics-release-1.7.3/ONEWS-1.1.x000066400000000000000000000051151475240274700167310ustar00rootroot00000000000000Summary of important user-visible changes for statistics 1.1.3: ------------------------------------------------------------------- ** The following functions are new in 1.1.3: copularnd mvtrnd ** The functions mnpdf and mnrnd are now also usable for greater numbers of categories for which the rows do not exactly sum to 1. Summary of important user-visible changes for statistics 1.1.2: ------------------------------------------------------------------- ** The following functions are new in 1.1.2: mnpdf mnrnd ** The package is now dependent on the io package (version 1.0.18 or later) since the functions that it depended of from miscellaneous package have been moved to io. ** The function `kmeans' now accepts the 'emptyaction' property with the 'singleton' value. This allows for the kmeans algorithm to handle empty cluster better. It also throws an error if the user does not request an empty cluster handling, and there is an empty cluster. Plus, the returned items are now a closer match to Matlab. Summary of important user-visible changes for statistics 1.1.1: ------------------------------------------------------------------- ** The following functions are new in 1.1.1: monotone_smooth kmeans jackknife ** Bug fixes on the functions: normalise_distribution combnk repanova ** The following functions were removed since equivalents are now part of GNU octave core: zscore ** boxplot.m now returns a structure with handles to the plot elemenets. Summary of important user-visible changes for statistics 1.1.0: ------------------------------------------------------------------- ** IMPORTANT note about `fstat' shadowing core library function: GNU octave's 3.2 release added a new function `fstat' to return information of a file. Statistics' `fstat' computes F mean and variance. Since MatLab's `fstat' is the equivalent to statistics' `fstat' (not to core's `fstat'), and to avoid problems with the statistics package, `fstat' has been deprecated in octave 3.4 and will be removed in Octave 3.8. In the mean time, please ignore this warning when installing the package. ** The following functions are new in 1.1.0: normalise_distribution repanova combnk ** The following functions were removed since equivalents are now part of GNU octave core: prctile ** The __tbl_delim__ function is now private. ** The function `boxplot' now accepts named arguments. ** Bug fixes on the functions: harmmean nanmax nanmin regress ** Small improvements on help text. statistics-release-1.7.3/ONEWS-1.2.x000066400000000000000000000047501475240274700167360ustar00rootroot00000000000000Summary of important user-visible changes for statistics 1.2.4: ------------------------------------------------------------------- ** Made princomp work with nargout < 2. ** Renamed dendogram to dendrogram. ** Added isempty check to kmeans. ** Transposed output of hist3. ** Converted calculation in hmmviterbi to log space. ** Bug fixes for stepwisefit wishrnd. ** Rewrite of cmdscale for improved compatibility. ** Fix in squareform for improved compatibility. ** New cvpartition class, with methods: display repartition test training ** New sample data file fisheriris.txt for tests ** The following functions are new: cdf crossval dcov pdist2 qrandn randsample signtest ttest ttest2 vartest vartest2 ztest Summary of important user-visible changes for statistics 1.2.3: ------------------------------------------------------------------- ** Made sure that output of nanstd is real. ** Fixed second output of nanmax and nanmin. ** Corrected handle for outliers in boxplot. ** Bug fix and enhanced functionality for mvnrnd. ** The following functions are new: wishrnd iwishrnd wishpdf iwishpdf cmdscale Summary of important user-visible changes for statistics 1.2.2: ------------------------------------------------------------------- ** Fixed documentation of dendogram and hist3 to work with TexInfo 5. Summary of important user-visible changes for statistics 1.2.1: ------------------------------------------------------------------- ** The following functions are new: pcares pcacov runstest stepwisefit hist3 ** dendogram now returns the leaf node numbers and order that the nodes were displayed in. ** New faster implementation of princomp. Summary of important user-visible changes for statistics 1.2.0: ------------------------------------------------------------------- ** The following functions are new: regress_gp dendogram plsregress ** New functions for the generalized extreme value (GEV) distribution: gevcdf gevfit gevfit_lmom gevinv gevlike gevpdf gevrnd gevstat ** The interface of the following functions has been modified: mvnrnd ** `kmeans' has been fixed to deal with clusters that contain only one element. ** `normplot' has been fixed to avoid use of functions that have been removed from Octave core. Also, the plot produced should now display some aesthetic elements and appropriate legends. ** The help text of `mvtrnd' has been improved. ** Package is no longer autoloaded. statistics-release-1.7.3/ONEWS-1.3.0000066400000000000000000000012641475240274700166240ustar00rootroot00000000000000Summary of important user-visible changes for statistics 1.3.0: ------------------------------------------------------------------- ** The following functions are new: bbscdf bbsinv bbspdf bbsrnd binotest burrcdf burrinv burrpdf burrrnd gpcdf gpinv gppdf gprnd grp2idx mahal mvtpdf nakacdf nakainv nakapdf nakarnd pdf tricdf triinv tripdf trirnd violin ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: betastat binostat cdf combnk gevfit hist3 kmeans linkage randsample squareform ttest statistics-release-1.7.3/ONEWS-1.4.x000066400000000000000000000141701475240274700167350ustar00rootroot00000000000000Summary of important user-visible changes for statistics 1.4.3: ------------------------------------------------------------------- New functions: ============== ** anova1 (patch #10127) kruskalwallis ** cluster (patch #10009) ** clusterdata (patch #10012) ** confusionchart (patch #9985) ** confusionmat (patch #9971) ** cophenet (patch #10040) ** datasample (patch #10050) ** evalclusters (patch #10052) ** expfit (patch #10092) explike ** gscatter (patch #10043) ** ismissing (patch #10102) ** inconsistent (patch #10008) ** mhsample.m (patch #10016) ** ncx2pdf (patch #9711) ** optimalleaforder.m (patch #10034) ** pca (patch #10104) ** rmmissing (patch #10102) ** silhouette (patch #9743) ** slicesample (patch #10019) ** wblplot (patch #8579) Improvements: ============= ** anovan.m: use double instead of toascii (bug #60514) ** binocdf: new option "upper" (bug #43721) ** boxplot: better Matlab compatibility; several Matlab-compatible plot options added (OutlierTags, Sample_IDs, BoxWidth, Widths, BoxStyle, Positions, Labels, Colors) and an Octave-specific one (CapWidhts); demos added; texinfo improved (patch #9930) ** auto MPG (carbig) sample dataset added from https://archive.ics.uci.edu/ml/datasets/Auto+MPG (patch #10045) ** crosstab.m: make n-dimensional (patch #10014) ** dendrogram.m: many improvements (patch #10036) ** fitgmdist.m: fix typo in ComponentProportion (bug #59386) ** gevfit: change orientation of results for Matlab compatibility (bug #47369) ** hygepdf: avoid overflow for certain inputs (bug #35827) ** kmeans: efficiency and compatibility tweaks (patch #10042) ** pdist: option for squared Euclidean distance (patch #10051) ** stepwisefit.m: give another option to select predictors (patch #8584) ** tricdf, triinv: fixes (bug #60113) Summary of important user-visible changes for statistics 1.4.2: ------------------------------------------------------------------- ** canoncorr: allow more variables than observations ** fitgmdist: return fitgmdist parameters (Bug #57917) ** gamfit: invert parameter per docs (Bug #57849) ** geoXXX: update docs 'number of failures (X-1)' => 'number of failures (X)' (Bug #57606) ** kolmogorov_smirnov_test.m: update function handle usage from octave6+ (Bug #57351) ** linkage.m: fix octave6+ parse error (Bug #57348) ** unifrnd: changed unifrnd(a,a) to return a 0 rather than NaN (Bug #56342) ** updates for usage of depreciated octave functions Summary of important user-visible changes for statistics 1.4.1: ------------------------------------------------------------------- ** update install scripts for octave 5.0 depreciated functions ** bug fixes to the following functions: pdist2.m: use max in distEucSq (Bug #50377) normpdf: use eps tolerance in tests (Bug #51963) fitgmdist: fix an output bug in fitgmdist t_test: Set tolerance on t_test BISTS (Bug #54557) gpXXXXX: change order of inputs to match matlab (Bug #54009) bartlett_test: df = k-1 (Bug #45894) gppdf: apply scale factor (Bug #54009) gmdistribution: updates for bug #54278, ##54279 wishrnd: Bug #55860 Summary of important user-visible changes for statistics 1.4.0: ------------------------------------------------------------------- ** The following functions are new: canoncorr fitgmdist gmdistribution sigma_pts ** The following functions have been moved from the statistics package but are conditionally installed: mad ** The following functions have been moved from octave to be conditionally installed: BASE cloglog logit prctile probit qqplot table (renamed to crosstab) DISTRIBUTIONS betacdf betainv betapdf betarnd binocdf binoinv binopdf binornd cauchy_cdf cauchy_inv cauchy_pdf cauchy_rnd chi2cdf chi2inv chi2pdf chi2rnd expcdf expinv exppdf exprnd fcdf finv fpdf frnd gamcdf gaminv gampdf gamrnd geocdf geoinv geopdf geornd hygecdf hygeinv hygepdf hygernd kolmogorov_smirnov_cdf laplace_cdf laplace_inv laplace_pdf laplace_rnd logistic_cdf logistic_inv logistic_pdf logistic_rnd logncdf logninv lognpdf lognrnd nbincdf nbininv nbinpdf nbinrnd normcdf norminv normpdf normrnd poisscdf poissinv poisspdf poissrnd stdnormal_cdf stdnormal_inv stdnormal_pdf stdnormal_rnd tcdf tinv tpdf trnd unidcdf unidinv unidpdf unidrnd unifcdf unifinv unifpdf unifrnd wblcdf wblinv wblpdf wblrnd wienrnd MODELS logistic_regression TESTS anova bartlett_test chisquare_test_homogeneity chisquare_test_independence cor_test f_test_regression hotelling_test hotelling_test_2 kolmogorov_smirnov_test kolmogorov_smirnov_test_2 kruskal_wallis_test manova mcnemar_test prop_test_2 run_test sign_test t_test t_test_2 t_test_regression u_test var_test welch_test wilcoxon_test z_test z_test_2 ** Functions marked with known test failures: grp2idx: bug #51928 gevfir_lmom: bug #31070 ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: dcov: returned dcov instead of dcor. added demo. violin: can be used with subplots. violin quality improved. princomp: Fix expected values of tsquare in unit tests fitgmdist: test number inputs to function hist3: fix removal of rows with NaN values ** added the packages test data to install statistics-release-1.7.3/ONEWS-1.5.x000066400000000000000000000300501475240274700167310ustar00rootroot00000000000000Summary of important user-visible changes for statistics 1.5.0: ------------------------------------------------------------------- Important Notice: 1) dependency change to Octave>=6.1.0 2) `mean` shadows core Octave's respective function 3) removed dependency on `io` package 4) incompatibility with the `nan` package New functions: ============== ** anova2 (fully Matlab compatible) ** bvncdf ** cdfcalc, cdfplot ** chi2gof (fully Matlab compatible. bug #46764) ** chi2test (bug #58838) ** cholcov (fully Matlab compatible) ** ecdf (fully Matlab compatible) ** evfit (fully Matlab compatible) ** evlike (fully Matlab compatible) ** fitlm (mostly Matlab compatible) ** fillmissing (patch #10102) ** friedman (fully Matlab compatible) ** grpstats (complementary to manova1) ** kruskalwallis (fully Matlab compatible) ** kstest (fully Matlab compatible) ** kstest2 (fully Matlab compatible. bug #56572) ** libsvmread, libsvmwrite (I/O functions for LIBSVM data files) ** manova1 (fully Matlab compatible) ** manovacluster (fully Matlab compatible) ** mean (fully Matlab compatible, it shadows mean from core Octave) ** multcompare (fully Matlab compatible) ** mvtcdfqmc ** ranksum (fully Matlab compatible. bug #42079) ** standardizeMissing (patch #10102) ** svmpredict, svmtrain (wrappers for LIBSVM 3.25) ** tiedrank (complementary to ranksum) ** x2fx (missing function: bug #48146) Improvements: ============= ** anova1: added extra feature for performing Welch's ANOVA (PR #15) ** anovan: mostly Matlab compatible, extra features. (patch #10123, PR #1-42) ** binopdf: implement high accuracy Loader algorithm for m>=10 (bug #34362) ** cdf: extended to include all available distributions ** crosstab: can handle char arrays, fixed ordering of groups ** gaminv: fixed accuracy for small 1st argument (bug #56453) ** geomean: fully Matlab compatible. (patch #59410) ** gevlike: fully Matlab compatible. ACOV output is the inverse Fisher Inf Mat ** grp2idx (fully Matlab compatible, indexes in order of appeearance) ** harmmean: fully Matlab compatible. ** hygepdf: added optional parameter "vectorexpand" to facilitate vectorization of other hyge functions. Allows different inputs lengths for x and t,m,n parameters, with broadcast expanded output (bug #34363) ** hygecdf: improved vectorization for non-scalar inputs. hygeinv hygernd ** ismissing: corrects handling of n-D arrays, NaN indicators, and improves matlab compatibility for different data types. (patch #10102) ** kmeans: improved help file, evaluate efficiency (bug #8959) ** laplace_cdf: allow for parameters mu and scale (bug #58688) laplace_inv laplace_pdf logistic_cdf logistic_inv logistic_pdf ** logistic_regression: fixed incorrect results (bug #60348) ** mvncdf: improved performance and accuracy (bug #44130) ** normplot: fixed ploting error (bug #62394), updated features ** pdf: extended to include all available distributions ** pdist: updated the 'cosine' metric to be more efficient (bug #62495) ** rmmissing: corrects cellstr array handling and improves matlab compatibility for different data types. (patch #10102) ** signtest: fix erroneous results, fully Matlab compatible (bug #49961) ** ttest2: can handle NaN values as missing data (bug #58697) ** violin: fix parsing color vector affecting Octave>=6.1.0 (bug #62805) ** wblplot: fixed coding style and help texinfo. (patch #8579) Removed Functions: ================== ** anova (replaced by anova1) ** caseread, casewrite (do not belong here) ** chisquare_test_homogeneity (replaced by chi2test) ** chisquare_test_independence (replaced by chi2test) ** kolmogorov_smirnov_test (replaced by kstest) ** kolmogorov_smirnov_test_2 (replaced by kstest2) ** kruskal_wallis_test (replaced by kruskalwallis) ** manova (replaced by manova1) ** repanova (replaced by anova2) ** sign_test (replaced by updated signtest) ** tblread, tblwrite (belong to `io` package when tables are implemented) ** t_test, t_test_2 (deprecated: use ttest & ttest2) ** wilcoxon_test (replaced by ranksum) Available Data Sets: ==================== ** acetylene Chemical reaction data with correlated predictors ** arrhythmia Cardiac arrhythmia data from the UCI machine learning repository ** carbig Measurements of cars, 1970–1982 ** carsmall Subset of carbig. Measurements of cars, 1970, 1976, 1982 ** cereal Breakfast cereal ingredients ** examgrades Exam grades on a scale of 0–100 ** fisheriris Fisher's 1936 iris data ** hald Heat of cement vs. mix of ingredients ** heart_scale.dat Used for SVM testing ** kmeansdata Four-dimensional clustered data ** mileage Mileage data for three car models from two factories ** morse Recognition of Morse code distinctions by non-coders ** popcorn Popcorn yield by popper type and brand ** stockreturns Simulated stock returns ** weather Daily high temperatures in the same month in two consecutive years Summary of important user-visible changes for statistics 1.5.1: ------------------------------------------------------------------- Important Notice: 1) `mean` shadows core Octave's respective function 2) incompatibility with the `nan` package New functions: ============== ** barttest (fully Matlab compatible) ** evcdf (fully Matlab compatible) ** evinv (fully Matlab compatible) ** evpdf (fully Matlab compatible) ** evrnd (fully Matlab compatible) ** evstat (fully Matlab compatible) ** gpfit (fully Matlab compatible) ** gplike (fully Matlab compatible) ** gpstat (fully Matlab compatible) ** levene_test (options for testtypes, handling NaNs and GROUPS like anova1) ** ncfcdf (fully Matlab compatible) ** ncfinv (fully Matlab compatible) ** ncfpdf (fully Matlab compatible) ** ncfrnd (fully Matlab compatible) ** ncfstat (fully Matlab compatible) ** nctcdf (fully Matlab compatible) ** nctinv (fully Matlab compatible) ** nctpdf (fully Matlab compatible) ** nctrnd (fully Matlab compatible) ** nctstat (fully Matlab compatible) ** ncx2cdf (fully Matlab compatible) ** ncx2inv (fully Matlab compatible) ** ncx2rnd (fully Matlab compatible) ** ncx2stat (fully Matlab compatible) ** normlike (fully Matlab compatible) ** sampsizepwr (fully Matlab compatible with extra functionality) ** vartestn (fully Matlab compatible) Improvements: ============= ** bartlett_test: improved functionality, hanlding NaNs and GROUPS like anova1 ** chi2cdf: added "upper" option and confidence bounds ** chi2test: improved functionality, handles multi-way tables ** crosstab: returns chi-square and p-value for multiway tables ** evfit: fixed bug that caused an error when x is a row vector ** fcdf: added "upper" option and confidence bounds ** gamcdf: added "upper" option and confidence bounds ** gpcdf: added "upper" option and confidence bounds ** mvnpdf: fixed MATLAB compatibility ** mvnrnd: fixed MATLAB compatibility ** ncx2pdf: reimplemented to be fully MATLAB compatible ** normcdf: added "upper" option and confidence bounds ** tcdf: added "upper" option ** vartest: fixed MATLAB compatibility ** vartest2: fixed MATLAB compatibility ** ztest: fixed MATLAB compatibility Removed Functions: ================== ** cloglog ** nanmean (replaced by mean) ** kolmogorov_smirnov_cdf (unused by new kstest, kstest2 functions) ** u_test (replaced by ranksum) ** var_test (replaced by vartest) ** z_test (replaced by ztest) Summary of important user-visible changes for statistics 1.5.2: ------------------------------------------------------------------- Important Notice: 1) `mean`, `median`, `std`, and `var` functions shadow core Octave's respective functions 2) incompatibility with the `nan` package New functions: ============== ** median (fully Matlab compatible) ** std (fully Matlab compatible) ** var (fully Matlab compatible) Improvements: ============= ** mean: fixed MATLAB compatibility ** multcompare: fixed erroneous results for Welch ANOVA, updated features ** tcdf: fixed erroneous results ** ttest: added support for NaN values and matrix inputs ** ttest2: added support for matrices and multiple t-tests Removed Functions: ================== ** nanmedian (replaced by median) ** nanstd (replaced by std) ** nanvar (replaced by var) Summary of important user-visible changes for statistics 1.5.3: ------------------------------------------------------------------- Important Notice: 1) `mean`, `median`, `std`, and `var` functions shadow core Octave's respective functions 2) incompatibility with the `nan` package New functions: ============== ** adtest (fully Matlab compatible) ** hotelling_t2test (new functionality, replacing old hotelling_test) ** hotelling_t2test2 (new functionality, replacing old hotelling_test_2) ** regression_ftest (new functionality, replacing old f_test_regression) ** regression_ttest (replacing old t_test_regression) ** vmcdf (von Mises cummulative distribution function) Improvements: ============= ** betacdf: added "upper" option ** binocdf: added "upper" option ** expcdf: added "upper" option and confidence bounds ** geocdf: added "upper" option ** gevcdf: added "upper" option ** hygecdf: added "upper" option ** laplace_cdf: updated functionality ** laplace_inv: updated functionality ** laplace_pdf: updated functionality ** laplace_rnd: updated functionality ** logistic_cdf: updated functionality ** logistic_inv: updated functionality ** logistic_pdf: updated functionality ** logistic_rnd: updated functionality ** logncdf: added "upper" option and confidence bounds ** mean: fixed MATLAB compatibility ** median: fixed MATLAB compatibility ** multcompare: print PostHoc Test table ** nbincdf: added "upper" option ** poisscdf: added "upper" option ** raylcdf: added "upper" option ** std: fixed MATLAB compatibility ** unidcdf: added "upper" option ** unifcdf: added "upper" option ** var: fixed MATLAB compatibility ** vmpdf: updated functionality ** vmrnd: updated functionality ** wblcdf: added "upper" option and confidence bounds Removed Functions: ================== ** anderson_darling_cdf (replaced by adtest) ** anderson_darling_test (replaced by adtest) ** hotelling_test (replaced by hotelling_t2test) ** hotelling_test_2 (replaced by hotelling_t2test2) ** f_test_regression (replaced by regression_ftest) ** t_test_regression (replaced by regression_ttest) Summary of important user-visible changes for statistics 1.5.4: ------------------------------------------------------------------- Important Notice: 1) `mean`, `median`, `std`, and `var` functions shadow core Octave's respective functions 2) incompatibility with the `nan` package New functions: ============== ** bvtcdf ** correlation_test (new functionality, replacing old cor_test) ** icdf (wrapper for all available *inv distribution functions) ** fishertest (fully Matlab compatible) ** procrustes (fully Matlab compatible) ** ztest2 (new functionality, replacing old prop_test_2) Improvements: ============= ** cdf: updated wrapper for all available *cdf distribution functions ** dcov: handles missing values and multivariate samples ** geomean: fixed MATLAB compatibility ** harmmean: fixed MATLAB compatibility ** mean: fixed MATLAB compatibility ** median: fixed MATLAB compatibility ** mvtcdf: improved speed, fixed Matlab compatibility ** pdf: updated wrapper for all available *pdf distribution functions ** random: updated wrapper for all available *rnd distribution functions ** regression_ttest: new functionality ** std: fixed MATLAB compatibility ** var: fixed MATLAB compatibility Removed Functions: ================== ** cor_test (replaced by correlation_test) ** prop_test_2 (replaced by ztest2) statistics-release-1.7.3/ONEWS-1.6.x000066400000000000000000000302701475240274700167360ustar00rootroot00000000000000 Summary of important user-visible changes for statistics 1.6.0: ------------------------------------------------------------------- Important Notice: 1) dependency changed to Octave>=7.2.0 2) various distribution functions have been renamed, deprecated, or modified extensively so that backwards compatibility is broken 3) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions 4) incompatibility with the `nan` package New functions: ============== ** betafit (fully Matlab compatible) ** betalike (fully Matlab compatible) ** binofit (fully Matlab compatible) ** binolike (similar functionality to MATLAB's negloglik) ** bisafit (similar functionality to MATLAB's mle) ** bisalike (similar functionality to MATLAB's negloglik and mlecov) ** burrfit (similar functionality to MATLAB's mle) ** burrlike (similar functionality to MATLAB's negloglik and mlecov) ** einstein (plotting function for the einstein tile) ** geofit (similar functionality to MATLAB's mle) ** gumbelcdf ** gumbelfit (similar functionality to MATLAB's mle) ** gumbelinv ** gumbellike (similar functionality to MATLAB's negloglik and mlecov) ** gumbelpdf ** gumbelrnd ** hncdf ** hnfit (similar functionality to MATLAB's mle) ** hninv ** hnlike (similar functionality to MATLAB's negloglik and mlecov) ** hnpdf ** hnrnd ** invgcdf ** invgfit (similar functionality to MATLAB's mle) ** invginv ** invglike (similar functionality to MATLAB's negloglik and mlecov) ** invgpdf ** invgrnd ** isoutlier (fully Matlab compatible) ** logifit (similar functionality to MATLAB's mle) ** logilike (similar functionality to MATLAB's negloglik and mlecov) ** loglcdf ** loglfit (similar functionality to MATLAB's mle) ** loglinv ** logllike (similar functionality to MATLAB's negloglik and mlecov) ** loglpdf ** loglrnd ** lognfit (fully Matlab compatible) ** lognlike (fully Matlab compatible) ** nakafit (similar functionality to MATLAB's mle) ** nakalike (similar functionality to MATLAB's negloglik and mlecov) ** nbinfit (similar functionality to MATLAB's mle) ** nbinlike (similar functionality to MATLAB's negloglik and mlecov) ** mad (fully Matlab compatible) ** normfit (fully Matlab compatible) ** poissfit (fully Matlab compatible, extra functionality) ** poisslike (similar functionality to MATLAB's negloglik) ** raylfit (fully Matlab compatible, extra functionality) ** rayllike (similar functionality to MATLAB's negloglik) ** ridge (fully Matlab compatible) ** unidfit (similar functionality to MATLAB's mle) ** unifit (similar functionality to MATLAB's mle) ** vminv (quantile function for the von Mises distribution) ** wblfit (fully Matlab compatible) ** wbllike (fully Matlab compatible) Improvements: ============= ** bisacdf: supports "upper" option ** burrcdf: supports "upper" option ** cauchycdf: supports "upper" option ** cdf: update support for all univariate cumulative distribution functions ** gamfit: fixed MATLAB compatibility ** gamlike: fixed MATLAB compatibility ** icdf: update support for all univariate quantile functions ** laplacecdf: supports "upper" option ** logicdf: supports "upper" option ** mcnemar_test: updated functionality ** nakacdf: supports "upper" option ** pcacov: fixed MATLAB compatibility ** pdf: update support for all univariate probability density functions ** plsregress: fixed MATLAB compatibility ** random: update support for all univariate functions ** runstest: fixed MATLAB compatibility ** tabulate: fixed MATLAB compatibility ** tricdf: supports "upper" option ** trimmean: fixed MATLAB compatibility Removed Functions: ================== ** run_test (replaced by runstest) ** stdnormal_cdf (replaced by normcdf) ** stdnormal_inv (replaced by norminv) ** stdnormal_pdf (replaced by normpdf) ** stdnormal_rnd (replaced by normrnd) Renamed Functions: ================== ** bisacdf (replacing bbscdf) ** bisainv (replacing bbsinv) ** bisapdf (replacing bbspdf) ** bisarnd (replacing bbsrnd) ** cauchycdf (replacing cauchy_cdf) ** cauchyinv (replacing cauchy_inv) ** cauchypdf (replacing cauchy_pdf) ** cauchyrnd (replacing cauchy_rnd) ** laplacecdf (replacing laplace_cdf) ** laplaceinv (replacing laplace_inv) ** laplacepdf (replacing laplace_pdf) ** laplacernd (replacing laplace_rnd) ** logicdf (replacing logistic_cdf) ** logiinv (replacing logistic_inv) ** logipdf (replacing logistic_pdf) ** logirnd (replacing logistic_rnd) Summary of important user-visible changes for statistics 1.6.1: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** ClassificationKNN (new classdef) ** predict (for ClassificationKNN classdef) ** fitcknn ** fitrgam ** knnsearch (fully Matlab compatible) ** mnrfit ** rangesearch (fully Matlab compatible) ** RegressionGAM (new classdef) ** predict (for RegressionGAM classdef) Improvements: ============= ** anovan: new features ** friedman: bug fixes ** pdist: updated functionality, fully MATLAB compatible ** pdist2: updated functionality, fully MATLAB compatible ** regress_gp: updated functionality with RBF kernel ** ridge: bug fixes Summary of important user-visible changes for statistics 1.6.2: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** ricecdf ** riceinv ** ricepdf ** ricernd Improvements: ============= ** changed ClassificationKNN legacy class to classdef ** changed RegressionGAM legacy class to classdef ** update figure handling in BISTs, bug fix when calling 'pkg test statistics' Summary of important user-visible changes for statistics 1.6.3: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** ricefit ** ricelike ** ricestat ** tlscdf ** tlsinv ** tlspdf ** tlsrnd ** tlsstat Improvements: ============= ** cdf: add support for Rician and location-scale T distributions ** gevlike: deprecate gradient as a second output argument (undocumented) ** icdf: add support for Rician and location-scale T distributions ** pdf: add support for Rician and location-scale T distributions ** random: add support for Rician and location-scale T distributions ** ricernd: fixed bug that produced erroneous results ** updated input validation and documentation to distribution *stat functions Summary of important user-visible changes for statistics 1.6.4: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** bisastat ** burrstat ** fitdist ** glmfit (needs a lot of work yet) ** hnstat ** invgstat ** logistat ** loglstat ** makedist ** mle ** nakastat ** plcdf ** plinv ** plpdf ** plrnd ** plstat ** tlsfit ** tlslike ** tristat New classdefs: ============== ** BetaDistribution ** BinomialDistribution (methods on truncated distributions do not work yet) ** BirnbaumSaundersDistribution ** BurrDistribution ** ExponentialDistribution ** ExtremeValueDistribution ** GammaDistribution ** GeneralizedExtremeValueDistribution ** GeneralizedParetoDistribution ** HalfNormalDistribution ** InverseGaussianDistribution ** LogisticDistribution ** LoglogisticDistribution ** LognormalDistribution ** LoguniformDistribution ** MultinomialDistribution ** NakagamiDistribution ** NegativeBinomialDistribution (methods on truncated distributions do not work yet) ** NormalDistribution ** PiecewiseLinearDistribution ('mean', 'std', and 'var' methods fail for truncated distributions) ** PoissonDistribution (methods on truncated distributions do not work yet) ** RayleighDistribution ** RicianDistribution ** tLocationScaleDistribution ** TriangularDistribution ** UniformDistribution ** WeibullDistribution Improvements: ============= ** binolike: accepts frequency vector as third input argument ** gevfit: accepts frequency vector as third input argument ** gevlike: accepts frequency vector as third input argument ** gpfit: accepts frequency vector and requires fixed parameter 'theta' ** gplike: accepts frequency vector as third input argument ** logl* distribution functions: changed parameters for MATLAB compatibility ** hnfit: accepts frequency vector as third input argument ** hnlike: accepts frequency vector as third input argument ** nbinfit: accepts frequency vector as third input argument ** nbinlike: accepts frequency vector as third input argument ** tri* distribution functions: swapped the order of b and c input arguments ** unidfit: accepts frequency vector as third input argument ** unifit: accepts frequency vector as third input argument Summary of important user-visible changes for statistics 1.6.5: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package Improvements: ============= ** mad/median: correct mad handling of vectors with multiple Infs ** PiecewiseLinearDistribution: fix truncated `mean`, `std`, and `var` methods ** various fixes to avoid errors when testing the package functions Summary of important user-visible changes for statistics 1.6.6: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** editDistance Improvements: ============= ** fillmissing: combining `movmedian` method and `missinglocation` option no longer ignores non-NaN values. ** gumbelinv: fix incorrect return value due to misplaced minus sign ** glmfit: fix input validation, parameter parsing, and update documentation ** histfit: updated support for other distributions, fixed MATLAB compatibility ** plot method for probability distribution objects partially implemented to allow plotting PDF and superimposing it over histograms of fitted data. Summary of important user-visible changes for statistics 1.6.7: ------------------------------------------------------------------- Important Notice: 1) `mad`, `mean`, `median`, `std`, `var` functions shadow core Octave's respective functions prior to Octave v9.1 2) incompatibility with the `nan` package New functions: ============== ** bar3 ** bar3h Improvements: ============= ** ClassificationKNN: bug fixes in computing Prior and when parsing explicit ClassNanes (github issue #148) ** nanmax: update functionality to support VECDIM and 'all' options, fix error when requesting a mutual comparison (savannah bug #65802) ** nanmin: update functionality to support VECDIM and 'all' options, fix error when requesting a mutual comparison (savannah bug #65802) statistics-release-1.7.3/README.md000066400000000000000000000145361475240274700166360ustar00rootroot00000000000000# statistics This is the official repository for the Statistics package for GNU Octave. **Content:** 1. [About](#1-about) 2. [Documentation](#2-documentation) 3. [Install statistics](#3-install-statistics) 4. [Provide feedback](#4-provide-feedback) 5. [Contribute](#5-contribute) ## 1. About The **statistics** package is a collection of functions for statistical analysis. As with GNU Octave, the **statistics** package aims to be mostly compatible with MATLAB's equivalent Statistics and Machine Learning Toolbox. However, this is not always applicable of even possible. Hence, identical (in name) functions do not necessarily share the same functionality or behavior. Nevertheless, they produce consistent and correct results, unless there is a bug: see [Murphy's Law](https://en.wikipedia.org/wiki/Murphy's_law) :smile:. As of 10.6.2022, the developemnt of the **statistics** package was moved from [SourceForge](https://octave.sourceforge.io/statistics/) and [Mercurial](https://en.wikipedia.org/wiki/Mercurial) to [GitHub](https://github.com/gnu-octave/statistics) and [Git](https://en.wikipedia.org/wiki/Git). Given the opportunity of this transition, the package has been redesigned, as compared to the its previous point [release 1.4.3](https://octave.sourceforge.io/download.php?package=statistics-1.4.3.tar.gz) at SourceForge, with the aim to keep its structure simplified and easier to maintain. To this end, two major decisions have been made: - Keep a single dependency to the last two major point releases of GNU Octave. - Deprecate old functions once their fully Matlab compatible equivalents are implemented. ## 2. Documentation All functions, class definitions, and their respective methods are documented with [texinfo](https://www.gnu.org/software/texinfo/) format, which can be accessed from the Octave command with the `help` function. Use dot notation to access the help of a particular method and new classdefs or the relative path for old style classes. For example: ``` help ClassificationKNN.predict help @cvpartition/test ``` You can also find the entire documentation of the **statistics** package along with its function index at [https://gnu-octave.github.io/statistics/](https://gnu-octave.github.io/statistics/). Alternatively, you can build the online documentation locally using the [`pkg-octave-doc`](https://github.com/gnu-octave/pkg-octave-doc) package. Assuming both packages are installed and loaded, browse to any directory of your choice with *write* permission and run: ``` package_texi2html ("statistics") ``` ## 3. Install statistics To install the latest release, you need Octave (>=7.2.0) installed on your system. Install it by typing: `pkg install -forge statistics` You can automatically download and install the latest development version of the **statistics** package found [here](https://github.com/gnu-octave/statistics/archive/refs/heads/main.zip) by typing: `pkg install "https://github.com/gnu-octave/statistics/archive/refs/heads/main.zip"` If you need to install a specific older release, for example `1.4.2`, type: `pkg install "https://github.com/gnu-octave/statistics/archive/refs/tags/release-1.4.2.tar.gz"` MacOS users, who wish to enable parallel processing for the [`fitncet`](https://gnu-octave.github.io/statistics/fitcnet.html) function and the [`ClassifcationNeuralNetwork`](https://gnu-octave.github.io/statistics/ClassificationNeuralNetwork.html) class, should first link to the OpenMP library before installing the **statistics** package. This will enable the compiler to use multithreading in the [`fcnntrain`](https://gnu-octave.github.io/statistics/fcnntrain.html) and [`fcnnpredict`](https://gnu-octave.github.io/statistics/fcnnpredict.html) dynamically linked functions. To do this, type: `setenv ("CPPFLAGS", "-I/opt/homebrew/opt/libomp/include -Xclang -fopenmp")` After installation, type: - `pkg load statistics` to load the **statistics** package. - `news statistics` to review all the user visible changes since last version. - `pkg test statistics` to run a test suite for all 452 [^1] functions and class definitions currently available and ensure that they work properly on your system. [^1]: Several functions are still missing from the statistics package, but you are welcome to [contribute](https://github.com/gnu-octave/statistics/blob/main/CONTRIBUTING.md)! ## 4. Provide feedback You are encouraged to provide feedback regarding possible bugs, missing features[^2], discrepancies or incompatibilities with Matlab functions. You may open an [issue](https://github.com/gnu-octave/statistics/issues) to open a discussion to your particular case. **Please, do NOT use the issue tracker for requesting help.** Use the [discourse group](https://octave.discourse.group/c/help/6) for requesting help with using functions and programming in Octave. Please, make sure that when reporting a bug you provide as much information as possible for other users to be able to replicate it. Use [markdown tips](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) to make your post clear and easy to read and understand your issue. [^2]: Don't open an issue just for requesting a missing function! Implement it yourself and make an invaluable contribution :innocent: ## 5. Contribute The **statistics** package is **open source**! Everyone is welcome to contribute. There are currently a few open [issues](https://github.com/gnu-octave/statistics/issues) that you can help fixing. If you find a bug and fix it, just [clone](https://github.com/gnu-octave/statistics.git) this repo with `git clone https://github.com/gnu-octave/statistics.git`, make your changes and add a [pull](https://github.com/gnu-octave/statistics/pulls) request. Alternatively, you may open an issue and add a git-patch file, which will be patched by the maintainer. Make sure you follow the coding style already used in the **statistics** package (similar to GNU Octave). For a summary of the coding style rules used in the package see [Contribute](https://github.com/gnu-octave/statistics/blob/main/CONTRIBUTING.md). Contributing is not only about fixing bugs or implementing new functions. Improving the texinfo of the functions help files or adding BISTs and demos at the end of the function files is also important. Fixing a typo in the help file is still of value though. So don't hesitate to contribute! :+1: statistics-release-1.7.3/doc/000077500000000000000000000000001475240274700161135ustar00rootroot00000000000000statistics-release-1.7.3/doc/statistics.png000066400000000000000000000006421475240274700210150ustar00rootroot00000000000000‰PNG  IHDRddÿ€tIMEß Ó@Ÿ…tEXtCommentCreated with GIMPW1IDATxÚíÜAƒ Ph¼ÿ•颫&mé(Âû«vaŒO%æRJ’º<À‚ ,X`Á‚ ,°`ÁºQ¶c›åüöw‘ˆ±^:9§¥ž³†°`Á‚K`Á‚kvgØ„6­³a…6­Ûï3Óz&+fžaw<­² <,X뾤œËp™ +§ÈåËî†j–ÔMJï>u<±À·¬ÕÞŽÛHw^à›¼†~êpÕeû Zw7„ ,é]ù÷úaå߈3 Ã,X\àÏè¢cßìíü^X±oöömªY°`Á‚%°`ÁÒî´O¤3¬Êö¬t)G´J†!,X°`ÁX°`Á‚K`Á‚uy¦ý®CÄ’Ä9¿ë`ªY°` ,X°`Á‚%°`Áº>Odœ1ë'ø£#IEND®B`‚statistics-release-1.7.3/inst/000077500000000000000000000000001475240274700163235ustar00rootroot00000000000000statistics-release-1.7.3/inst/@cvpartition/000077500000000000000000000000001475240274700207655ustar00rootroot00000000000000statistics-release-1.7.3/inst/@cvpartition/cvpartition.m000066400000000000000000000206011475240274700235040ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{C} =} cvpartition (@var{X}, [@var{partition_type}, [@var{k}]]) ## ## Create a partition object for cross validation. ## ## @var{X} may be a positive integer, interpreted as the number of values ## @var{n} to partition, or a vector of length @var{n} containing class ## designations for the elements, in which case the partitioning types ## @var{KFold} and @var{HoldOut} attempt to ensure each partition represents the ## classes proportionately. ## ## @var{partition_type} must be one of the following: ## ## @table @asis ## @item @samp{KFold} ## Divide set into @var{k} equal-size subsets (this is the default, with ## @var{k}=10). ## @item @samp{HoldOut} ## Divide set into two subsets, "training" and "validation". If @var{k} is a ## fraction, that is the fraction of values put in the validation subset; if it ## is a positive integer, that is the number of values in the validation subset ## (by default @var{k}=0.1). ## @item @samp{LeaveOut} ## Leave-one-out partition (each element is placed in its own subset). ## @item @samp{resubstitution} ## Training and validation subsets that both contain all the original elements. ## @item @samp{Given} ## Subset indices are as given in @var{X}. ## @end table ## ## The following fields are defined for the @samp{cvpartition} class: ## ## @table @asis ## @item @samp{classes} ## Class designations for the elements. ## @item @samp{inds} ## Subset indices for the elements. ## @item @samp{n_classes} ## Number of different classes. ## @item @samp{NumObservations} ## @var{n}, number of elements in data set. ## @item @samp{NumTestSets} ## Number of testing subsets. ## @item @samp{TestSize} ## Number of elements in (each) testing subset. ## @item @samp{TrainSize} ## Number of elements in (each) training subset. ## @item @samp{Type} ## Partition type. ## @end table ## ## @seealso{crossval, @@cvpartition/display} ## @end deftypefn function C = cvpartition (X, partition_type = "KFold", k = []) if (nargin < 1 || nargin > 3 || !isvector(X)) print_usage (); endif if (isscalar (X)) n = X; n_classes = 1; else n = numel (X); endif switch (tolower (partition_type)) case {"kfold", "holdout", "leaveout", "resubstitution", "given"} otherwise warning ("cvpartition: unrecognized type, using KFold.") partition_type = "KFold"; endswitch switch (tolower (partition_type)) case {"kfold", "holdout", "given"} if (! isscalar (X)) [y, ~, j] = unique (X(:)); n_per_class = accumarray (j, 1); n_classes = numel (n_per_class); endif endswitch C = struct ("classes", [], "inds", [], "n_classes", [], "NumObservations", ... [], "NumTestSets", [], "TestSize", [], "TrainSize", [], "Type", []); ## The non-Matlab fields classes, inds, n_classes ## are only useful for some methods switch (tolower (partition_type)) case "kfold" if (isempty (k)) k = 10; endif if (n_classes == 1) inds = floor((0:(n-1))' * (k / n)) + 1; else inds = nan(n, 1); for i = 1:n_classes ## Alternate ordering over classes so that ## the subsets are more nearly the same size if (mod (i, 2)) inds(j == i) = floor((0:(n_per_class(i)-1))' * ... (k / n_per_class(i))) + 1; else inds(j == i) = floor(((n_per_class(i)-1):-1:0)' * ... (k / n_per_class(i))) + 1; endif endfor endif C.inds = inds; C.NumTestSets = k; [~, ~, jj] = unique (inds); n_per_subset = accumarray (jj, 1); C.TrainSize = n - n_per_subset; C.TestSize = n_per_subset; case "given" C.inds = j; C.NumTestSets = n_classes; C.TrainSize = n - n_per_class; C.TestSize = n_per_class; case "holdout" if (isempty (k)) k = 0.1; endif if (k < 1) f = k; # target fraction to sample k = round (k * n); # number of samples else f = k / n; endif inds = zeros (n, 1, "logical"); if (n_classes == 1) inds(randsample(n, k)) = true; # indices for test set else # sample from each class k_check = 0; for i = 1:n_classes ki = round(f*n_per_class(i)); inds(find(j == i)(randsample(n_per_class(i), ki))) = true; k_check += ki; endfor if (k_check < k) # add random elements to test set to make it k inds(find(!inds)(randsample(n - k_check, k - k_check))) = true; elseif (k_check > k) # remove random elements from test set inds(find(inds)(randsample(k_check, k_check - k))) = false; endif C.classes = j; endif C.n_classes = n_classes; C.TrainSize = n - k; C.TestSize = k; C.NumTestSets = 1; C.inds = inds; case "leaveout" C.TrainSize = ones (n, 1); C.TestSize = (n-1) * ones (n, 1); C.NumTestSets = n; case "resubstitution" C.TrainSize = C.TestSize = n; C.NumTestSets = 1; endswitch C.NumObservations = n; C.Type = tolower (partition_type); C = class (C, "cvpartition"); endfunction %!demo %! ## Partition with Fisher iris dataset (n = 150) %! ## Stratified by species %! load fisheriris %! y = species; %! ## 10-fold cross-validation partition %! c = cvpartition (species, 'KFold', 10) %! ## leave-10-out partition %! c1 = cvpartition (species, 'HoldOut', 10) %! idx1 = test (c, 2); %! idx2 = training (c, 2); %! ## another leave-10-out partition %! c2 = repartition (c1) %!test %! C = cvpartition (ones (10, 1)); %! assert (isa (C, "cvpartition"), true); %!test %! C = cvpartition (ones (10, 1), "KFold", 5); %! assert (get (C, "NumObservations"), 10); %! assert (get (C, "NumTestSets"), 5); %! assert (get (C, "TrainSize"), ones(5,1) * 8); %! assert (get (C, "TestSize"), ones (5,1) * 2); %! assert (get (C, "inds"), [1 1 2 2 3 3 4 4 5 5]'); %! assert (get (C, "Type"), "kfold"); %!test %! C = cvpartition (ones (10, 1), "KFold", 2); %! assert (get (C, "NumObservations"), 10); %! assert (get (C, "NumTestSets"), 2); %! assert (get (C, "TrainSize"), [5; 5]); %! assert (get (C, "TestSize"), [5; 5]); %! assert (get (C, "inds"), [1 1 1 1 1 2 2 2 2 2]'); %! assert (get (C, "Type"), "kfold"); %!test %! C = cvpartition (ones (10, 1), "HoldOut", 5); %! assert (get (C, "NumObservations"), 10); %! assert (get (C, "NumTestSets"), 1); %! assert (get (C, "TrainSize"), 5); %! assert (get (C, "TestSize"), 5); %! assert (class (get (C, "inds")), "logical"); %! assert (length (get (C, "inds")), 10); %! assert (get (C, "Type"), "holdout"); %!test %! C = cvpartition ([1 2 3 4 5 6 7 8 9 10], "LeaveOut", 5); %! assert (get (C, "NumObservations"), 10); %! assert (get (C, "NumTestSets"), 10); %! assert (get (C, "TrainSize"), ones (10, 1)); %! assert (get (C, "TestSize"), ones (10, 1) * 9); %! assert (get (C, "inds"), []); %! assert (get (C, "Type"), "leaveout"); %!test %! C = cvpartition ([1 2 3 4 5 6 7 8 9 10], "resubstitution", 5); %! assert (get (C, "NumObservations"), 10); %! assert (get (C, "NumTestSets"), 1); %! assert (get (C, "TrainSize"), 10); %! assert (get (C, "TestSize"), 10); %! assert (get (C, "inds"), []); %! assert (get (C, "Type"), "resubstitution"); %!test %! C = cvpartition ([1 2 3 4 5 6 7 8 9 10], "Given", 2); %! assert (get (C, "NumObservations"), 10); %! assert (get (C, "NumTestSets"), 10); %! assert (get (C, "TrainSize"), ones (10, 1) * 9); %! assert (get (C, "TestSize"), ones (10, 1)); %! assert (get (C, "inds"), [1:10]'); %! assert (get (C, "Type"), "given"); %!warning ... %! C = cvpartition ([1 2 3 4 5 6 7 8 9 10], "some", 2); statistics-release-1.7.3/inst/@cvpartition/display.m000066400000000000000000000050111475240274700226050ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} display (@var{C}) ## ## Display a @samp{cvpartition} object, @var{C}. ## ## @seealso{@@cvpartition/cvpartition} ## @end deftypefn function display (C) if (nargin != 1) print_usage (); endif switch C.Type case "kfold" str = "K-fold"; case "given" str = "Given"; case "holdout" str = "HoldOut"; case "leaveout" str = "Leave-One-Out"; case "resubstitution" str = "Resubstitution"; otherwise str = "Unknown-type"; endswitch disp([str " cross validation partition"]) disp([" N: " num2str(C.NumObservations)]) disp(["NumTestSets: " num2str(C.NumTestSets)]) disp([" TrainSize: " num2str(C.TrainSize')]) disp([" TestSize: " num2str(C.TestSize')]) endfunction %!test %! C = cvpartition (ones (10, 1), "KFold", 5); %! s = evalc ("display (C)"); %! sout = "K-fold cross validation partition"; %! assert (strcmpi (s(1:length (sout)), sout), true); %!test %! C = cvpartition (ones (10, 1), "HoldOut", 5); %! s = evalc ("display (C)"); %! sout = "HoldOut cross validation partition"; %! assert (strcmpi (s(1:length (sout)), sout), true); %!test %! C = cvpartition (ones (10, 1), "LeaveOut", 5); %! s = evalc ("display (C)"); %! sout = "Leave-One-Out cross validation partition"; %! assert (strcmpi (s(1:length (sout)), sout), true); %!test %! C = cvpartition (ones (10, 1), "resubstitution", 5); %! s = evalc ("display (C)"); %! sout = "Resubstitution cross validation partition"; %! assert (strcmpi (s(1:length (sout)), sout), true); %!test %! C = cvpartition (ones (10, 1), "Given", 5); %! s = evalc ("display (C)"); %! sout = "Given cross validation partition"; %! assert (strcmpi (s(1:length (sout)), sout), true); %!error display () statistics-release-1.7.3/inst/@cvpartition/get.m000066400000000000000000000036621475240274700217310ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{s} = get} (@var{C}, @var{f}) ## ## Get a field, @var{f}, from a @samp{cvpartition} object, @var{C}. ## ## @seealso{@@cvpartition/cvpartition} ## @end deftypefn function s = get (c, f) if (nargin == 1) s = c; elseif (nargin == 2) if (ischar (f)) switch (f) case {"classes", "inds", "n_classes", "NumObservations", ... "NumTestSets", "TestSize", "TrainSize", "Type"} s = eval (["struct(c)." f]); otherwise error ("get: invalid property %s.", f); endswitch else error ("get: expecting the property to be a string."); endif else print_usage (); endif endfunction %!shared C %! C = cvpartition (ones (10, 1), "KFold", 5); %!assert (get (C, "NumObservations"), 10); %!assert (get (C, "NumTestSets"), 5); %!assert (get (C, "TrainSize"), ones(5,1) * 8); %!assert (get (C, "TestSize"), ones (5,1) * 2); %!assert (get (C, "inds"), [1 1 2 2 3 3 4 4 5 5]'); %!assert (get (C, "Type"), "kfold"); %!error get (C, "some") %!error get (C, 25) %!error get (C, {25}) statistics-release-1.7.3/inst/@cvpartition/repartition.m000066400000000000000000000054101475240274700235030ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Cnew} =} repartition (@var{C}) ## ## Return a new cvpartition object. ## ## @var{C} should be a @samp{cvpartition} object. @var{Cnew} will use the same ## partition_type as @var{C} but redo any randomization performed (currently, ## only the HoldOut type uses randomization). ## ## @seealso{@@cvpartition/cvpartition} ## @end deftypefn function Cnew = repartition (C) if (nargin < 1 || nargin > 2) print_usage (); endif Cnew = C; switch (C.Type) case 'kfold' case 'given' case 'holdout' # Currently, only the HoldOut method uses randomization n = C.NumObservations; k = C.TestSize; n_classes = C.n_classes; if (k < 1) f = k; # target fraction to sample k = round (k * n); #number of samples else f = k / n; endif inds = zeros (n, 1, "logical"); if (n_classes == 1) inds(randsample(n, k)) = true; #indices for test set else # sample from each class j = C.classes; #integer class labels n_per_class = accumarray (j, 1); n_classes = numel (n_per_class); k_check = 0; for i = 1:n_classes ki = round(f*n_per_class(i)); inds(find(j == i)(randsample(n_per_class(i), ki))) = true; k_check += ki; endfor if (k_check < k) # add random elements to test set to make it k inds(find(!inds)(randsample(n - k_check, k - k_check))) = true; elseif (k_check > k) # remove random elements from test set inds(find(inds)(randsample(k_check, k_check - k))) = false; endif endif Cnew.inds = inds; case "leaveout" case "resubstitution" endswitch endfunction %!test %! C = cvpartition (ones (10, 1), "KFold", 5); %! Cnew = repartition (C); %! assert (isa (Cnew, "cvpartition"), true); %!test %! C = cvpartition (ones (100, 1), "HoldOut", 5); %! Cnew = repartition (C); %! indC = get (C, "inds"); %! indCnew = get (Cnew, "inds"); %! assert (isequal (indC, indCnew), false); statistics-release-1.7.3/inst/@cvpartition/set.m000066400000000000000000000040541475240274700217410ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Cnew} =} set (@var{C}, @var{field}, @var{value}) ## ## Set @var{field}, in a @samp{cvpartition} object, @var{C}. ## ## @seealso{@@cvpartition/cvpartition} ## @end deftypefn function s = set (c, varargin) s = struct(c); if (length (varargin) < 2 || rem (length (varargin), 2) != 0) error ("set: expecting field/value pairs."); endif while (length (varargin) > 1) prop = varargin{1}; val = varargin{2}; varargin(1:2) = []; if (ischar (prop)) switch (prop) case {"classes", "inds", "n_classes", "NumObservations", ... "NumTestSets", "TestSize", "TrainSize", "Type"} s = setfield (s, prop, val); otherwise error ("set: invalid field %s.", prop); endswitch else error ("set: expecting the field to be a string."); endif endwhile s = class (s, "cvpartition"); endfunction %!shared C %! C = cvpartition (ones (10, 1), "KFold", 5); %!test %! Cnew = set (C, "inds", [1 2 2 2 3 4 3 4 5 5]'); %! assert (get (Cnew, "inds"), [1 2 2 2 3 4 3 4 5 5]'); %!error set (C) %!error set (C, "NumObservations") %!error set (C, "some", 15) %!error set (C, 15, 15) statistics-release-1.7.3/inst/@cvpartition/test.m000066400000000000000000000040541475240274700221250ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{inds} =} test (@var{C}, [@var{i}]) ## ## Return logical vector for testing-subset indices from a @samp{cvpartition} ## object, @var{C}. @var{i} is the fold index (default is 1). ## ## @seealso{@@cvpartition/cvpartition, @@cvpartition/training} ## @end deftypefn function inds = test (C, i = []) if (nargin < 1 || nargin > 2) print_usage (); endif if (nargin < 2 || isempty (i)) i = 1; endif switch (C.Type) case {"kfold", "given"} inds = C.inds == i; case "holdout" inds = C.inds; case "leaveout" inds = zeros(C.NumObservations, 1, "logical"); inds(i) = true; case "resubstitution" inds = ones(C.NumObservations, 1, "logical"); endswitch endfunction %!shared C %! C = cvpartition (ones (10, 1), "KFold", 5); %!assert (test (C, 1), logical ([1 1 0 0 0 0 0 0 0 0]')) %!assert (test (C, 2), logical ([0 0 1 1 0 0 0 0 0 0]')) %!assert (test (C, 3), logical ([0 0 0 0 1 1 0 0 0 0]')) %!assert (test (C, 4), logical ([0 0 0 0 0 0 1 1 0 0]')) %!assert (test (C, 5), logical ([0 0 0 0 0 0 0 0 1 1]')) %!test %! C = set (C, "inds", [1 2 2 2 3 4 3 4 5 5]'); %!assert (test (C), logical ([1 0 0 0 0 0 0 0 0 0]')) %!assert (test (C, 2), logical ([0 1 1 1 0 0 0 0 0 0]')) %!assert (test (C, 3), logical ([0 0 0 0 1 0 1 0 0 0]')) statistics-release-1.7.3/inst/@cvpartition/training.m000066400000000000000000000041271475240274700227620ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{inds} =} training (@var{C}, [@var{i}]) ## ## Return logical vector for training-subset indices from a @samp{cvpartition} ## object, @var{C}. @var{i} is the fold index (default is 1). ## ## @seealso{@@cvpartition/cvpartition, @@cvpartition/test} ## @end deftypefn function inds = training (C, i = []) if (nargin < 1 || nargin > 2) print_usage (); endif if (nargin < 2 || isempty (i)) i = 1; endif switch (C.Type) case {"kfold", "given"} inds = C.inds != i; case "holdout" inds = ! C.inds; case "leaveout" inds = ones (C.NumObservations, 1, "logical"); inds(i) = false; case "resubstitution" inds = ones (C.NumObservations, 1, "logical"); endswitch endfunction %!shared C %! C = cvpartition (ones (10, 1), "KFold", 5); %!assert (training (C, 1), logical ([0 0 1 1 1 1 1 1 1 1]')) %!assert (training (C, 2), logical ([1 1 0 0 1 1 1 1 1 1]')) %!assert (training (C, 3), logical ([1 1 1 1 0 0 1 1 1 1]')) %!assert (training (C, 4), logical ([1 1 1 1 1 1 0 0 1 1]')) %!assert (training (C, 5), logical ([1 1 1 1 1 1 1 1 0 0]')) %!test %! C = set (C, "inds", [1 2 2 2 3 4 3 4 5 5]'); %!assert (training (C), logical ([0 1 1 1 1 1 1 1 1 1]')) %!assert (training (C, 2), logical ([1 0 0 0 1 1 1 1 1 1]')) %!assert (training (C, 3), logical ([1 1 1 1 0 1 0 1 1 1]')) statistics-release-1.7.3/inst/Classification/000077500000000000000000000000001475240274700212565ustar00rootroot00000000000000statistics-release-1.7.3/inst/Classification/ClassificationDiscriminant.m000066400000000000000000001634561475240274700267530ustar00rootroot00000000000000## Copyright (C) 2024 Ruchika Sonagote ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClassificationDiscriminant ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} ClassificationDiscriminant (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} ClassificationDiscriminant (@dots{}, @var{name}, @var{value}) ## ## Create a @qcode{ClassificationDiscriminant} class object containing a ## discriminant analysis model. ## ## @code{@var{obj} = ClassificationDiscriminant (@var{X}, @var{Y})} returns a ## ClassificationDiscriminant object, with @var{X} as the predictor data ## and @var{Y} containing the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or variables. ## @var{X} will be used to train the discriminant model. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can contain any type of ## categorical data. @var{Y} must have the same number of rows as @var{X}. ## @end itemize ## ## @code{@var{obj} = ClassificationDiscriminant (@dots{}, @var{name}, ## @var{value})} returns a ClassificationDiscriminant object with parameters ## specified by @qcode{Name-Value} pair arguments. ## Type @code{help ClassificationDiscriminant} for more info. ## ## A @qcode{ClassificationDiscriminant} object, @var{obj}, stores the labeled ## training data and various parameters for the discriminant analysis model, ## which can be accessed in the following fields: ## ## @multitable @columnfractions 0.23 0.02 0.75 ## @headitem @var{Field} @tab @tab @var{Description} ## ## @item @qcode{X} @tab @tab Unstandardized predictor data, specified as a ## numeric matrix. Each column of @var{X} represents one predictor (variable), ## and each row represents one observation. ## ## @item @qcode{Y} @tab @tab Class labels, specified as a logical, ## numeric vector, or cell array of character vectors. Each value in @var{Y} ## is the observed class label for the corresponding row in @var{X}. ## ## @item @qcode{NumObservations} @tab @tab Number of observations used in ## training the ClassificationDiscriminant model, specified as a positive ## integer scalar. ## ## @item @qcode{RowsUsed} @tab @tab Rows of the original training data ## used in fitting the ClassificationDiscriminant model, specified as a ## numerical vector. ## ## @item @qcode{PredictorNames} @tab @tab Predictor variable names, ## specified as a cell array of character vectors. The variable names are in ## the same order in which they appear in the training data @var{X}. ## ## @item @qcode{ResponseName} @tab @tab Response variable name, specified ## as a character vector. ## ## @item @qcode{ClassNames} @tab @tab Names of the classes in the training ## data @var{Y} with duplicates removed, specified as a cell array of character ## vectors. ## ## @item @qcode{Prior} @tab @tab Prior probabilities for each class, ## specified as a numeric vector. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{Cost} @tab @tab Cost of the misclassification of a point, ## specified as a square matrix. @qcode{Cost(i,j)} is the cost of classifying a ## point into class @qcode{j} if its true class is @qcode{i} (that is, the rows ## correspond to the true class and the columns correspond to the predicted ## class). The order of the rows and columns in @qcode{Cost} corresponds to the ## order of the classes in @qcode{ClassNames}. The number of rows and columns ## in @qcode{Cost} is the number of unique classes in the response. By default, ## @qcode{Cost(i,j) = 1} if @qcode{i != j}, and @qcode{Cost(i,j) = 0} if ## @qcode{i = j}. In other words, the cost is 0 for correct classification and ## 1 for incorrect classification. ## ## @item @qcode{Sigma} @tab @tab Within-class covariance matrix, specified ## as a numeric matrix. For 'linear' discriminant type matrix is of size ## @math{pxp}, where p is the number of predictors. ## ## @item @qcode{Mu} @tab @tab Class means, specified as a @math{Kxp} ## real matrix. K is the number of classes, and p is the number of ## predictors. ## ## @item @qcode{Coeffs} @tab @tab Coefficient matrices, specified as a ## struct array. ## ## @item @qcode{Delta} @tab @tab Threshold for linear discriminant model, ## specified as a numeric scalar. ## ## @item @qcode{DiscrimType} @tab @tab Discriminant type, specified as a ## character vector. ## ## @item @qcode{Gamma} @tab @tab Gamma regularization parameter, specified ## as a numeric scalar. ## ## @item @qcode{MinGamma} @tab @tab Minimum value of Gamma so that the ## correlation matrix is invertible, specified as nonnegative scalar. ## ## @item @qcode{LogDetSigma} @tab @tab Logarithm of the determinant of the ## within-class covariance matrix. For linear discriminant analysis it is ## specified as a numeric scalar. ## ## @item @qcode{XCentered} @tab @tab X data with class means ## subtracted, returned as a real matrix. ## ## @end multitable ## ## @seealso{fitcdiscr} ## @end deftypefn properties (Access = public) X = []; # Predictor data Y = []; # Class labels NumObservations = []; # Number of observations in training dataset RowsUsed = []; # Rows used in fitting NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of Misclassification ScoreTransform = []; # Transformation for classification scores Sigma = []; # Within-class covariance Mu = []; # Class means Coeffs = []; # Coefficient matrices Delta = []; # Threshold for linear discriminant model DiscrimType = []; # Discriminant type Gamma = []; # Gamma regularization parameter MinGamma = []; # Minmum value of Gamma LogDetSigma = []; # Log of det of within-class covariance matrix XCentered = []; # X data with class means subtracted endproperties methods (Access = public) ## constructor function this = ClassificationDiscriminant (X, Y, varargin) ## Check for sufficient number of input arguments if (nargin < 2) error ("ClassificationDiscriminant: too few input arguments."); endif ## Validate X if (! isnumeric (X)) error ("ClassificationDiscriminant: X must be a numeric matrix."); endif ## Check X and Y have the same number of observations if (rows (X) != rows (Y)) error (["ClassificationDiscriminant: number of rows ", ... "in X and Y must be equal."]); endif ## Assign original X and Y data this.X = X; this.Y = Y; ## Get groups in Y [gY, gnY, glY] = grp2idx (Y); ## Set default values before parsing optional parameters ClassNames = []; Cost = []; DiscrimType = "linear"; Gamma = 0; Delta = 0; NumPredictors = []; PredictorNames = []; ResponseName = 'Y'; Prior = "empirical"; FillCoeffs = "on"; this.ScoreTransform = 'none'; ## Parse optional parameters while (numel (varargin) > 0) switch (lower (varargin{1})) case "predictornames" PredictorNames = varargin{2}; if (! iscellstr (PredictorNames)) error (strcat (["ClassificationDiscriminant: 'PredictorNames'"], ... [" must be supplied as a cellstring array."])); elseif (numel (PredictorNames) != columns (X)) error (strcat (["ClassificationDiscriminant: 'PredictorNames'"], ... [" must equal the number of columns in X."])); endif case "responsename" ResponseName = varargin{2}; if (! ischar (ResponseName)) error (strcat (["ClassificationDiscriminant: 'ResponseName'"], ... [" must be a character vector."])); endif case "classnames" ClassNames = varargin{2}; if (! (iscellstr (ClassNames) || isnumeric (ClassNames) || islogical (ClassNames))) error (strcat (["ClassificationDiscriminant: 'ClassNames'"], ... [" must be a cellstring, logical or numeric"], ... [" vector."])); endif ## Check that all class names are available in gnY msg = strcat (["ClassificationDiscriminant: not all"], ... [" 'ClassNames' are present in Y."]); if (iscellstr (ClassNames)) if (! all (cell2mat (cellfun (@(x) any (strcmp (x, gnY)), ClassNames, "UniformOutput", false)))) error (msg); endif else if (! all (cell2mat (arrayfun (@(x) any (x == glY), ClassNames, "UniformOutput", false)))) error (msg); endif endif case "prior" Prior = varargin{2}; if (! ((isnumeric (Prior) && isvector (Prior)) || (strcmpi (Prior, "empirical") || strcmpi (Prior, "uniform")))) error (strcat (["ClassificationDiscriminant: 'Prior' must"], ... [" be either a numeric or a character vector."])); endif case "cost" Cost = varargin{2}; if (! (isnumeric (Cost) && issquare (Cost))) error (strcat (["ClassificationDiscriminant: 'Cost'"], ... [" must be a numeric square matrix."])); endif case "scoretransform" name = "ClassificationDiscriminant"; this.ScoreTransform = parseScoreTransform (varargin{2}, name); case "discrimtype" DiscrimType = tolower (varargin{2}); if (! (strcmpi (DiscrimType, "linear"))) error (strcat (["ClassificationDiscriminant: unsupported "], ... [" discriminant type."])); endif case "fillcoeffs" FillCoeffs = tolower (varargin{2}); if (! any (strcmpi (FillCoeffs, {"on", "off"}))) error (strcat (["ClassificationDiscriminant: 'FillCoeffs'"], ... [" must be 'on' or 'off'."])); endif case "gamma" Gamma = varargin{2}; if (Gamma >= 1 || Gamma < 0) error (strcat (["ClassificationDiscriminant: 'Gamma'"], ... [" must be between 0 and 1."])); endif otherwise error (strcat (["ClassificationDiscriminant: invalid"], ... [" parameter name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Generate default predictors and response variabe names (if necessary) NumPredictors = columns (X); if (isempty (PredictorNames)) for i = 1:NumPredictors PredictorNames {i} = strcat ("x", num2str (i)); endfor endif if (isempty (ResponseName)) ResponseName = "Y"; endif ## Assign predictors and response variable names this.NumPredictors = NumPredictors; this.PredictorNames = PredictorNames; this.ResponseName = ResponseName; ## Handle class names if (! isempty (ClassNames)) if (iscellstr (ClassNames)) ru = find (! ismember (gnY, ClassNames)); else ru = find (! ismember (glY, ClassNames)); endif for i = 1:numel (ru) gY(gY == ru(i)) = NaN; endfor endif ## Remove missing values from X and Y RowsUsed = ! logical (sum (isnan ([X, gY]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [gY, gnY, glY] = grp2idx (Y); this.ClassNames = gnY; ## Check X contains valid data if (! (isnumeric (X) && isfinite (X))) error ("ClassificationDiscriminant: invalid values in X."); endif this.NumObservations = rows (X); this.RowsUsed = cast (RowsUsed, "double"); ## Handle Prior and Cost if (strcmpi ("uniform", Prior)) this.Prior = ones (size (gnY)) ./ numel (gnY); elseif (isempty (Prior) || strcmpi ("empirical", Prior)) pr = []; for i = 1:numel (gnY) pr = [pr; sum(gY==i)]; endfor this.Prior = pr ./ sum (pr); elseif (isnumeric (Prior)) if (numel (gnY) != numel (Prior)) error (strcat (["ClassificationDiscriminant: the elements"], ... [" in 'Prior' do not correspond to the"], ... [" selected classes in Y."])); endif this.Prior = Prior ./ sum (Prior); endif if (isempty (Cost)) this.Cost = cast (! eye (numel (gnY)), "double"); else if (numel (gnY) != sqrt (numel (Cost))) error (strcat (["ClassificationDiscriminant: the number"], ... [" of rows and columns in 'Cost' must"], ... [" correspond to selected classes in Y."])); endif this.Cost = Cost; endif ## Assign DiscrimType this.DiscrimType = DiscrimType; this.Delta = Delta; this.Gamma = Gamma; num_classes = numel (this.ClassNames); num_features = columns (X); this.Mu = zeros (num_classes, num_features); for i = 1:num_classes this.Mu(i, :) = mean (X(gY == i, :), 1); endfor ## Center the predictors (XCentered) this.XCentered = zeros (size (X)); for i = 1:rows (X) class_idx = gY(i); this.XCentered(i, :) = X(i, :) - this.Mu(class_idx, :); endfor ## Calculate Within-class covariance (Sigma) if (strcmp (this.DiscrimType, "linear")) this.Sigma = zeros (num_features); for i = 1:num_classes Xi = X(gY == i, :) - this.Mu(i, :); this.Sigma = this.Sigma + (Xi' * Xi); endfor this.Sigma = this.Sigma / (this.NumObservations - num_classes); ## Check for predictors zero within-class variance zwcv = find (diag (this.Sigma) == 0); if (! isempty (zwcv)) msg = strcat (["ClassificationDiscriminant: Predictor"], ... [" '%s' has zero within-class variance."]); error (msg, PredictorNames{zwcv(1)}); endif D = diag (diag (this.Sigma)); ## fix me: MinGamma calculation is not same as Matlab. ## Instead of using (det (sigma) > 0) this criteria (line no: 391) ## may be Matlab is using some threshold. ## Also linear search might not be best here. ## Regularize Sigma this.Sigma = (this.Sigma * (1 - this.Gamma)) + (D * this.Gamma); this.MinGamma = 0; ## Calculate the MinGamma if (det (this.Sigma) <= 0) gamma = 0; step = 1e-15; sigma = this.Sigma; while (true) sigma = (sigma * (1 - gamma)) + (D * gamma); if (det (sigma) > 0) minGamma = gamma; break; endif gamma = gamma + step; if (gamma > 1) error (["ClassificationDiscriminant: failed to ", ... "find 'MinGamma' within reasonable range."]); endif endwhile this.MinGamma = minGamma; if (this.Gamma < minGamma) this.Gamma = minGamma; endif this.Sigma = (this.Sigma * (1 - this.Gamma)) + (D * this.Gamma); endif endif ## Calculate log determinant of Sigma if (strcmp (this.DiscrimType, "linear")) this.LogDetSigma = log (det (this.Sigma)); endif if (strcmpi (FillCoeffs, "on")) ## Calculate coefficients switch (this.DiscrimType) case "linear" this.Coeffs = struct(); for i = 1:num_classes for j = 1:num_classes this.Coeffs(i, j).DiscrimType = ""; this.Coeffs(i, j).Const = []; this.Coeffs(i, j).Linear = []; this.Coeffs(i, j).Class1 = this.ClassNames{i}; this.Coeffs(i, j).Class2 = this.ClassNames{j}; if (i != j) A = (this.Mu(i, :) - this.Mu(j, :)) / this.Sigma; K = log (this.Prior(i) ... / this.Prior(j)) - 0.5 * (this.Mu(i, :) ... / this.Sigma * this.Mu(i, :)') + 0.5 * (this.Mu(j, :) ... / this.Sigma * this.Mu(j, :)'); this.Coeffs(i, j).DiscrimType = this.DiscrimType; this.Coeffs(i, j).Linear = A'; this.Coeffs(i, j).Const = K; endif endfor endfor endswitch endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationDiscriminant} {@var{label} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {ClassificationDiscriminant} {[@var{label}, @var{score}, @var{cost}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the discriminant ## analysis model from a ClassificationDiscriminant object. ## ## @code{@var{label} = predict (@var{obj}, @var{XC})} returns the vector of ## labels predicted for the corresponding instances in @var{XC}, using the ## predictor data in @code{obj.X} and corresponding labels, @code{obj.Y}, ## stored in the ClassificationDiscriminant model, @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationDiscriminant} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the discriminant ## model in @var{obj}. ## @end itemize ## ## @code{[@var{label}, @var{score}, @var{cost}] = predict (@var{obj}, ## @var{XC})} also returns @var{score}, which contains the predicted class ## scores or posterior probabilities for each instance of the corresponding ## unique classes, and @var{cost}, which is a matrix containing the expected ## cost of the classifications. ## ## The @var{score} matrix contains the posterior probabilities for each ## class, calculated using the multivariate normal probability density ## function and the prior probabilities of each class. These scores are ## normalized to ensure they sum to 1 for each observation. ## ## The @var{cost} matrix contains the expected classification cost for each ## class, computed based on the posterior probabilities and the specified ## misclassification costs. ## ## @seealso{ClassificationDiscriminant, fitcdiscr} ## @end deftypefn function [label, score, cost] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error ("ClassificationDiscriminant.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("ClassificationDiscriminant.predict: XC is empty."); elseif (columns (this.X) != columns (XC)) error (strcat (["ClassificationDiscriminant.predict:"], ... [" XC must have the same number of"], ... [" predictors as the trained model."])); endif ## Get training data and labels X = this.X(logical (this.RowsUsed),:); Y = this.Y(logical (this.RowsUsed),:); numObservations = rows (XC); numClasses = numel (this.ClassNames); score = zeros (numObservations, numClasses); cost = zeros (numObservations, numClasses); ## Calculate discriminant score (posterior probabilities) for i = 1:numClasses for j = 1:numObservations P_x_given_k = mvnpdf (XC(j, :), this.Mu(i, :), this.Sigma); score(j, i) = P_x_given_k * this.Prior(i); endfor endfor ## Normalize score to get posterior probabilities scoreSum = sum (score, 2); score = bsxfun (@rdivide, score, scoreSum); ## Handle numerical issues score(isnan (score)) = 0; ## Calculate expected classification cost for i = 1:numClasses cost(:, i) = sum (bsxfun (@times, score, this.Cost(:, i)'), 2); endfor ## Predict the class labels based on the minimum cost [~, minIdx] = min (cost, [], 2); label = this.ClassNames(minIdx); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationDiscriminant} {@var{L} =} loss (@var{obj}, @var{X}, @var{Y}) ## @deftypefnx {ClassificationDiscriminant} {@var{L} =} loss (@dots{}, @var{name}, @var{value}) ## ## Compute loss for a trained ClassificationDiscriminant object. ## ## @code{@var{L} = loss (@var{obj}, @var{X}, @var{Y})} computes the loss, ## @var{L}, using the default loss function @qcode{'mincost'}. ## ## @itemize ## @item ## @code{obj} is a @var{ClassificationDiscriminant} object trained on ## @code{X} and @code{Y}. ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or ## variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels ## of corresponding predictor data in @var{X}. @var{Y} must have same ## numbers of Rows as @var{X}. ## @end itemize ## ## @code{@var{L} = loss (@dots{}, @var{name}, @var{value})} allows ## additional options specified by @var{name}-@var{value} pairs: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"LossFun"} @tab @tab Specifies the loss function to use. ## Can be a function handle with four input arguments (C, S, W, Cost) ## which returns a scalar value or one of: ## 'binodeviance', 'classifcost', 'classiferror', 'exponential', ## 'hinge', 'logit','mincost', 'quadratic'. ## @itemize ## @item ## @code{C} is a logical matrix of size @math{NxK}, where @math{N} is the ## number of observations and @math{K} is the number of classes. ## The element @code{C(i,j)} is true if the class label of the i-th ## observation is equal to the j-th class. ## @item ## @code{S} is a numeric matrix of size @math{NxK}, where each element ## represents the classification score for the corresponding class. ## @item ## @code{W} is a numeric vector of length @math{N}, representing ## the observation weights. ## @item ## @code{Cost} is a @math{KxK} matrix representing the misclassification ## costs. ## @end itemize ## ## @item @qcode{"Weights"} @tab @tab Specifies observation weights, must be ## a numeric vector of length equal to the number of rows in X. ## Default is @code{ones (size (X, 1))}. loss normalizes the weights so that ## observation weights in each class sum to the prior probability of that ## class. When you supply Weights, loss computes the weighted ## classification loss. ## ## @end multitable ## ## @seealso{ClassificationDiscriminant} ## @end deftypefn function L = loss (this, X, Y, varargin) ## Check for sufficient input arguments if (nargin < 3) error ("ClassificationDiscriminant.loss: too few input arguments."); elseif (mod (nargin - 3, 2) != 0) error (strcat (["ClassificationDiscriminant.loss: name-value"], ... [" arguments must be in pairs."])); elseif (nargin > 7) error ("ClassificationDiscriminant.loss: too many input arguments."); endif ## Check for valid X if (isempty (X)) error ("ClassificationDiscriminant.loss: X is empty."); elseif (columns (this.X) != columns (X)) error (strcat (["ClassificationDiscriminant.loss: X must have the"], ... [" same number of predictors as the trained model."])); endif ## Default values LossFun = 'mincost'; Weights = []; ## Validate Y valid_types = {'char', 'string', 'logical', 'single', 'double', 'cell'}; if (! (any (strcmp (class (Y), valid_types)))) error ("ClassificationDiscriminant.loss: Y must be of a valid type."); endif ## Validate size of Y if (size (Y, 1) != size (X, 1)) error (strcat (["ClassificationDiscriminant.loss: Y must"], ... [" have the same number of rows as X."])); endif ## Parse name-value arguments while (numel (varargin) > 0) Value = varargin{2}; switch (tolower (varargin{1})) case 'lossfun' lf_opt = {"binodeviance", "classifcost", "classiferror", ... "exponential", "hinge","logit", "mincost", "quadratic"}; if (isa (Value, 'function_handle')) ## Check if the loss function is valid if (nargin (Value) != 4) error (strcat (["ClassificationDiscriminant.loss: custom"], ... [" loss function must accept exactly four"], ... [" input arguments."])); endif try n = 1; K = 2; C_test = false (n, K); S_test = zeros (n, K); W_test = ones (n, 1); Cost_test = ones (K) - eye (K); test_output = Value (C_test, S_test, W_test, Cost_test); if (! isscalar (test_output)) error (strcat (["ClassificationDiscriminant.loss:"], ... [" custom loss function must return"], ... [" a scalar value."])); endif catch error (strcat (["ClassificationDiscriminant.loss: custom"], ... [" loss function is not valid or does not"], ... [" produce correct output."])); end_try_catch LossFun = Value; elseif (ischar (Value) && any (strcmpi (Value, lf_opt))) LossFun = Value; else error ("ClassificationDiscriminant.loss: invalid loss function."); endif case 'weights' if (isnumeric (Value) && isvector (Value)) if (numel (Value) != size (X ,1)) error (strcat (["ClassificationDiscriminant.loss: number"], ... [" of 'Weights' must be equal to the"], ... [" number of rows in X."])); elseif (numel (Value) == size (X, 1)) Weights = Value; endif else error ("ClassificationDiscriminant.loss: invalid 'Weights'."); endif otherwise error (strcat (["ClassificationDiscriminant.loss: invalid"], ... [" parameter name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Check for missing values in X if (! isa (LossFun, 'function_handle')) lossfun = tolower (LossFun); if (! strcmp (lossfun, 'mincost') && ! strcmp (lossfun, 'classiferror') && ! strcmp (lossfun, 'classifcost') && any (isnan (X(:)))) L = NaN; return; endif endif ## Convert Y to a cell array of strings if (ischar (Y)) Y = cellstr (Y); elseif (isnumeric (Y)) Y = cellstr (num2str (Y)); elseif (islogical (Y)) Y = cellstr (num2str (double (Y))); elseif (iscell (Y)) Y = cellfun (@num2str, Y, 'UniformOutput', false); else error (strcat (["ClassificationDiscriminant.loss: Y must be a"], ... [" numeric, logical, char, string, or cell array."])); endif ## Check if Y contains correct classes if (! all (ismember (unique (Y), this.ClassNames))) error (strcat (["ClassificationDiscriminant.loss: Y must contain"], ... [" only the classes in ClassNames."])); endif ## Set default weights if not specified if (isempty (Weights)) Weights = ones (size (X, 1), 1); endif ## Normalize Weights unique_classes = this.ClassNames; class_prior_probs = this.Prior; norm_weights = zeros (size (Weights)); for i = 1:numel (unique_classes) class_idx = ismember (Y, unique_classes{i}); if (sum (Weights(class_idx)) > 0) norm_weights(class_idx) = ... Weights(class_idx) * class_prior_probs(i) / sum (Weights(class_idx)); endif endfor Weights = norm_weights / sum (norm_weights); ## Number of observations n = size (X, 1); ## Predict classification scores [label, scores] = predict (this, X); ## C is vector of K-1 zeros, with 1 in the ## position corresponding to the true class K = numel (this.ClassNames); C = false (n, K); for i = 1:n class_idx = find (ismember (this.ClassNames, Y{i})); C(i, class_idx) = true; endfor Y_new = C'; ## Compute the loss using custom loss function if (isa (LossFun, 'function_handle')) L = LossFun (C, scores, Weights, this.Cost); return; endif ## Compute the scalar classification score for each observation m_j = zeros (n, 1); for i = 1:n m_j(i) = scores(i,:) * Y_new(:,i); endfor ## Compute the loss switch (tolower (LossFun)) case 'binodeviance' b = log (1 + exp (-2 * m_j)); L = (Weights') * b; case 'hinge' h = max (0, 1 - m_j); L = (Weights') * h; case 'exponential' e = exp (-m_j); L = (Weights') * e; case 'logit' l = log (1 + exp (-m_j)); L = (Weights') * l; case 'quadratic' q = (1 - m_j) .^ 2; L = (Weights') * q; case 'classiferror' L = 0; for i = 1:n L = L + Weights(i) * (! isequal (Y(i), label(i))); endfor case 'mincost' Cost = this.Cost; L = 0; for i = 1:n f_Xj = scores(i, :); gamma_jk = f_Xj * Cost; [~, min_cost_class] = min (gamma_jk); cj = Cost(find (ismember (this.ClassNames, Y(i))), min_cost_class); L = L + Weights(i) * cj; endfor case 'classifcost' Cost = this.Cost; L = 0; for i = 1:n y_idx = find (ismember (this.ClassNames, Y(i))); y_hat_idx = find (ismember (this.ClassNames, label(i))); L = L + Weights(i) * Cost(y_idx, y_hat_idx); endfor otherwise error ("ClassificationDiscriminant.loss: invalid loss function."); endswitch endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationDiscriminant} {@var{m} =} margin (@var{obj}, @var{X}, @var{Y}) ## ## @code{@var{m} = margin (@var{obj}, @var{X}, @var{Y})} returns ## the classification margins for @var{obj} with data @var{X} and ## classification @var{Y}. @var{m} is a numeric vector of length size (X,1). ## ## @itemize ## @item ## @code{obj} is a @var{ClassificationDiscriminant} object trained on @code{X} ## and @code{Y}. ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or ## variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels ## of corresponding predictor data in @var{X}. @var{Y} must have same ## numbers of Rows as @var{X}. ## @end itemize ## ## The classification margin for each observation is the difference between ## the classification score for the true class and the maximal ## classification score for the false classes. ## ## @seealso{fitcdiscr, ClassificationDiscriminant} ## @end deftypefn function m = margin (this, X, Y) ## Check for sufficient input arguments if (nargin < 3) error ("ClassificationDiscriminant.margin: too few input arguments."); endif ## Check for valid X if (isempty (X)) error ("ClassificationDiscriminant.margin: X is empty."); elseif (columns (this.X) != columns (X)) error (strcat (["ClassificationDiscriminant.margin: X must have the"], ... [" same number of predictors as the trained model."])); endif ## Validate Y valid_types = {'char', 'string', 'logical', 'single', 'double', 'cell'}; if (! (any (strcmp (class (Y), valid_types)))) error ("ClassificationDiscriminant.margin: Y must be of a valid type."); endif ## Validate X valid_types = {'single', 'double'}; if (! (any (strcmp (class (X), valid_types)))) error ("ClassificationDiscriminant.margin: X must be of a valid type."); endif ## Validate size of Y if (size (Y, 1) != size (X, 1)) error (strcat (["ClassificationDiscriminant.margin: Y must"], ... [" have the same number of rows as X."])); endif ## Convert Y to a cell array of strings if (ischar (Y)) Y = cellstr (Y); elseif (isnumeric (Y)) Y = cellstr (num2str (Y)); elseif (islogical (Y)) Y = cellstr (num2str (double (Y))); elseif (iscell (Y)) Y = cellfun (@num2str, Y, 'UniformOutput', false); else error (strcat (["ClassificationDiscriminant.margin: Y must be"], ... [" a numeric, logical, char, string, or cell array."])); endif ## Check if Y contains correct classes if (! all (ismember (unique (Y), this.ClassNames))) error (strcat (["ClassificationDiscriminant.margin: Y must"], ... [" contain only the classes in ClassNames."])); endif ## Number of Observations n = size (X, 1); ## Initialize the margin vector m = zeros (n, 1); ## Calculate the classification scores [~, scores] = predict (this, X); ## Loop over each observation to compute the margin for i = 1:n ## True class index true_class_idx = find (ismember (this.ClassNames, Y{i})); ## Score for the true class true_class_score = scores(i, true_class_idx); ## Get the maximal score for the false classes scores(i, true_class_idx) = -Inf; # Temporarily max_false_class_score = max (scores(i, :)); if (max_false_class_score == -Inf) m = NaN; return; endif scores(i, true_class_idx) = true_class_score; # Restore ## Calculate the margin m(i) = true_class_score - max_false_class_score; endfor endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationDiscriminant} {@var{CVMdl} =} crossval (@var{obj}) ## @deftypefnx {ClassificationDiscriminant} {@var{CVMdl} =} crossval (@dots{}, @var{Name}, @var{Value}) ## ## Cross Validate a Discriminant classification object. ## ## @code{@var{CVMdl} = crossval (@var{obj})} returns a cross-validated model ## object, @var{CVMdl}, from a trained model, @var{obj}, using 10-fold ## cross-validation by default. ## ## @code{@var{CVMdl} = crossval (@var{obj}, @var{name}, @var{value})} ## specifies additional name-value pair arguments to customize the ## cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"KFold"} @tab @tab Specify the number of folds to use in ## k-fold cross-validation. @code{"KFold", @var{k}}, where @var{k} is an ## integer greater than 1. ## ## @item @qcode{"Holdout"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{"Holdout", @var{p}}, where @var{p} is a ## scalar in the range @math{(0,1)}. ## ## @item @qcode{"Leaveout"} @tab @tab Specify whether to perform ## leave-one-out cross-validation. @code{"Leaveout", @var{Value}}, where ## @var{Value} is 'on' or 'off'. ## ## @item @qcode{"CVPartition"} @tab @tab Specify a @qcode{cvpartition} ## object used for cross-validation. @code{"CVPartition", @var{cv}}, where ## @code{isa (@var{cv}, "cvpartition")} = 1. ## ## @end multitable ## ## @seealso{fitcdiscr, ClassificationDiscriminant, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function CVMdl = crossval (this, varargin) ## Check input if (nargin < 1) error ("ClassificationDiscriminant.crossval: too few input arguments."); endif if (numel (varargin) == 1) error (strcat (["ClassificationDiscriminant.crossval: Name-Value"], ... [" arguments must be in pairs."])); elseif (numel (varargin) > 2) error (strcat (["ClassificationDiscriminant.crossval: specify only"], ... [" one of the optional Name-Value paired arguments."])); endif ## Add default values numFolds = 10; Holdout = []; Leaveout = 'off'; CVPartition = []; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case 'kfold' numFolds = varargin{2}; if (! (isnumeric (numFolds) && isscalar (numFolds) && (numFolds == fix (numFolds)) && numFolds > 1)) error (strcat (["ClassificationDiscriminant.crossval: 'KFold'"], ... [" must be an integer value greater than 1."])); endif case 'holdout' Holdout = varargin{2}; if (! (isnumeric (Holdout) && isscalar (Holdout) && Holdout > 0 && Holdout < 1)) error (strcat (["ClassificationDiscriminant.crossval: 'Holdout'"], ... [" must be a numeric value between 0 and 1."])); endif case 'leaveout' Leaveout = varargin{2}; if (! (ischar (Leaveout) && (strcmpi (Leaveout, 'on') || strcmpi (Leaveout, 'off')))) error (strcat (["ClassificationDiscriminant.crossval:"], ... [" 'Leaveout' must be either 'on' or 'off'."])); endif case 'cvpartition' CVPartition = varargin{2}; if (!(isa (CVPartition, 'cvpartition'))) error (strcat (["ClassificationDiscriminant.crossval:"],... [" 'CVPartition' must be a 'cvpartition' object."])); endif otherwise error (strcat (["ClassificationDiscriminant.crossval: invalid"],... [" parameter name in optional paired arguments."])); endswitch varargin (1:2) = []; endwhile ## Determine the cross-validation method to use if (! isempty (CVPartition)) partition = CVPartition; elseif (! isempty (Holdout)) partition = cvpartition (this.Y, 'Holdout', Holdout); elseif (strcmpi (Leaveout, 'on')) partition = cvpartition (this.Y, 'LeaveOut'); else partition = cvpartition (this.Y, 'KFold', numFolds); endif ## Create a cross-validated model object CVMdl = ClassificationPartitionedModel (this, partition); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationDiscriminant} {@var{CVMdl} =} compact (@var{obj}) ## ## Create a CompactClassificationDiscriminant object. ## ## @code{@var{CVMdl} = compact (@var{obj})} creates a compact version of the ## ClassificationDiscriminant object, @var{obj}. ## ## @seealso{fitcdiscr, ClassificationDiscriminant, ## CompactClassificationDiscriminant} ## @end deftypefn function CVMdl = compact (this) ## Greate a compact model CVMdl = CompactClassificationDiscriminant (this); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationDiscriminant} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationDiscriminant object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ## ClassificationDiscriminant object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcdiscr, ClassificationDiscriminant} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "ClassificationDiscriminant"; ## Create variables from model properties X = this.X; Y = this.Y; NumObservations = this.NumObservations; RowsUsed = this.RowsUsed; NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; Sigma = this.Sigma; Mu = this.Mu; Coeffs = this.Coeffs; Delta = this.Delta; DiscrimType = this.DiscrimType; Gamma = this.Gamma; MinGamma = this.MinGamma; LogDetSigma = this.LogDetSigma; XCentered = this.XCentered; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "X", "Y", "NumObservations", "RowsUsed", ... "NumPredictors", "PredictorNames", "ResponseName", "ClassNames", ... "ScoreTransform", "Prior", "Cost", "Sigma", "Mu", "Coeffs", ... "Delta", "DiscrimType", "Gamma", "MinGamma", "LogDetSigma", ... "XCentered"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationDiscriminant object mdl = ClassificationDiscriminant (1, 1); ## Check that fieldnames in DATA match properties in ClassificationDiscriminant names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) msg = "ClassificationDiscriminant.load_model: invalid model in '%s'."; error (msg, filename); endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction endmethods endclassdef %!demo %! ## Create discriminant classifier %! ## Evaluate some model predictions on new data. %! %! load fisheriris %! x = meas; %! y = species; %! xc = [min(x); mean(x); max(x)]; %! obj = fitcdiscr (x, y); %! [label, score, cost] = predict (obj, xc); %!demo %! load fisheriris %! model = fitcdiscr (meas, species); %! X = mean (meas); %! Y = {'versicolor'}; %! ## Compute loss for discriminant model %! L = loss (model, X, Y) %!demo %! load fisheriris %! mdl = fitcdiscr (meas, species); %! X = mean (meas); %! Y = {'versicolor'}; %! ## Margin for discriminant model %! m = margin (mdl, X, Y) %!demo %! load fisheriris %! x = meas; %! y = species; %! obj = fitcdiscr (x, y, "gamma", 0.4); %! ## Cross-validation for discriminant model %! CVMdl = crossval (obj) ## Test constructor %!test %! load fisheriris %! x = meas; %! y = species; %! PredictorNames = {'Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width'}; %! Mdl = ClassificationDiscriminant (x, y, "PredictorNames", PredictorNames); %! sigma = [0.265008, 0.092721, 0.167514, 0.038401; ... %! 0.092721, 0.115388, 0.055244, 0.032710; ... %! 0.167514, 0.055244, 0.185188, 0.042665; ... %! 0.038401, 0.032710, 0.042665, 0.041882]; %! mu = [5.0060, 3.4280, 1.4620, 0.2460; ... %! 5.9360, 2.7700, 4.2600, 1.3260; ... %! 6.5880, 2.9740, 5.5520, 2.0260]; %! xCentered = [ 9.4000e-02, 7.2000e-02, -6.2000e-02, -4.6000e-02; ... %! -1.0600e-01, -4.2800e-01, -6.2000e-02, -4.6000e-02; ... %! -3.0600e-01, -2.2800e-01, -1.6200e-01, -4.6000e-02]; %! assert (class (Mdl), "ClassificationDiscriminant"); %! assert ({Mdl.X, Mdl.Y, Mdl.NumObservations}, {x, y, 150}) %! assert ({Mdl.DiscrimType, Mdl.ResponseName}, {"linear", "Y"}) %! assert ({Mdl.Gamma, Mdl.MinGamma}, {0, 0}, 1e-15) %! assert (Mdl.ClassNames, unique (species)) %! assert (Mdl.Sigma, sigma, 1e-6) %! assert (Mdl.Mu, mu, 1e-14) %! assert (Mdl.XCentered([1:3],:), xCentered, 1e-14) %! assert (Mdl.LogDetSigma, -9.9585, 1e-4) %! assert (Mdl.PredictorNames, PredictorNames) %!test %! load fisheriris %! x = meas; %! y = species; %! Mdl = ClassificationDiscriminant (x, y, "Gamma", 0.5); %! sigma = [0.265008, 0.046361, 0.083757, 0.019201; ... %! 0.046361, 0.115388, 0.027622, 0.016355; ... %! 0.083757, 0.027622, 0.185188, 0.021333; ... %! 0.019201, 0.016355, 0.021333, 0.041882]; %! mu = [5.0060, 3.4280, 1.4620, 0.2460; ... %! 5.9360, 2.7700, 4.2600, 1.3260; ... %! 6.5880, 2.9740, 5.5520, 2.0260]; %! xCentered = [ 9.4000e-02, 7.2000e-02, -6.2000e-02, -4.6000e-02; ... %! -1.0600e-01, -4.2800e-01, -6.2000e-02, -4.6000e-02; ... %! -3.0600e-01, -2.2800e-01, -1.6200e-01, -4.6000e-02]; %! assert (class (Mdl), "ClassificationDiscriminant"); %! assert ({Mdl.X, Mdl.Y, Mdl.NumObservations}, {x, y, 150}) %! assert ({Mdl.DiscrimType, Mdl.ResponseName}, {"linear", "Y"}) %! assert ({Mdl.Gamma, Mdl.MinGamma}, {0.5, 0}) %! assert (Mdl.ClassNames, unique (species)) %! assert (Mdl.Sigma, sigma, 1e-6) %! assert (Mdl.Mu, mu, 1e-14) %! assert (Mdl.XCentered([1:3],:), xCentered, 1e-14) %! assert (Mdl.LogDetSigma, -8.6884, 1e-4) ## Test input validation for constructor %!shared X, Y, MODEL %! X = rand (10,2); %! Y = [ones(5,1);2*ones(5,1)]; %! MODEL = ClassificationDiscriminant (X, Y); %!error ClassificationDiscriminant () %!error ... %! ClassificationDiscriminant (ones(4, 1)) %!error ... %! ClassificationDiscriminant (ones (4,2), ones (1,4)) %!error ... %! ClassificationDiscriminant (X, Y, "PredictorNames", ["A"]) %!error ... %! ClassificationDiscriminant (X, Y, "PredictorNames", "A") %!error ... %! ClassificationDiscriminant (X, Y, "PredictorNames", {"A", "B", "C"}) %!error ... %! ClassificationDiscriminant (X, Y, "ResponseName", {"Y"}) %!error ... %! ClassificationDiscriminant (X, Y, "ResponseName", 1) %!error ... %! ClassificationDiscriminant (X, Y, "ClassNames", @(x)x) %!error ... %! ClassificationDiscriminant (X, Y, "ClassNames", ['a']) %!error ... %! ClassificationDiscriminant (X, ones (10,1), "ClassNames", [1, 2]) %!error ... %! ClassificationDiscriminant ([1;2;3;4;5], {'a';'b';'a';'a';'b'}, "ClassNames", {'a','c'}) %!error ... %! ClassificationDiscriminant (X, logical (ones (10,1)), "ClassNames", [true, false]) %!error ... %! ClassificationDiscriminant (X, Y, "Prior", {"1", "2"}) %!error ... %! ClassificationDiscriminant (X, ones (10,1), "Prior", [1 2]) %!error ... %! ClassificationDiscriminant (X, Y, "Cost", [1, 2]) %!error ... %! ClassificationDiscriminant (X, Y, "Cost", "string") %!error ... %! ClassificationDiscriminant (X, Y, "Cost", {eye(2)}) %!error ... %! ClassificationDiscriminant (X, Y, "Cost", ones (3)) %!error ... %! ClassificationDiscriminant (ones (5,2), [1; 1; 2; 2; 2]) %!error ... %! ClassificationDiscriminant (ones (5,2), [1; 1; 2; 2; 2], "PredictorNames", {"A", "B"}) %!error ... %! ClassificationDiscriminant ([1,2;2,2;3,2;4,2;5,2], ones (5, 1)) %!error ... %! ClassificationDiscriminant ([1,2;2,2;3,2;4,2;5,2], ones (5, 1), "PredictorNames", {"A", "B"}) ## Test predict method %!test %! load fisheriris %! x = meas; %! y = species; %! Mdl = fitcdiscr (meas, species, "Gamma", 0.5); %! [label, score, cost] = predict (Mdl, [2, 2, 2, 2]); %! assert (label, {'versicolor'}) %! assert (score, [0, 0.9999, 0.0001], 1e-4) %! assert (cost, [1, 0.0001, 0.9999], 1e-4) %! [label, score, cost] = predict (Mdl, [2.5, 2.5, 2.5, 2.5]); %! assert (label, {'versicolor'}) %! assert (score, [0, 0.6368, 0.3632], 1e-4) %! assert (cost, [1, 0.3632, 0.6368], 1e-4) %!test %! load fisheriris %! x = meas; %! y = species; %! xc = [min(x); mean(x); max(x)]; %! Mdl = fitcdiscr (x, y); %! [label, score, cost] = predict (Mdl, xc); %! l = {'setosa'; 'versicolor'; 'virginica'}; %! s = [1, 0, 0; 0, 1, 0; 0, 0, 1]; %! c = [0, 1, 1; 1, 0, 1; 1, 1, 0]; %! assert (label, l) %! assert (score, s, 1e-4) %! assert (cost, c, 1e-4) ## Test input validation for predict method %!error ... %! predict (MODEL) %!error ... %! predict (MODEL, []) %!error ... %! predict (MODEL, 1) ## Test loss method %!test %! load fisheriris %! model = fitcdiscr (meas, species); %! x = mean (meas); %! y = {'versicolor'}; %! L = loss (model, x, y); %! assert (L, 0) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y, "Gamma", 0.4); %! x_test = [1, 6; 3, 3]; %! y_test = {'A'; 'B'}; %! L = loss (model, x_test, y_test); %! assert (L, 0.3333, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3]; %! y_test = ['1']; %! L = loss (model, x_test, y_test, 'LossFun', 'quadratic'); %! assert (L, 0.2423, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3; 5, 7]; %! y_test = ['1'; '2']; %! L = loss (model, x_test, y_test, 'LossFun', 'classifcost'); %! assert (L, 0.3333, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3; 5, 7]; %! y_test = ['1'; '2']; %! L = loss (model, x_test, y_test, 'LossFun', 'hinge'); %! assert (L, 0.5886, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3; 5, 7]; %! y_test = ['1'; '2']; %! W = [1; 2]; %! L = loss (model, x_test, y_test, 'LossFun', 'logit', 'Weights', W); %! assert (L, 0.5107, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_with_nan = [1, 2; NaN, 4]; %! y_test = {'A'; 'B'}; %! L = loss (model, x_with_nan, y_test); %! assert (L, 0.3333, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y); %! x_with_nan = [1, 2; NaN, 4]; %! y_test = {'A'; 'B'}; %! L = loss (model, x_with_nan, y_test, 'LossFun', 'logit'); %! assert (isnan (L)) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y); %! customLossFun = @(C, S, W, Cost) sum (W .* sum (abs (C - S), 2)); %! L = loss (model, x, y, 'LossFun', customLossFun); %! assert (L, 0.8889, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = [1; 2; 1]; %! model = fitcdiscr (x, y); %! L = loss (model, x, y, 'LossFun', 'classiferror'); %! assert (L, 0.3333, 1e-4) ## Test input validation for loss method %!error ... %! loss (MODEL) %!error ... %! loss (MODEL, ones (4,2)) %!error ... %! loss (MODEL, [], zeros (2)) %!error ... %! loss (MODEL, 1, zeros (2)) %!error ... %! loss (MODEL, ones (4,2), ones (4,1), 'LossFun') %!error ... %! loss (MODEL, ones (4,2), ones (3,1)) %!error ... %! loss (MODEL, ones (4,2), ones (4,1), 'LossFun', 'a') %!error ... %! loss (MODEL, ones (4,2), ones (4,1), 'Weights', 'w') ## Test margin method %! load fisheriris %! mdl = fitcdiscr (meas, species); %! X = mean (meas); %! Y = {'versicolor'}; %! m = margin (mdl, X, Y); %! assert (m, 1, 1e-6) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = [1; 2; 1]; %! mdl = fitcdiscr (X, Y, "gamma", 0.5); %! m = margin (mdl, X, Y); %! assert (m, [0.3333; -0.3333; 0.3333], 1e-4) ## Test input validation for margin method %!error ... %! margin (MODEL) %!error ... %! margin (MODEL, ones (4,2)) %!error ... %! margin (MODEL, [], zeros (2)) %!error ... %! margin (MODEL, 1, zeros (2)) %!error ... %! margin (MODEL, ones (4,2), ones (3,1)) ## Test crossval method %!shared x, y, obj %! load fisheriris %! x = meas; %! y = species; %! obj = fitcdiscr (x, y, "gamma", 0.4); %!test %! CVMdl = crossval (obj); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 10) %! assert (class (CVMdl.Trained{1}), "CompactClassificationDiscriminant") %! assert (CVMdl.CrossValidatedModel, "ClassificationDiscriminant") %!test %! CVMdl = crossval (obj, "KFold", 3); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 3) %! assert (class (CVMdl.Trained{1}), "CompactClassificationDiscriminant") %! assert (CVMdl.CrossValidatedModel, "ClassificationDiscriminant") %!test %! CVMdl = crossval (obj, "HoldOut", 0.2); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationDiscriminant") %! assert (CVMdl.CrossValidatedModel, "ClassificationDiscriminant") %!test %! CVMdl = crossval (obj, "LeaveOut", 'on'); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationDiscriminant") %! assert (CVMdl.CrossValidatedModel, "ClassificationDiscriminant") %!test %! partition = cvpartition (y, 'KFold', 3); %! CVMdl = crossval (obj, 'cvPartition', partition); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert (CVMdl.KFold == 3) %! assert (class (CVMdl.Trained{1}), "CompactClassificationDiscriminant") %! assert (CVMdl.CrossValidatedModel, "ClassificationDiscriminant") ## Test input validation for crossval method %!error ... %! crossval (obj, "kfold") %!error... %! crossval (obj, "kfold", 12, "holdout", 0.2) %!error ... %! crossval (obj, "kfold", 'a') %!error ... %! crossval (obj, "holdout", 2) %!error ... %! crossval (obj, "leaveout", 1) %!error ... %! crossval (obj, "cvpartition", 1) statistics-release-1.7.3/inst/Classification/ClassificationGAM.m000066400000000000000000001403421475240274700247200ustar00rootroot00000000000000## Copyright (C) 2024 Ruchika Sonagote ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClassificationGAM ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} ClassificationGAM (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} ClassificationGAM (@dots{}, @var{name}, @var{value}) ## ## Create a @qcode{ClassificationGAM} class object containing a generalized ## additive classification model. ## ## The @qcode{ClassificationGAM} class implements a gradient boosting algorithm ## for classification, using spline fitting as the weak learner. This approach ## allows the model to capture non-linear relationships between predictors and ## the binary response variable. ## ## @code{@var{obj} = ClassificationGAM (@var{X}, @var{Y})} returns a ## ClassificationGAM object, with @var{X} as the predictor data and @var{Y} ## containing the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of predictor data where rows ## correspond to observations and columns correspond to features or variables. ## @item ## @code{Y} is @math{Nx1} numeric vector containing binary class labels, ## typically 0 or 1. ## @end itemize ## ## @code{@var{obj} = ClassificationGAM (@dots{}, @var{name}, @var{value})} ## returns a ClassificationGAM object with parameters specified by ## @qcode{Name-Value} pair arguments. Type @code{help fitcgam} for more info. ## ## A @qcode{ClassificationGAM} object, @var{obj}, stores the labeled training ## data and various parameters for the Generalized Additive Model (GAM) for ## classification, which can be accessed in the following fields: ## ## @multitable @columnfractions 0.23 0.02 0.75 ## @headitem @var{Field} @tab @tab @var{Description} ## ## @item @qcode{X} @tab @tab Predictor data, specified as a ## numeric matrix. Each column of @var{X} represents one predictor (variable), ## and each row represents one observation. ## ## @item @qcode{Y} @tab @tab Class labels, specified as numeric vector ## of 0's and 1's. Each value in @var{Y} is the observed class ## label for the corresponding row in @var{X}. ## ## @item @qcode{BaseModel} @tab @tab A structure containing the parameters ## of the base model without any interaction terms. The base model represents ## the generalized additive model (GAM) with only the main effects (predictor ## terms) included. ## ## @item @qcode{ModelwInt} @tab @tab A structure containing the parameters ## of the model that includes interaction terms. This model extends the base ## model by adding interaction terms between predictors, as specified by the ## @qcode{Interactions} property. ## ## @item @qcode{IntMatrix} @tab @tab A logical matrix or a matrix of ## column indices that describes the interaction terms applied to the predictor ## data. ## ## @item @qcode{NumObservations} @tab @tab Number of observations used in ## training the ClassificationGAM model, specified as a positive integer scalar. ## This number can be less than the number of rows in the training data because ## rows containing @qcode{NaN} values are not part of the fit. ## ## @item @qcode{RowsUsed} @tab @tab Rows of the original training data ## used in fitting the ClassificationGAM model, specified as a numerical vector. ## If you want to use this vector for indexing the training data in @var{X}, you ## have to convert it to a logical vector, i.e ## @qcode{X = obj.X(logical (obj.RowsUsed), :);} ## ## @item @qcode{NumPredictors} @tab @tab The number of predictors ## (variables) in @var{X}. ## ## @item @qcode{PredictorNames} @tab @tab Predictor variable names, ## specified as a cell array of character vectors. The variable names are in ## the same order in which they appear in the training data @var{X}. ## ## @item @qcode{ResponseName} @tab @tab Response variable name, specified ## as a character vector. ## ## @item @qcode{ClassNames} @tab @tab Names of the classes in the training ## data @var{Y} with duplicates removed, specified as a cell array of character ## vectors. ## ## @item @qcode{Cost} @tab @tab Cost of the misclassification of a point, ## specified as a square matrix. @qcode{Cost(i,j)} is the cost of classifying a ## point into class @qcode{j} if its true class is @qcode{i} (that is, the rows ## correspond to the true class and the columns correspond to the predicted ## class). The order of the rows and columns in @qcode{Cost} corresponds to the ## order of the classes in @qcode{ClassNames}. The number of rows and columns ## in @qcode{Cost} is the number of unique classes in the response. By default, ## @qcode{Cost(i,j) = 1} if @qcode{i != j}, and @qcode{Cost(i,j) = 0} if ## @qcode{i = j}. In other words, the cost is 0 for correct classification and ## 1 for incorrect classification. ## ## @item @qcode{Formula} @tab @tab A model specification given as a string ## in the form @qcode{"Y ~ terms"} where @qcode{Y} represents the reponse ## variable and @qcode{terms} the predictor variables. The formula can be used ## to specify a subset of variables for training model. For example: ## @qcode{"Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3"} specifies four linear terms ## for the first four columns of for predictor data, and @qcode{x1:x2} and ## @qcode{x2:x3} specify the two interaction terms for 1st-2nd and 3rd-4th ## columns respectively. Only these terms will be used for training the model, ## but @var{X} must have at least as many columns as referenced in the formula. ## If Predictor Variable names have been defined, then the terms in the formula ## must reference to those. When @qcode{"formula"} is specified, all terms used ## for training the model are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object as a matrix containing the column indexes for each ## term including both the predictors and the interactions used. ## ## @item @qcode{Interactions} @tab @tab A logical matrix, a positive integer ## scalar, or the string @qcode{"all"} for defining the interactions between ## predictor variables. When given a logical matrix, it must have the same ## number of columns as @var{X} and each row corresponds to a different ## interaction term combining the predictors indexed as @qcode{true}. Each ## interaction term is appended as a column vector after the available predictor ## column in @var{X}. When @qcode{"all"} is defined, then all possible ## combinations of interactions are appended in @var{X} before training. At the ## moment, parsing a positive integer has the same effect as the @qcode{"all"} ## option. When @qcode{"interactions"} is specified, only the interaction terms ## appended to @var{X} are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object. ## ## @item @qcode{Knots} @tab @tab A scalar or a row vector with the same ## columns as @var{X}. It defines the knots for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 5, hence expanded to @qcode{ones (1, columns (X)) * 5}. You can ## parse a row vector with different number of knots for each predictor ## variable to be fitted with, although not recommended. ## ## @item @qcode{Order} @tab @tab A scalar or a row vector with the same ## columns as @var{X}. It defines the order of the polynomial when training the ## GAM. As a scalar, it is expanded to a row vector. The default values is 3, ## hence expanded to @qcode{ones (1, columns (X)) * 3}. You can parse a row ## vector with different number of polynomial order for each predictor variable ## to be fitted with, although not recommended. ## ## @item @qcode{DoF} @tab @tab A scalar or a row vector with the same ## columns as @var{X}. It defines the degrees of freedom for fitting a ## polynomial when training the GAM. As a scalar, it is expanded to a row ## vector. The default value is 8, hence expanded to ## @qcode{ones (1, columns (X)) * 8}. You can parse a row vector with different ## degrees of freedom for each predictor variable to be fitted with, ## although not recommended. ## ## @end multitable ## ## You can parse either a @qcode{"Formula"} or an @qcode{"Interactions"} ## optional parameter. Parsing both parameters will result an error. ## Accordingly, you can only pass up to two parameters among @qcode{"Knots"}, ## @qcode{"Order"}, and @qcode{"DoF"} to define the required polynomial for ## training the GAM model. ## ## @seealso{fitcgam, CompactClassificationGAM, ClassificationPartitionedModel} ## @end deftypefn properties (Access = public) X = []; # Predictor data Y = []; # Class labels NumObservations = []; # Number of observations in training dataset RowsUsed = []; # Rows used in fitting NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variable names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of Misclassification ScoreTransform = []; # Transformation for classification scores Formula = []; # Formula for GAM model Interactions = []; # Number or matrix of interaction terms Knots = []; # Knots of spline fitting Order = []; # Order of spline fitting DoF = []; # Degrees of freedom for fitting spline LearningRate = []; # Learning rate for training NumIterations = []; # Max number of iterations for training BaseModel = []; # Base model parameters (no interactions) ModelwInt = []; # Model parameters with interactions IntMatrix = []; # Interactions matrix applied to predictor data endproperties methods (Access = public) ## Class object constructor function this = ClassificationGAM (X, Y, varargin) ## Check for sufficient number of input arguments if (nargin < 2) error ("ClassificationGAM: too few input arguments."); endif ## Check X and Y have the same number of observations if (rows (X) != rows (Y)) error ("ClassificationGAM: number of rows in X and Y must be equal."); endif nsample = rows (X); ndims_X = columns (X); ## Assign original X and Y data this.X = X; this.Y = Y; ## Get groups in Y [gY, gnY, glY] = grp2idx (Y); ## Set default values before parsing optional parameters PredictorNames = {}; ResponseName = []; Formula = []; Interactions = []; ClassNames = []; DoF = ones (1, ndims_X) * 8; Order = ones (1, ndims_X) * 3; Knots = ones (1, ndims_X) * 5; LearningRate = 0.1; NumIterations = 100; Cost = []; this.ScoreTransform = 'none'; ## Number of parameters for Knots, DoF, Order (maximum 2 allowed) KOD = 0; ## Number of parameters for Formula, Ineractions (maximum 1 allowed) F_I = 0; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "predictornames" PredictorNames = varargin{2}; if (! iscellstr (PredictorNames)) error (strcat (["ClassificationGAM: 'PredictorNames'"], ... [" must be supplied as a cellstring array."])); elseif (numel (PredictorNames) != columns (X)) error (strcat (["ClassificationGAM: 'PredictorNames'"], ... [" must equal the number of columns in X."])); endif case "responsename" ResponseName = varargin{2}; if (! ischar (ResponseName)) error (strcat (["ClassificationGAM: 'ResponseName'"], ... [" must be a character vector."])); endif case "classnames" ClassNames = varargin{2}; if (! (iscellstr (ClassNames) || isnumeric (ClassNames) || islogical (ClassNames))) error (strcat (["ClassificationGAM: 'ClassNames' must be a"], ... [" cellstring, logical or numeric vector."])); endif ## Check that all class names are available in gnY msg = "ClassificationGAM: not all 'ClassNames' are present in Y."; if (iscellstr (ClassNames)) if (! all (cell2mat (cellfun (@(x) any (strcmp (x, gnY)), ClassNames, "UniformOutput", false)))) error (msg); endif else if (! all (cell2mat (arrayfun (@(x) any (x == glY), ClassNames, "UniformOutput", false)))) error (msg); endif endif case "cost" Cost = varargin{2}; if (! (isnumeric (Cost) && issquare (Cost))) error (strcat (["ClassificationGAM: 'Cost' must be"], ... [" a numeric square matrix."])); endif case "scoretransform" name = "ClassificationGAM"; this.ScoreTransform = parseScoreTransform (varargin{2}, name); case "formula" if (F_I < 1) Formula = varargin{2}; if (! ischar (Formula) && ! islogical (Formula)) error ("ClassificationGAM: 'Formula' must be a string."); endif F_I += 1; else error (strcat (["ClassificationGAM: 'Interactions'"], ... [" have already been defined."]))'; endif case "interactions" if (F_I < 1) tmp = varargin{2}; if (isnumeric (tmp) && isscalar (tmp) && tmp == fix (tmp) && tmp >= 0) Interactions = tmp; elseif (islogical (tmp)) Interactions = tmp; elseif (ischar (tmp) && strcmpi (tmp, "all")) Interactions = tmp; else error ("ClassificationGAM: invalid 'Interactions' parameter."); endif F_I += 1; else error ("ClassificationGAM: 'Formula' has already been defined."); endif case "knots" if (KOD < 2) Knots = varargin{2}; if (! isnumeric (Knots) || ! (isscalar (Knots) || isequal (size (Knots), [1, ndims_X]))) error ("ClassificationGAM: invalid value for 'Knots'."); endif DoF = Knots + Order; Order = DoF - Knots; KOD += 1; else error (strcat (["ClassificationGAM: 'DoF' and 'Order'"], ... [" have been set already."])); endif case "order" if (KOD < 2) Order = varargin{2}; if (! isnumeric (Order) || ! (isscalar (Order) || isequal (size (Order), [1, ndims_X]))) error ("ClassificationGAM: invalid value for 'Order'."); endif DoF = Knots + Order; Knots = DoF - Order; KOD += 1; else error (strcat (["ClassificationGAM: 'DoF' and 'Knots'"], ... [" have been set already."])); endif case "dof" if (KOD < 2) DoF = varargin{2}; if (! isnumeric (DoF) || ! (isscalar (DoF) || isequal (size (DoF), [1, ndims_X]))) error ("ClassificationGAM: invalid value for 'DoF'."); endif Knots = DoF - Order; Order = DoF - Knots; KOD += 1; else error (strcat (["ClassificationGAM: 'Knots' and 'Order'"], ... [" have been set already."])); endif case "learningrate" LearningRate = varargin{2}; if (LearningRate > 1 || LearningRate <= 0) error (strcat (["ClassificationGAM: 'LearningRate'"], ... [" must be between 0 and 1."])); endif case "numiterations" NumIterations = varargin{2}; if (! isnumeric (NumIterations) || NumIterations <= 0) error (strcat (["ClassificationGAM: 'NumIterations'"], ... [" must be a positive integer value."])); endif otherwise error (strcat (["ClassificationGAM: invalid parameter"], ... [" name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Generate default predictors and response variabe names (if necessary) if (isempty (PredictorNames)) for i = 1:columns (X) PredictorNames {i} = strcat ("x", num2str (i)); endfor endif if (isempty (ResponseName)) ResponseName = "Y"; endif ## Assign predictors and response variable names this.PredictorNames = PredictorNames; this.ResponseName = ResponseName; ## Handle class names if (! isempty (ClassNames)) if (iscellstr (ClassNames)) ru = find (! ismember (gnY, ClassNames)); else ru = find (! ismember (glY, ClassNames)); endif for i = 1:numel (ru) gY(gY == ru(i)) = NaN; endfor endif ## Remove nans from X and Y RowsUsed = ! logical (sum (isnan ([X, gY]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [gY, gnY, glY] = grp2idx (Y); this.ClassNames = gnY; ## Check that we are dealing only with binary classification if (numel (gnY) > 2) error ("ClassificationGAM: can only be used for binary classification."); endif ## Force Y into numeric if (! isnumeric (Y)) Y = gY - 1; endif this.NumObservations = rows (X); this.RowsUsed = cast (RowsUsed, "double"); ## Assign the number of original predictors to the ClassificationGAM object this.NumPredictors = ndims_X; if (isempty (Cost)) this.Cost = cast (! eye (numel (gnY)), "double"); else if (numel (gnY) != sqrt (numel (Cost))) error (strcat (["ClassificationGAM: the number of rows"], ... [" and columns in 'Cost' must correspond"], ... [" to selected classes in Y."])); endif this.Cost = Cost; endif ## Assign remaining optional parameters this.Formula = Formula; this.Interactions = Interactions; this.Knots = Knots; this.Order = Order; this.DoF = DoF; this.LearningRate = LearningRate; this.NumIterations = NumIterations; ## Fit the basic model Inter = mean (Y); [iter, param, res, RSS, intercept] = this.fitGAM (X, Y, Inter, Knots, ... Order, LearningRate, ... NumIterations); this.BaseModel.Intercept = intercept; this.BaseModel.Parameters = param; this.BaseModel.Iterations = iter; this.BaseModel.Residuals = res; this.BaseModel.RSS = RSS; ## Handle interaction terms (if given) if (F_I > 0) if (isempty (this.Formula)) ## Analyze Interactions optional parameter this.IntMatrix = this.parseInteractions (); ## Append interaction terms to the predictor matrix for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = X(:,tindex); Xinter = ones (this.NumObservations, 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append interaction terms X = [X, Xinter]; endfor else ## Analyze Formula optional parameter this.IntMatrix = this.parseFormula (); ## Add selected predictors and interaction terms XN = []; for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = X(:,tindex); Xinter = ones (this.NumObservations, 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append selected predictors and interaction terms XN = [XN, Xinter]; endfor X = XN; endif ## Update length of Knots, Order, and DoF vectors to match ## the columns of X with the interaction terms Knots = ones (1, columns (X)) * Knots(1); # Knots Order = ones (1, columns (X)) * Order(1); # Order of spline DoF = ones (1, columns (X)) * DoF(1); # Degrees of freedom ## Fit the model with interactions [iter, param, res, RSS, intercept] = this.fitGAM (X, Y, Inter, Knots, ... Order, LearningRate, ... NumIterations); this.ModelwInt.Intercept = intercept; this.ModelwInt.Parameters = param; this.ModelwInt.Iterations = iter; this.ModelwInt.Residuals = res; this.ModelwInt.RSS = RSS; endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationGAM} {@var{label} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {ClassificationGAM} {@var{label} =} predict (@dots{}, @qcode{'IncludeInteractions'}, @var{includeInteractions}) ## @deftypefnx {ClassificationGAM} {[@var{label}, @var{score}] =} predict (@dots{}) ## ## Predict labels for new data using the Generalized Additive Model (GAM) ## stored in a ClassificationGAM object. ## ## @code{@var{label} = predict (@var{obj}, @var{XC})} returns the predicted ## labels for the data in @var{X} based on the model stored in the ## ClassificationGAM object, @var{obj}. ## ## @code{@var{label} = predict (@var{obj}, @var{XC}, 'IncludeInteractions', ## @var{includeInteractions})} allows you to specify whether interaction ## terms should be included when making predictions. ## ## @code{[@var{label}, @var{score}] = predict (@dots{})} also returns ## @var{score}, which contains the predicted class scores or posterior ## probabilities for each observation. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationGAM} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix where each row is an ## observation and each column corresponds to a predictor variable. ## @item ## @var{includeInteractions} is a 'true' or 'false' indicating whether to ## include interaction terms in the predictions. ## @end itemize ## ## @seealso{fitcgam, ClassificationGAM} ## @end deftypefn function [labels, scores] = predict (this, XC, varargin) ## Check for sufficient input arguments if (nargin < 2) error ("ClassificationGAM.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("ClassificationGAM.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["ClassificationGAM.predict:"], ... [" XC must have the same number of"], ... [" predictors as the trained model."])); endif ## Clean XC data notnansf = ! logical (sum (isnan (XC), 2)); XC = XC (notnansf, :); ## Default values for Name-Value Pairs incInt = ! isempty (this.IntMatrix); Cost = this.Cost; ## Parse optional arguments while (numel (varargin) > 0) switch (tolower (varargin {1})) case "includeinteractions" tmpInt = varargin{2}; if (! islogical (tmpInt) || (tmpInt != 0 && tmpInt != 1)) error (strcat (["ClassificatioGAM.predict:"], ... [" includeinteractions must be a logical value."])); endif ## Check model for interactions if (tmpInt && isempty (this.IntMatrix)) error (strcat (["ClassificatioGAM.predict: trained model"], ... [" does not include any interactions."])); endif incInt = tmpInt; otherwise error (strcat (["ClassificationGAM.predict: invalid NAME in"], ... [" optional pairs of arguments."])); endswitch varargin (1:2) = []; endwhile ## Choose whether interactions must be included if (incInt) if (! isempty (this.Interactions)) ## Append interaction terms to the predictor matrix for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = XC(:,tindex); Xinter = ones (rows (XC), 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append interaction terms XC = [XC, Xinter]; endfor else ## Add selected predictors and interaction terms XN = []; for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = XC(:,tindex); Xinter = ones (rows (XC), 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append selected predictors and interaction terms XN = [XN, Xinter]; endfor XC = XN; endif ## Get parameters and intercept vectors from model with interactions params = this.ModelwInt.Parameters; Interc = this.ModelwInt.Intercept; else ## Get parameters and intercept vectors from base model params = this.BaseModel.Parameters; Interc = this.BaseModel.Intercept; endif ## Predict probabilities from testing data scores = predict_val (params, XC, Interc); ## Compute the expected misclassification cost matrix numObservations = size (XC, 1); CE = zeros (numObservations, 2); for k = 1:2 for i = 1:2 CE(:, k) = CE(:, k) + scores(:, i) * Cost(k, i); endfor endfor ## Select the class with the minimum expected misclassification cost [~, minIdx] = min (CE, [], 2); labels = this.ClassNames (minIdx); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationGAM} {@var{CVMdl} =} crossval (@var{obj}) ## @deftypefnx {ClassificationGAM} {@var{CVMdl} =} crossval (@dots{}, @var{Name}, @var{Value}) ## ## Cross Validate a Generalized Additive Model classification object. ## ## @code{@var{CVMdl} = crossval (@var{obj})} returns a cross-validated model ## object, @var{CVMdl}, from a trained model, @var{obj}, using 10-fold ## cross-validation by default. ## ## @code{@var{CVMdl} = crossval (@var{obj}, @var{name}, @var{value})} ## specifies additional name-value pair arguments to customize the ## cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"KFold"} @tab @tab Specify the number of folds to use in ## k-fold cross-validation. @code{"KFold", @var{k}}, where @var{k} is an ## integer greater than 1. ## ## @item @qcode{"Holdout"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{"Holdout", @var{p}}, where @var{p} is a ## scalar in the range @math{(0,1)}. ## ## @item @qcode{"Leaveout"} @tab @tab Specify whether to perform ## leave-one-out cross-validation. @code{"Leaveout", @var{Value}}, where ## @var{Value} is 'on' or 'off'. ## ## @item @qcode{"CVPartition"} @tab @tab Specify a @qcode{cvpartition} ## object used for cross-validation. @code{"CVPartition", @var{cv}}, where ## @code{isa (@var{cv}, "cvpartition")} = 1. ## ## @end multitable ## ## @seealso{fitcgam, ClassificationGAM, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function CVMdl = crossval (this, varargin) ## Check input if (nargin < 1) error ("ClassificationGAM.crossval: too few input arguments."); endif if (numel (varargin) == 1) error (strcat (["ClassificationGAM.crossval: Name-Value"], ... [" arguments must be in pairs."])); elseif (numel (varargin) > 2) error (strcat (["ClassificationGAM.crossval: specify only"], ... [" one of the optional Name-Value paired arguments."])); endif ## Add default values numFolds = 10; Holdout = []; Leaveout = 'off'; CVPartition = []; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case 'kfold' numFolds = varargin{2}; if (! (isnumeric (numFolds) && isscalar (numFolds) && (numFolds == fix (numFolds)) && numFolds > 1)) error (strcat (["ClassificationGAM.crossval: 'KFold'"], ... [" must be an integer value greater than 1."])); endif case 'holdout' Holdout = varargin{2}; if (! (isnumeric (Holdout) && isscalar (Holdout) && Holdout > 0 && Holdout < 1)) error (strcat (["ClassificationGAM.crossval: 'Holdout'"], ... [" must be a numeric value between 0 and 1."])); endif case 'leaveout' Leaveout = varargin{2}; if (! (ischar (Leaveout) && (strcmpi (Leaveout, 'on') || strcmpi (Leaveout, 'off')))) error (strcat (["ClassificationGAM.crossval: 'Leaveout'"], ... [" must be either 'on' or 'off'."])); endif case 'cvpartition' CVPartition = varargin{2}; if (!(isa (CVPartition, 'cvpartition'))) error (strcat (["ClassificationGAM.crossval: 'CVPartition'"],... [" must be a 'cvpartition' object."])); endif otherwise error (strcat (["ClassificationGAM.crossval: invalid"],... [" parameter name in optional paired arguments."])); endswitch varargin (1:2) = []; endwhile ## Determine the cross-validation method to use if (! isempty (CVPartition)) partition = CVPartition; elseif (! isempty (Holdout)) partition = cvpartition (this.Y, 'Holdout', Holdout); elseif (strcmpi (Leaveout, 'on')) partition = cvpartition (this.Y, 'LeaveOut'); else partition = cvpartition (this.Y, 'KFold', numFolds); endif ## Create a cross-validated model object CVMdl = ClassificationPartitionedModel (this, partition); endfunction endmethods ## Helper functions methods (Access = private) ## Determine interactions from Interactions optional parameter function intMat = parseInteractions (this) if (islogical (this.Interactions)) ## Check that interaction matrix corresponds to predictors if (numel (this.PredictorNames) != columns (this.Interactions)) error (strcat (["ClassificationGAM: columns in 'Interactions'"], ... [" matrix must equal to the number of predictors."])); endif intMat = this.Interactions; elseif (isnumeric (this.Interactions)) ## Need to measure the effect of all interactions to keep the best ## performing. Just check that the given number is not higher than ## p*(p-1)/2, where p is the number of predictors. p = this.NumPredictors; if (this.Interactions > p * (p - 1) / 2) error (strcat (["ClassificationGAM: number of interaction terms"], ... [" requested is larger than all possible"], ... [" combinations of predictors in X."])); endif ## Get all combinations except all zeros allMat = flip (fullfact(p)([2:end],:), 2); ## Only keep interaction terms iterms = find (sum (allMat, 2) != 1); intMat = allMat(iterms); elseif (strcmpi (this.Interactions, "all")) p = this.NumPredictors; ## Calculate all p*(p-1)/2 interaction terms allMat = flip (fullfact(p)([2:end],:), 2); ## Only keep interaction terms iterms = find (sum (allMat, 2) != 1); intMat = allMat(iterms); endif endfunction ## Determine interactions from formula function intMat = parseFormula (this) intMat = []; ## Check formula for syntax if (isempty (strfind (this.Formula, '~'))) error ("ClassificationGAM: invalid syntax in 'Formula'."); endif ## Split formula and keep predictor terms formulaParts = strsplit (this.Formula, '~'); ## Check there is some string after '~' if (numel (formulaParts) < 2) error ("ClassificationGAM: no predictor terms in 'Formula'."); endif predictorString = strtrim (formulaParts{2}); if (isempty (predictorString)) error ("ClassificationGAM: no predictor terms in 'Formula'."); endif ## Spit additive terms (between + sign) aterms = strtrim (strsplit (predictorString, '+')); ## Process all terms for i = 1:numel (aterms) ## Find individual terms (string missing ':') if (isempty (strfind (aterms(i), ':'){:})) ## Search PredictorNames to associate with column in X sterms = strcmp (this.PredictorNames, aterms(i)); ## Append to interactions matrix intMat = [intMat; sterms]; else ## Split interaction terms (string contains ':') mterms = strsplit (aterms{i}, ':'); ## Add each individual predictor to interaction term vector iterms = logical (zeros (1, this.NumPredictors)); for t = 1:numel (mterms) iterms = iterms | strcmp (this.PredictorNames, mterms(t)); endfor ## Check that all predictors have been identified if (sum (iterms) != t) error (strcat (["ClassificationGAM: some predictors"], ... [" have not been identified."])); endif ## Append to interactions matrix intMat = [intMat; iterms]; endif endfor ## Check that all terms have been identified if (! all (sum (intMat, 2) > 0)) error ("ClassificationGAM: some terms have not been identified."); endif endfunction ## Fit the model function [iter, param, res, RSS, intercept] = fitGAM (this, X, Y, Inter, ... Knots, Order, learning_rate, num_iterations) ## Initialize variables [n_samples, n_features] = size (X); RSS = zeros (1, n_features); ## Initialize model predictions with the intercept (log-odds) p = Inter; intercept = log (p / (1 - p)); f = intercept * ones (n_samples, 1); ## Start boosting iterations for iter = 1:num_iterations ## Compute the gradient y_pred = 1 ./ (1 + exp (-f)); ## Sigmoid function gradient = Y - y_pred; ## Negative gradient of log-loss ## Initialize a variable to store predictions for this iteration f_new = zeros (n_samples, 1); for j = 1:n_features ## Fit a spline to the gradient for feature X_j spline_model = splinefit (X(:, j), gradient, Knots(j), ... "order", Order(j)); ## Predict using the fitted spline spline_pred = ppval (spline_model, X(:, j)); ## Store the spline model parameters param(j) = spline_model; ## Update the model predictions f_new = f_new + learning_rate * spline_pred; endfor ## Update the overall model predictions f = f + f_new ; endfor ## Final residuals and RSS calculation res = Y - 1 ./ (1 + exp (-f)); RSS = sum (res .^ 2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {ClassificationGAM} {@var{CVMdl} =} compact (@var{obj}) ## ## Create a CompactClassificationGAM object. ## ## @code{@var{CVMdl} = compact (@var{obj})} creates a compact version of the ## ClassificationGAM object, @var{obj}. ## ## @seealso{fitcdiscr, ClassificationGAM, CompactClassificationGAM} ## @end deftypefn function CVMdl = compact (this) ## Greate a compact model CVMdl = CompactClassificationGAM (this); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationGAM} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationGAM object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ClassificationGAM ## object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcgam, ClassificationGAM, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "ClassificationGAM"; ## Create variables from model properties X = this.X; Y = this.Y; NumObservations = this.NumObservations; RowsUsed = this.RowsUsed; NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; Formula = this.Formula; Interactions = this.Interactions; Knots = this.Knots; Order = this.Order; DoF = this.DoF; BaseModel = this.BaseModel; ModelwInt = this.ModelwInt; IntMatrix = this.IntMatrix; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "X", "Y", "NumObservations", "RowsUsed", ... "NumPredictors", "PredictorNames", "ResponseName", "ClassNames", ... "Prior", "Cost", "ScoreTransform", "Formula", "Interactions", ... "Knots", "Order", "DoF", "BaseModel", "ModelwInt", "IntMatrix"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationGAM object mdl = ClassificationGAM (1, 1); ## Check that fieldnames in DATA match properties in ClassificationGAM names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) error ("ClassificationGAM.load_model: invalid model in '%s'.", filename) endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction endmethods endclassdef ## Helper function function scores = predict_val (params, XC, intercept) [nsample, ndims_X] = size (XC); ypred = ones (nsample, 1) * intercept; ## Add the remaining terms for j = 1:ndims_X ypred = ypred + ppval (params(j), XC (:,j)); endfor ## Apply the sigmoid function to get probabilities pos_prob = 1 ./ (1 + exp (-ypred)); neg_prob = 1 - pos_prob; scores = [neg_prob, pos_prob]; endfunction %!demo %! ## Train a GAM classifier for binary classification %! ## using specific data and plot the decision boundaries. %! %! ## Define specific data %! X = [1, 2; 2, 3; 3, 3; 4, 5; 5, 5; ... %! 6, 7; 7, 8; 8, 8; 9, 9; 10, 10]; %! Y = [0; 0; 0; 0; 0; ... %! 1; 1; 1; 1; 1]; %! %! ## Train the GAM model %! obj = fitcgam (X, Y, "Interactions", "all") %! %! ## Create a grid of values for prediction %! x1 = [min(X(:,1)):0.1:max(X(:,1))]; %! x2 = [min(X(:,2)):0.1:max(X(:,2))]; %! [x1G, x2G] = meshgrid (x1, x2); %! XGrid = [x1G(:), x2G(:)]; %! [labels, score] = predict (obj, XGrid); ## Test constructor %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [0; 0; 1; 1]; %! PredictorNames = {'Feature1', 'Feature2', 'Feature3'}; %! a = ClassificationGAM (x, y, "PredictorNames", PredictorNames); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {x, y, 4}) %! assert ({a.NumPredictors, a.ResponseName}, {3, "Y"}) %! assert (a.ClassNames, {'0'; '1'}) %! assert (a.PredictorNames, PredictorNames) %! assert (a.BaseModel.Intercept, 0) %!test %! load fisheriris %! inds = strcmp (species,'versicolor') | strcmp (species,'virginica'); %! X = meas(inds, :); %! Y = species(inds, :)'; %! Y = strcmp (Y, 'virginica')'; %! a = ClassificationGAM (X, Y, 'Formula', 'Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3'); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {X, Y, 100}) %! assert ({a.NumPredictors, a.ResponseName}, {4, "Y"}) %! assert (a.ClassNames, {'0'; '1'}) %! assert (a.Formula, 'Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3') %! assert (a.PredictorNames, {'x1', 'x2', 'x3', 'x4'}) %! assert (a.ModelwInt.Intercept, 0) %!test %! X = [2, 3, 5; 4, 6, 8; 1, 2, 3; 7, 8, 9; 5, 4, 3]; %! Y = [0; 1; 0; 1; 1]; %! a = ClassificationGAM (X, Y, 'Knots', [4, 4, 4], 'Order', [3, 3, 3]); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {X, Y, 5}) %! assert ({a.NumPredictors, a.ResponseName}, {3, "Y"}) %! assert (a.ClassNames, {'0'; '1'}) %! assert (a.PredictorNames, {'x1', 'x2', 'x3'}) %! assert (a.Knots, [4, 4, 4]) %! assert (a.Order, [3, 3, 3]) %! assert (a.DoF, [7, 7, 7]) %! assert (a.BaseModel.Intercept, 0.4055, 1e-1) ## Test input validation for constructor %!error ClassificationGAM () %!error ... %! ClassificationGAM (ones(4, 1)) %!error ... %! ClassificationGAM (ones (4,2), ones (1,4)) %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "PredictorNames", ["A"]) %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "PredictorNames", "A") %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "PredictorNames", {"A", "B", "C"}) %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "ResponseName", {"Y"}) %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "ResponseName", 1) %!error ... %! ClassificationGAM (ones(10,2), ones (10,1), "ClassNames", @(x)x) %!error ... %! ClassificationGAM (ones(10,2), ones (10,1), "ClassNames", ['a']) %!error ... %! ClassificationGAM (ones(10,2), ones (10,1), "ClassNames", [1, 2]) %!error ... %! ClassificationGAM (ones(5,2), {'a';'b';'a';'a';'b'}, "ClassNames", {'a','c'}) %!error ... %! ClassificationGAM (ones(10,2), logical (ones (10,1)), "ClassNames", [true, false]) %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "Cost", [1, 2]) %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "Cost", "string") %!error ... %! ClassificationGAM (ones (5,2), ones (5,1), "Cost", {eye(2)}) ## Test predict method %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8; 9, 10]; %! y = [1; 0; 1; 0; 1]; %! a = ClassificationGAM (x, y, "interactions", "all"); %! l = {'0'; '0'; '0'; '0'; '0'}; %! s = [0.3760, 0.6240; 0.4259, 0.5741; 0.3760, 0.6240; ... %! 0.4259, 0.5741; 0.3760, 0.6240]; %! [labels, scores] = predict (a, x); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {x, y, 5}) %! assert ({a.NumPredictors, a.ResponseName}, {2, "Y"}) %! assert (a.ClassNames, {'1'; '0'}) %! assert (a.PredictorNames, {'x1', 'x2'}) %! assert (a.ModelwInt.Intercept, 0.4055, 1e-1) %! assert (labels, l) %! assert (scores, s, 1e-1) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [0; 0; 1; 1]; %! interactions = [false, true, false; true, false, true; false, true, false]; %! a = fitcgam (x, y, "learningrate", 0.2, "interactions", interactions); %! [label, score] = predict (a, x, "includeinteractions", true); %! l = {'0'; '0'; '1'; '1'}; %! s = [0.5106, 0.4894; 0.5135, 0.4865; 0.4864, 0.5136; 0.4847, 0.5153]; %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {x, y, 4}) %! assert ({a.NumPredictors, a.ResponseName}, {3, "Y"}) %! assert (a.ClassNames, {'0'; '1'}) %! assert (a.PredictorNames, {'x1', 'x2', 'x3'}) %! assert (a.ModelwInt.Intercept, 0) %! assert (label, l) %! assert (score, s, 1e-1) ## Test input validation for predict method %!error ... %! predict (ClassificationGAM (ones (4,2), ones (4,1))) %!error ... %! predict (ClassificationGAM (ones (4,2), ones (4,1)), []) %!error ... %! predict (ClassificationGAM (ones (4,2), ones (4,1)), 1) ## Test crossval method %!shared x, y, obj %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [0; 0; 1; 1]; %! obj = fitcgam (x, y); %!test %! CVMdl = crossval (obj); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 10) %! assert (class (CVMdl.Trained{1}), "CompactClassificationGAM") %! assert (CVMdl.CrossValidatedModel, "ClassificationGAM") %!test %! CVMdl = crossval (obj, "KFold", 5); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 5) %! assert (class (CVMdl.Trained{1}), "CompactClassificationGAM") %! assert (CVMdl.CrossValidatedModel, "ClassificationGAM") %!test %! CVMdl = crossval (obj, "HoldOut", 0.2); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationGAM") %! assert (CVMdl.CrossValidatedModel, "ClassificationGAM") %!test %! partition = cvpartition (y, 'KFold', 3); %! CVMdl = crossval (obj, 'cvPartition', partition); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert (CVMdl.KFold == 3) %! assert (class (CVMdl.Trained{1}), "CompactClassificationGAM") %! assert (CVMdl.CrossValidatedModel, "ClassificationGAM") ## Test input validation for crossval method %!error ... %! crossval (obj, "kfold") %!error... %! crossval (obj, "kfold", 12, "holdout", 0.2) %!error ... %! crossval (obj, "kfold", 'a') %!error ... %! crossval (obj, "holdout", 2) %!error ... %! crossval (obj, "leaveout", 1) %!error ... %! crossval (obj, "cvpartition", 1) statistics-release-1.7.3/inst/Classification/ClassificationKNN.m000066400000000000000000003231541475240274700247460ustar00rootroot00000000000000## Copyright (C) 2023 Mohammed Azmat Khan ## Copyright (C) 2023-2024 Andreas Bertsatos ## Copyright (C) 2024 Ruchika Sonagote ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClassificationKNN ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} ClassificationKNN (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} ClassificationKNN (@dots{}, @var{name}, @var{value}) ## ## Create a @qcode{ClassificationKNN} class object containing a k-Nearest ## Neighbor classification model. ## ## @code{@var{obj} = ClassificationKNN (@var{X}, @var{Y})} returns a ## ClassificationKNN object, with @var{X} as the predictor data and @var{Y} ## containing the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or variables. ## @var{X} will be used to train the kNN model. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can contain any type of ## categorical data. @var{Y} must have same numbers of Rows as @var{X}. ## @end itemize ## ## @code{@var{obj} = ClassificationKNN (@dots{}, @var{name}, @var{value})} ## returns a ClassificationKNN object with parameters specified by ## @qcode{Name-Value} pair arguments. Type @code{help fitcknn} for more info. ## ## A @qcode{ClassificationKNN} object, @var{obj}, stores the labelled training ## data and various parameters for the k-Nearest Neighbor classification model, ## which can be accessed in the following fields: ## ## @multitable @columnfractions 0.23 0.02 0.75 ## @headitem @var{Field} @tab @tab @var{Description} ## ## @item @qcode{X} @tab @tab Unstandardized predictor data, specified as a ## numeric matrix. Each column of @var{X} represents one predictor (variable), ## and each row represents one observation. ## ## @item @qcode{Y} @tab @tab Class labels, specified as a logical or ## numeric vector, or cell array of character vectors. Each value in @var{Y} is ## the observed class label for the corresponding row in @var{X}. ## ## @item @qcode{NumObservations} @tab @tab Number of observations used in ## training the ClassificationKNN model, specified as a positive integer scalar. ## This number can be less than the number of rows in the training data because ## rows containing @qcode{NaN} values are not part of the fit. ## ## @item @qcode{RowsUsed} @tab @tab Rows of the original training data ## used in fitting the ClassificationKNN model, specified as a numerical vector. ## If you want to use this vector for indexing the training data in @var{X}, you ## have to convert it to a logical vector, i.e ## @qcode{X = obj.X(logical (obj.RowsUsed), :);} ## ## @item @qcode{Standardize} @tab @tab A boolean flag indicating whether ## the data in @var{X} have been standardized prior to training. ## ## @item @qcode{Sigma} @tab @tab Predictor standard deviations, specified ## as a numeric vector of the same length as the columns in @var{X}. If the ## predictor variables have not been standardized, then @qcode{"obj.Sigma"} is ## empty. ## ## @item @qcode{Mu} @tab @tab Predictor means, specified as a numeric ## vector of the same length as the columns in @var{X}. If the predictor ## variables have not been standardized, then @qcode{"obj.Mu"} is empty. ## ## @item @qcode{NumPredictors} @tab @tab The number of predictors ## (variables) in @var{X}. ## ## @item @qcode{PredictorNames} @tab @tab Predictor variable names, ## specified as a cell array of character vectors. The variable names are in ## the same order in which they appear in the training data @var{X}. ## ## @item @qcode{ResponseName} @tab @tab Response variable name, specified ## as a character vector. ## ## @item @qcode{ClassNames} @tab @tab Names of the classes in the training ## data @var{Y} with duplicates removed, specified as a cell array of character ## vectors. ## ## @item @qcode{BreakTies} @tab @tab Tie-breaking algorithm used by predict ## when multiple classes have the same smallest cost, specified as one of the ## following character arrays: @qcode{"smallest"} (default), which favors the ## class with the smallest index among the tied groups, i.e. the one that ## appears first in the training labelled data. @qcode{"nearest"}, which favors ## the class with the nearest neighbor among the tied groups, i.e. the class ## with the closest member point according to the distance metric used. ## @qcode{"random"}, which randomly picks one class among the tied groups. ## ## @item @qcode{Prior} @tab @tab Prior probabilities for each class, ## specified as a numeric vector. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{Cost} @tab @tab Cost of the misclassification of a point, ## specified as a square matrix. @qcode{Cost(i,j)} is the cost of classifying a ## point into class @qcode{j} if its true class is @qcode{i} (that is, the rows ## correspond to the true class and the columns correspond to the predicted ## class). The order of the rows and columns in @qcode{Cost} corresponds to the ## order of the classes in @qcode{ClassNames}. The number of rows and columns ## in @qcode{Cost} is the number of unique classes in the response. By default, ## @qcode{Cost(i,j) = 1} if @qcode{i != j}, and @qcode{Cost(i,j) = 0} if ## @qcode{i = j}. In other words, the cost is 0 for correct classification and ## 1 for incorrect classification. ## ## @item @qcode{ScoreTransform} @tab @tab A function_handle which is used ## for transforming the kNN prediction score into a posterior probability. By ## default, it is @qcode{'none'}, in which case the @code{predict} and ## @code{resubPredict} methods return the prediction scores. ## ## @item @qcode{NumNeighbors} @tab @tab Number of nearest neighbors in ## @var{X} used to classify each point during prediction, specified as a ## positive integer value. ## ## @item @qcode{Distance} @tab @tab Distance metric, specified as a ## character vector. The allowable distance metric names depend on the choice ## of the neighbor-searcher method. See the available distance metrics in ## @code{knnseaarch} for more info. ## ## @item @qcode{DistanceWeight} @tab @tab Distance weighting function, ## specified as a function handle, which accepts a matrix of nonnegative ## distances, and returns a matrix the same size containing nonnegative distance ## weights. ## ## @item @qcode{DistParameter} @tab @tab Parameter for the distance ## metric, specified either as a positive definite covariance matrix (when the ## distance metric is @qcode{"mahalanobis"}, or a positive scalar as the ## Minkowski distance exponent (when the distance metric is @qcode{"minkowski"}, ## or a vector of positive scale values with length equal to the number of ## columns of @var{X} (when the distance metric is @qcode{"seuclidean"}. For ## any other distance metric, the value of @qcode{DistParameter} is empty. ## ## @item @qcode{NSMethod} @tab @tab Nearest neighbor search method, ## specified as either @qcode{"kdtree"}, which creates and uses a Kd-tree to ## find nearest neighbors, or @qcode{"exhaustive"}, which uses the exhaustive ## search algorithm by computing the distance values from all points in @var{X} ## to find nearest neighbors. ## ## @item @qcode{IncludeTies} @tab @tab A boolean flag indicating whether ## prediction includes all the neighbors whose distance values are equal to the ## @math{k^th} smallest distance. If @qcode{IncludeTies} is @qcode{true}, ## prediction includes all of these neighbors. Otherwise, prediction uses ## exactly @math{k} neighbors. ## ## @item @qcode{BucketSize} @tab @tab Maximum number of data points in the ## leaf node of the Kd-tree, specified as positive integer value. This argument ## is meaningful only when @qcode{NSMethod} is @qcode{"kdtree"}. ## ## @end multitable ## ## @seealso{fitcknn, knnsearch, rangesearch, pdist2} ## @end deftypefn properties (Access = public) X = []; # Predictor data Y = []; # Class labels NumObservations = []; # Number of observations in training dataset RowsUsed = []; # Rows used in fitting NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of misclassification ScoreTransform = []; # Transformation for classification scores Standardize = []; # Flag to standardize predictors Sigma = []; # Predictor standard deviations Mu = []; # Predictor means BreakTies = []; # Tie-breaking algorithm NumNeighbors = []; # Number of nearest neighbors Distance = []; # Distance metric DistanceWeight = []; # Distance weighting function DistParameter = []; # Parameter for distance metric NSMethod = []; # Nearest neighbor search method IncludeTies = []; # Flag for handling ties BucketSize = []; # Maximum data points in each node endproperties methods (Access = public) ## Class object constructor function this = ClassificationKNN (X, Y, varargin) ## Check for sufficient number of input arguments if (nargin < 2) error ("ClassificationKNN: too few input arguments."); endif ## Check X and Y have the same number of observations if (rows (X) != rows (Y)) error ("ClassificationKNN: number of rows in X and Y must be equal."); endif ## Assign original X and Y data to the ClassificationKNN object this.X = X; this.Y = Y; ## Get groups in Y [gY, gnY, glY] = grp2idx (Y); ## Set default values before parsing optional parameters Standardize = false; PredictorNames = []; ResponseName = []; ClassNames = []; Prior = []; Cost = []; Scale = []; # Distance scale for 'seuclidean' Cov = []; # Covariance matrix for 'mahalanobis' Exponent = []; # Exponent for 'minkowski' BreakTies = []; NumNeighbors = []; Distance = []; DistanceWeight = []; DistParameter = []; NSMethod = []; IncludeTies = false; BucketSize = 50; this.ScoreTransform = 'none'; ## Number of parameters for Standardize, Scale, Cov (maximum 1 allowed) SSC = 0; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "standardize" if (SSC < 1) Standardize = varargin{2}; if (! (Standardize == true || Standardize == false)) error (strcat (["ClassificationKNN: 'Standardize' must"], ... [" be either true or false."])); endif SSC += 1; else error (strcat (["ClassificationKNN: 'Standardize' cannot"], ... [" simultaneously be specified with either"], ... [" Scale or Cov."])); endif case "predictornames" PredictorNames = varargin{2}; if (! iscellstr (PredictorNames)) error (strcat (["ClassificationKNN: 'PredictorNames' must"], ... [" be supplied as a cellstring array."])); elseif (columns (PredictorNames) != columns (X)) error (strcat (["ClassificationKNN: 'PredictorNames' must"], ... [" have the same number of columns as X."])); endif case "responsename" ResponseName = varargin{2}; if (! ischar (ResponseName)) error (strcat (["ClassificationKNN: 'ResponseName'"], ... [" must be a character vector."])); endif case "classnames" ClassNames = varargin{2}; if (! (iscellstr (ClassNames) || isnumeric (ClassNames) || islogical (ClassNames))) error (strcat (["ClassificationKNN: 'ClassNames' must be a"], ... [" cellstring, logical or numeric vector."])); endif ## Check that all class names are available in gnY if (iscellstr (ClassNames)) if (! all (cell2mat (cellfun (@(x) any (strcmp (x, gnY)), ClassNames, "UniformOutput", false)))) error (strcat (["ClassificationKNN: not all 'ClassNames'"], ... [" are present in Y."])); endif else if (! all (cell2mat (arrayfun (@(x) any (x == glY), ClassNames, "UniformOutput", false)))) error (strcat (["ClassificationKNN: not all 'ClassNames'"], ... [" are present in Y."])); endif endif case "prior" Prior = varargin{2}; if (! ((isnumeric (Prior) && isvector (Prior)) || (strcmpi (Prior, "empirical") || strcmpi (Prior, "uniform")))) error (strcat (["ClassificationKNN: 'Prior' must be either"], ... [" a numeric vector or a character vector."])); endif case "cost" Cost = varargin{2}; if (! (isnumeric (Cost) && issquare (Cost))) error (strcat (["ClassificationKNN: 'Cost' must be"], ... [" a numeric square matrix."])); endif case "scoretransform" name = "ClassificationKNN"; this.ScoreTransform = parseScoreTransform (varargin{2}, name); case "breakties" BreakTies = varargin{2}; if (! ischar (BreakTies)) error (strcat (["ClassificationKNN: 'BreakTies'"], ... [" must be a character vector."])); endif ## Check that all class names are available in gnY BTs = {"smallest", "nearest", "random"}; if (! any (strcmpi (BTs, BreakTies))) error ("ClassificationKNN: invalid value for 'BreakTies'."); endif case "numneighbors" NumNeighbors = varargin{2}; if (! (isnumeric (NumNeighbors) && isscalar (NumNeighbors) && NumNeighbors > 0 && fix (NumNeighbors) == NumNeighbors)) error (strcat (["ClassificationKNN: 'NumNeighbors'"], ... [" must be a positive integer."])); endif case "distance" Distance = varargin{2}; DMs = {"euclidean", "seuclidean", "mahalanobis", "minkowski", ... "cityblock", "manhattan", "chebychev", "cosine", ... "correlation", "spearman", "hamming", "jaccard"}; if (ischar (Distance)) if (! any (strcmpi (DMs, Distance))) error ("ClassificationKNN: unsupported distance metric."); endif elseif (is_function_handle (Distance)) ## Check the input output sizes of the user function D2 = []; try D2 = Distance (X(1,:), Y); catch ME error (strcat (["ClassificationKNN: invalid function"], ... [" handle for distance metric."])); end_try_catch Yrows = rows (Y); if (! isequal (size (D2), [Yrows, 1])) error (strcat (["ClassificationKNN: custom distance"], ... [" function produces wrong output size."])); endif else error ("ClassificationKNN: invalid distance metric."); endif case "distanceweight" DistanceWeight = varargin{2}; DMs = {"equal", "inverse", "squareinverse"}; if (is_function_handle (DistanceWeight)) m = eye (5); if (! isequal (size (m), size (DistanceWeight (m)))) error (strcat (["ClassificationKNN: function handle for"], ... [" distance weight must return the same"], ... [" size as its input."])); endif this.DistanceWeight = DistanceWeight; else if (! any (strcmpi (DMs, DistanceWeight))) error ("ClassificationKNN: invalid distance weight."); endif if (strcmpi ("equal", DistanceWeight)) this.DistanceWeight = @(x) x; endif if (strcmpi ("inverse", DistanceWeight)) this.DistanceWeight = @(x) x.^(-1); endif if (strcmpi ("squareinverse", DistanceWeight)) this.DistanceWeight = @(x) x.^(-2); endif endif case "scale" if (SSC < 1) Scale = varargin{2}; if (! (isnumeric (Scale) && isvector (Scale))) error ("ClassificationKNN: 'Scale' must be a numeric vector."); endif SSC += 1; else error (strcat (["ClassificationKNN: 'Scale' cannot"], ... [" simultaneously be specified with either"], ... [" 'Standardize' or 'Cov'."])); endif case "cov" if (SSC < 1) Cov = varargin{2}; [~, p] = chol (Cov); if (p != 0) error (strcat (["ClassificationKNN: 'Cov' must be a"], ... [" symmetric positive definite matrix."])); endif SSC += 1; else error (strcat (["ClassificationKNN: 'Cov' cannot"], ... [" simultaneously be specified with either"], ... [" 'Standardize' or 'Scale'."])); endif case "exponent" Exponent = varargin{2}; if (! (isnumeric (Exponent) && isscalar (Exponent) && Exponent > 0 && fix (Exponent) == Exponent)) error (strcat (["ClassificationKNN: 'Exponent'"], ... [" must be a positive integer."])); endif case "nsmethod" NSMethod = varargin{2}; NSM = {"kdtree", "exhaustive"}; if (! ischar (NSMethod)) error (strcat (["ClassificationKNN: 'NSMethod' must"], ... [" be a character vector."])); endif if (! any (strcmpi (NSM, NSMethod))) error (strcat (["ClassificationKNN: 'NSMethod' must"], ... [" be either 'kdtree' or 'exhaustive'."])); endif case "includeties" IncludeTies = varargin{2}; if (! (IncludeTies == true || IncludeTies == false)) error (strcat (["ClassificationKNN: 'IncludeTies'"], ... [" must be either true or false."])); endif case "bucketsize" BucketSize = varargin{2}; if (! (isnumeric (BucketSize) && isscalar (BucketSize) && BucketSize > 0 && fix (BucketSize) == BucketSize)) error (strcat (["ClassificationKNN: 'BucketSize'"], ... [" must be a positive integer."])); endif otherwise error (strcat (["ClassificationKNN: invalid parameter"],... [" name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Get number of variables in training data ndims_X = columns (X); ## Assign the number of predictors to the ClassificationKNN object this.NumPredictors = ndims_X; ## Generate default predictors and response variabe names (if necessary) if (isempty (PredictorNames)) for i = 1:ndims_X PredictorNames {i} = strcat ("x", num2str (i)); endfor endif if (isempty (ResponseName)) ResponseName = "Y"; endif ## Assign predictors and response variable names this.PredictorNames = PredictorNames; this.ResponseName = ResponseName; ## Handle class names if (! isempty (ClassNames)) if (iscellstr (ClassNames)) ru = find (! ismember (gnY, ClassNames)); else ru = find (! ismember (glY, ClassNames)); endif for i = 1:numel (ru) gY(gY == ru(i)) = NaN; endfor endif ## Remove missing values from X and Y RowsUsed = ! logical (sum (isnan ([X, gY]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [gY, gnY, glY] = grp2idx (Y); this.ClassNames = gnY; ## Check X contains valid data if (! (isnumeric (X) && isfinite (X))) error ("ClassificationKNN: invalid values in X."); endif ## Assign the number of observations and their correspoding indices ## on the original data, which will be used for training the model, ## to the ClassificationKNN object this.NumObservations = rows (X); this.RowsUsed = cast (RowsUsed, "double"); ## Handle Standardize flag if (Standardize) this.Standardize = true; this.Sigma = std (X, [], 1); this.Sigma(this.Sigma == 0) = 1; # predictor is constant this.Mu = mean (X, 1); else this.Standardize = false; this.Sigma = []; this.Mu = []; endif ## Handle BreakTies if (isempty (BreakTies)) this.BreakTies = "smallest"; else this.BreakTies = BreakTies; endif ## Handle Prior and Cost if (strcmpi ("uniform", Prior)) this.Prior = ones (size (gnY)) ./ numel (gnY); elseif (isempty (Prior) || strcmpi ("empirical", Prior)) pr = []; for i = 1:numel (gnY) pr = [pr; sum(gY==i)]; endfor this.Prior = pr ./ sum (pr); elseif (isnumeric (Prior)) if (numel (gnY) != numel (Prior)) error (strcat (["ClassificationKNN: the elements in 'Prior'"], ... [" do not correspond to selected classes in Y."])); endif this.Prior = Prior ./ sum (Prior); endif if (isempty (Cost)) this.Cost = cast (! eye (numel (gnY)), "double"); else if (numel (gnY) != sqrt (numel (Cost))) error (strcat (["ClassificationKNN: the number of rows"], ... [" and columns in 'Cost' must correspond"], ... [" to selected classes in Y."])); endif this.Cost = Cost; endif ## Get number of neighbors if (isempty (NumNeighbors)) this.NumNeighbors = 1; else this.NumNeighbors = NumNeighbors; endif ## Get distance metric if (isempty (Distance)) Distance = "euclidean"; endif this.Distance = Distance; ## Get distance weight if (isempty (DistanceWeight)) this.DistanceWeight = @(x) x; endif ## Handle distance metric parameters (Scale, Cov, Exponent) if (! isempty (Scale)) if (! strcmpi (Distance, "seuclidean")) error (strcat (["ClassificationKNN: 'Scale' is only valid"], ... [" when distance metric is seuclidean."])); endif if (numel (Scale) != ndims_X) error (strcat (["ClassificationKNN: 'Scale' vector must have"], ... [" equal length to the number of columns in X."])); endif if (any (Scale < 0)) error (strcat (["ClassificationKNN: 'Scale' vector must"], ... [" contain nonnegative scalar values."])); endif this.DistParameter = Scale; else if (strcmpi (Distance, "seuclidean")) if (Standardize) this.DistParameter = ones (1, ndims_X); else this.DistParameter = std (X, [], 1); endif endif endif if (! isempty (Cov)) if (! strcmpi (Distance, "mahalanobis")) error (strcat (["ClassificationKNN: 'Cov' is only valid"], ... [" when distance metric is 'mahalanobis'."])); endif if (columns (Cov) != ndims_X) error (strcat (["ClassificationKNN: 'Cov' matrix"], ... [" must have equal columns as X."])); endif this.DistParameter = Cov; else if (strcmpi (Distance, "mahalanobis")) this.DistParameter = cov (X); endif endif if (! isempty (Exponent)) if (! strcmpi (Distance, "minkowski")) error (strcat (["ClassificationKNN: 'Exponent' is only"], ... [" valid when distance metric is 'minkowski'."])); endif this.DistParameter = Exponent; else if (strcmpi (Distance, "minkowski")) this.DistParameter = 2; endif endif ## Get Nearest neighbor search method kdm = {"euclidean", "cityblock", "manhattan", "minkowski", "chebychev"}; if (! isempty (NSMethod)) if (strcmpi ("kdtree", NSMethod) && (! any (strcmpi (kdm, Distance)))) error (strcat (["ClassificationKNN: 'kdtree' method is only va"], ... ["lid for 'euclidean', 'cityblock', 'manhattan',"], ... [" 'minkowski', and 'chebychev' distance metrics."])); endif this.NSMethod = NSMethod; else if (any (strcmpi (kdm, Distance)) && ndims_X <= 10) this.NSMethod = "kdtree"; else this.NSMethod = "exhaustive"; endif endif ## Assign IncludeTies and BucketSize properties this.IncludeTies = IncludeTies; this.BucketSize = BucketSize; endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationKNN} {@var{labels} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {ClassificationKNN} {[@var{labels}, @var{scores}, @var{cost}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the kNN algorithm from a ## k-Nearest Neighbor classification model. ## ## @code{@var{labels} = predict (@var{obj}, @var{XC})} returns the matrix of ## labels predicted for the corresponding instances in @var{XC}, using the ## predictor data in @code{obj.X} and corresponding labels, @code{obj.Y}, ## stored in the k-Nearest Neighbor classification model, @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationKNN} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @end itemize ## ## @code{[@var{labels}, @var{scores}, @var{cost}] = predict (@var{obj}, ## @var{XC})} also returns @var{scores}, which contains the predicted class ## scores or posterior probabilities for each instance of the corresponding ## unique classes, and @var{cost}, which is a matrix containing the expected ## cost of the classifications. By default, @var{scores} returns the ## posterior probabilities for KNN models, unless a specific ScoreTransform ## function has been specified. See @code{fitcknn} for more info. ## ## Note! @code{predict} is explicitly using @qcode{"exhaustive"} as the ## nearest search method due to the very slow implementation of ## @qcode{"kdtree"} in the @code{knnsearch} function. ## ## @seealso{fitcknn, ClassificationKNN, knnsearch} ## @end deftypefn function [labels, scores, cost] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error ("ClassificationKNN.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("ClassificationKNN.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["ClassificationKNN.predict:"], ... [" XC must have the same number of"], ... [" predictors as the trained model."])); endif ## Get training data and labels X = this.X(logical (this.RowsUsed),:); Y = this.Y(logical (this.RowsUsed),:); ## Standardize (if necessary) if (this.Standardize) X = (X - this.Mu) ./ this.Sigma; XC = (XC - this.Mu) ./ this.Sigma; endif ## Train kNN if (strcmpi (this.Distance, "seuclidean")) [idx, dist] = knnsearch (X, XC, "k", this.NumNeighbors, ... "NSMethod", "exhaustive", "Distance", "seuclidean", ... "Scale", this.DistParameter, "sortindices", true, ... "includeties", this.IncludeTies, ... "bucketsize", this.BucketSize); elseif (strcmpi (this.Distance, "mahalanobis")) [idx, dist] = knnsearch (X, XC, "k", this.NumNeighbors, ... "NSMethod", "exhaustive", "Distance", "mahalanobis", ... "cov", this.DistParameter, "sortindices", true, ... "includeties", this.IncludeTies, ... "bucketsize", this.BucketSize); elseif (strcmpi (this.Distance, "minkowski")) [idx, dist] = knnsearch (X, XC, "k", this.NumNeighbors, ... "NSMethod", "exhaustive", "Distance", "minkowski", ... "P", this.DistParameter, "sortindices", true, ... "includeties",this.IncludeTies, ... "bucketsize", this.BucketSize); else [idx, dist] = knnsearch (X, XC, "k", this.NumNeighbors, ... "NSMethod", "exhaustive", "Distance", this.Distance, ... "sortindices", true, "includeties", this.IncludeTies, ... "bucketsize", this.BucketSize); endif ## Make prediction labels = {}; scores = []; cost = []; ## Get IDs and labels for each point in training data [gY, gnY, glY] = grp2idx (Y); ## Evaluate the K nearest neighbours for each new point for i = 1:rows (idx) ## Get K nearest neighbours if (this.IncludeTies) NN_idx = idx{i}; NNdist = dist{i}; else NN_idx = idx(i,:); NNdist = dist(i,:); endif k = numel (NN_idx); kNNgY = gY(NN_idx); ## Count frequency for each class for c = 1:numel (this.ClassNames) freq(c) = sum (kNNgY == c) / k; endfor ## Get labels according to BreakTies if (strcmpi (this.BreakTies, "smallest")) [~, idl] = max (freq); else idl = find (freq == max (freq)); tgn = numel (idl); if (tgn > 1) if (strcmpi (this.BreakTies, "nearest")) for t = 1:tgn tgs(t) = find (gY(NN_idx) == idl(t)); endfor [~, idm] = min (tgs); idl = idl(idm); else # "random" idl = idl(randperm (numel (idl))(1)); endif endif endif labels = [labels; gnY{idl}]; ## Calculate scores and cost scores = [scores; freq]; cost = [cost; 1-freq]; ## Apply ScoreTransform (if applicable) if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["ClassificationKNN.predict: 'ScoreTransform'"], ... [" must be a 'function_handle' object."])); endif scores = f (scores); endif endfor endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationKNN} {@var{L} =} loss (@var{obj}, @var{X}, @var{Y}) ## @deftypefnx {ClassificationKNN} {@var{L} =} loss (@dots{}, @var{name}, @var{value}) ## ## Compute loss for a trained ClassificationKNN object. ## ## @code{@var{L} = loss (@var{obj}, @var{X}, @var{Y})} computes the loss, ## @var{L}, using the default loss function @qcode{'mincost'}. ## ## @itemize ## @item ## @code{obj} is a @var{ClassificationKNN} object trained on @code{X} and ## @code{Y}. ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or ## variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels ## of corresponding predictor data in @var{X}. @var{Y} must have same ## numbers of Rows as @var{X}. ## @end itemize ## ## @code{@var{L} = loss (@dots{}, @var{name}, @var{value})} allows ## additional options specified by @var{name}-@var{value} pairs: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"LossFun"} @tab @tab Specifies the loss function to use. ## Can be a function handle with four input arguments (C, S, W, Cost) ## which returns a scalar value or one of: ## 'binodeviance', 'classifcost', 'classiferror', 'exponential', ## 'hinge', 'logit','mincost', 'quadratic'. ## @itemize ## @item ## @code{C} is a logical matrix of size @math{NxK}, where @math{N} is the ## number of observations and @math{K} is the number of classes. ## The element @code{C(i,j)} is true if the class label of the i-th ## observation is equal to the j-th class. ## @item ## @code{S} is a numeric matrix of size @math{NxK}, where each element ## represents the classification score for the corresponding class. ## @item ## @code{W} is a numeric vector of length @math{N}, representing ## the observation weights. ## @item ## @code{Cost} is a @math{KxK} matrix representing the misclassification ## costs. ## @end itemize ## ## @item @qcode{"Weights"} @tab @tab Specifies observation weights, must be ## a numeric vector of length equal to the number of rows in X. ## Default is @code{ones (size (X, 1))}. loss normalizes the weights so that ## observation weights in each class sum to the prior probability of that ## class. When you supply Weights, loss computes the weighted ## classification loss. ## ## @end multitable ## ## @seealso{fitcknn, ClassificationKNN} ## @end deftypefn function L = loss (this, X, Y, varargin) ## Check for sufficient input arguments if (nargin < 3) error ("ClassificationKNN.loss: too few input arguments."); elseif (mod (nargin - 3, 2) != 0) error (["ClassificationKNN.loss: name-value arguments must be in", ... " pairs."]); elseif (nargin > 7) error ("ClassificationKNN.loss: too many input arguments."); endif ## Check for valid X if (isempty (X)) error ("ClassificationKNN.loss: X is empty."); elseif (columns (this.X) != columns (X)) error (strcat (["ClassificationKNN.loss: X must have the same"], ... [" number of predictors as the trained model."])); endif ## Default values LossFun = 'mincost'; Weights = []; ## Validate Y valid_types = {'char', 'string', 'logical', 'single', 'double', 'cell'}; if (! (any (strcmp (class (Y), valid_types)))) error ("ClassificationKNN.loss: Y must be of a valid type."); endif ## Validate size of Y if (size (Y, 1) != size (X, 1)) error (["ClassificationKNN.loss: Y must have the same number", ... " of rows as X."]); endif ## Parse name-value arguments while (numel (varargin) > 0) Value = varargin{2}; switch (tolower (varargin{1})) case 'lossfun' if (isa (Value, 'function_handle')) ## Check if the loss function is valid if (nargin (Value) != 4) error (["ClassificationKNN.loss: custom loss function", ... " must accept exactly four input arguments."]); endif try n = 1; K = 2; C_test = false (n, K); S_test = zeros (n, K); W_test = ones (n, 1); Cost_test = ones (K) - eye (K); test_output = Value (C_test, S_test, W_test, Cost_test); if (! isscalar (test_output)) error (["ClassificationKNN.loss: custom loss function", ... " must return a scalar value."]); endif catch error (["ClassificationKNN.loss: custom loss function", ... " is not valid or does not produce correct output."]); end_try_catch LossFun = Value; elseif (ischar (Value) && any (strcmpi (Value, {"binodeviance", ... "classifcost", "classiferror", "exponential", "hinge", ... "logit", "mincost", "quadratic"}))) LossFun = Value; else error ("ClassificationKNN.loss: invalid loss function."); endif case 'weights' if (isnumeric (Value) && isvector (Value)) if (numel (Value) != size (X ,1)) error (["ClassificationKNN.loss: size of Weights must", ... " be equal to the number of rows in X."]); elseif (numel (Value) == size (X, 1)) Weights = Value; endif else error ("ClassificationKNN.loss: invalid Weights."); endif otherwise error ("ClassificationKNN.loss: invalid name-value arguments."); endswitch varargin (1:2) = []; endwhile ## Check for missing values in X if (! isa (LossFun, 'function_handle')) lossfun = tolower (LossFun); if (! strcmp (lossfun, 'mincost') && ! strcmp (lossfun, ... 'classiferror') && ! strcmp (lossfun, 'classifcost') ... && any (isnan (X(:)))) L = NaN; return; endif endif ## Convert Y to a cell array of strings if (ischar (Y)) Y = cellstr (Y); elseif (isnumeric (Y)) Y = cellstr (num2str (Y)); elseif (islogical (Y)) Y = cellstr (num2str (double (Y))); elseif (iscell (Y)) Y = cellfun (@num2str, Y, 'UniformOutput', false); else error (["ClassificationKNN.loss: Y must be a numeric,", ... " logical, char, string, or cell array."]); endif ## Check if Y contains correct classes if (! all (ismember (unique (Y), this.ClassNames))) error (["ClassificationKNN.loss: Y must contain only", ... " the classes in ClassNames."]); endif ## Set default weights if not specified if (isempty (Weights)) Weights = ones (size (X, 1), 1); endif ## Normalize Weights unique_classes = this.ClassNames; class_prior_probs = this.Prior; norm_weights = zeros (size (Weights)); for i = 1:numel (unique_classes) class_idx = ismember (Y, unique_classes{i}); if (sum (Weights(class_idx)) > 0) norm_weights(class_idx) = ... Weights(class_idx) * class_prior_probs(i) / sum (Weights(class_idx)); endif endfor Weights = norm_weights / sum (norm_weights); ## Number of observations n = size (X, 1); ## Predict classification scores [label, scores] = predict (this, X); ## C is vector of K-1 zeros, with 1 in the ## position corresponding to the true class K = numel (this.ClassNames); C = false (n, K); for i = 1:n class_idx = find (ismember (this.ClassNames, Y{i})); C(i, class_idx) = true; endfor Y_new = C'; ## Compute the loss using custom loss function if (isa (LossFun, 'function_handle')) L = LossFun (C, scores, Weights, this.Cost); return; endif ## Compute the scalar classification score for each observation m_j = zeros (n, 1); for i = 1:n m_j(i) = scores(i,:) * Y_new(:,i); endfor ## Compute the loss switch (tolower (LossFun)) case 'binodeviance' b = log (1 + exp (-2 * m_j)); L = (Weights') * b; case 'hinge' h = max (0, 1 - m_j); L = (Weights') * h; case 'exponential' e = exp (-m_j); L = (Weights') * e; case 'logit' l = log (1 + exp (-m_j)); L = (Weights') * l; case 'quadratic' q = (1 - m_j) .^ 2; L = (Weights') * q; case 'classiferror' L = 0; for i = 1:n L = L + Weights(i) * (! isequal (Y(i), label(i))); endfor case 'mincost' Cost = this.Cost; L = 0; for i = 1:n f_Xj = scores(i, :); gamma_jk = f_Xj * Cost; [~, min_cost_class] = min (gamma_jk); cj = Cost(find (ismember (this.ClassNames, Y(i))), min_cost_class); L = L + Weights(i) * cj; endfor case 'classifcost' Cost = this.Cost; L = 0; for i = 1:n y_idx = find (ismember (this.ClassNames, Y(i))); y_hat_idx = find (ismember (this.ClassNames, label(i))); L = L + Weights(i) * Cost(y_idx, y_hat_idx); endfor otherwise error ("ClassificationKNN.loss: invalid loss function."); endswitch endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationKNN} {@var{m} =} margin (@var{obj}, @var{X}, @var{Y}) ## ## @code{@var{m} = margin (@var{obj}, @var{X}, @var{Y})} returns ## the classification margins for @var{obj} with data @var{X} and ## classification @var{Y}. @var{m} is a numeric vector of length size (X,1). ## ## @itemize ## @item ## @code{obj} is a @var{ClassificationKNN} object trained on @code{X} ## and @code{Y}. ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or ## variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels ## of corresponding predictor data in @var{X}. @var{Y} must have same ## numbers of Rows as @var{X}. ## @end itemize ## ## The classification margin for each observation is the difference between ## the classification score for the true class and the maximal ## classification score for the false classes. ## ## @seealso{fitcknn, ClassificationKNN} ## @end deftypefn function m = margin (this, X, Y) ## Check for sufficient input arguments if (nargin < 3) error ("ClassificationKNN.margin: too few input arguments."); endif ## Check for valid X if (isempty (X)) error ("ClassificationKNN.margin: X is empty."); elseif (columns (this.X) != columns (X)) error (strcat (["ClassificationKNN.margin: X must have the same"], ... [" number of predictors as the trained model."])); endif ## Validate Y valid_types = {'char', 'string', 'logical', 'single', 'double', 'cell'}; if (! (any (strcmp (class (Y), valid_types)))) error ("ClassificationKNN.margin: Y must be of a valid type."); endif ## Validate X valid_types = {'single', 'double'}; if (! (any (strcmp (class (X), valid_types)))) error ("ClassificationKNN.margin: X must be of a valid type."); endif ## Validate size of Y if (size (Y, 1) != size (X, 1)) error (["ClassificationKNN.margin: Y must have the same", ... " number of rows as X."]); endif ## Convert Y to a cell array of strings if (ischar (Y)) Y = cellstr (Y); elseif (isnumeric (Y)) Y = cellstr (num2str (Y)); elseif (islogical (Y)) Y = cellstr (num2str (double (Y))); elseif (iscell (Y)) Y = cellfun (@num2str, Y, 'UniformOutput', false); else error (["ClassificationKNN.margin: Y must be a numeric,", ... " logical, char, string, or cell array."]); endif ## Check if Y contains correct classes if (! all (ismember (unique (Y), this.ClassNames))) error (["ClassificationKNN.margin: Y must contain only", ... " the classes in ClassNames."]); endif ## Number of Observations n = size (X, 1); ## Initialize the margin vector m = zeros (n, 1); ## Calculate the classification scores [~, scores] = predict (this, X); ## Loop over each observation to compute the margin for i = 1:n ## True class index true_class_idx = find (ismember (this.ClassNames, Y{i})); ## Score for the true class true_class_score = scores(i, true_class_idx); ## Get the maximal score for the false classes scores(i, true_class_idx) = -Inf; ## Temporarily max_false_class_score = max (scores(i, :)); if (max_false_class_score == -Inf) m = NaN; return; endif scores(i, true_class_idx) = true_class_score; ## Restore ## Calculate the margin m(i) = true_class_score - max_false_class_score; endfor endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationKNN} {@var{[pd, x, y]} =} partialDependence (@var{obj}, @var{Vars}, @var{Labels}) ## @deftypefnx {ClassificationKNN} {@var{[pd, x, y]} =} partialDependence (@dots{}, @var{Data}) ## @deftypefnx {ClassificationKNN} {@var{[pd, x, y]} =} partialDependence (@dots{}, @var{name}, @var{value}) ## ## Compute partial dependence for a trained ClassificationKNN object. ## ## @code{@var{[pd, x, y]} = partialDependence (@var{obj}, @var{Vars}, ## @var{Labels})} ## computes the partial dependence of the classification scores on the ## variables @var{Vars} for the specified class @var{Labels}. ## ## @itemize ## @item ## @code{obj} is a trained @var{ClassificationKNN} object. ## @item ## @code{Vars} is a vector of positive integers, character vector, ## string array, or cell array of character ## vectors representing predictor variables (it can be indices of ## predictor variables in @var{obj.X}). ## @item ## @code{Labels} is a character vector, logical vector, numeric vector, ## or cell array of character vectors representing class ## labels. (column vector) ## @end itemize ## ## @code{@var{[pd, x, y]} = partialDependence (@dots{}, @var{Data})} ## specifies new predictor data to use for computing the partial dependence. ## ## @code{@var{[pd, x, y]} = partialDependence (@dots{}, @var{name}, ## @var{value})} allows additional options specified by name-value pairs: ## ## @multitable @columnfractions 0.32 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"NumObservationsToSample"} @tab @tab Number of ## observations to sample. Must be a positive integer. Defaults to the ## number of observations in the training data. ## @item @qcode{"QueryPoints"} @tab @tab Points at which to evaluate ## the partial dependence. ## Must be a numeric column vector, numeric two-column matrix, or ## cell array of character column vectors. ## @item @qcode{"UseParallel"} @tab @tab Logical value indicating ## whether to perform computations in parallel. ## Defaults to @code{false}. ## @end multitable ## ## @subheading Return Values ## @itemize ## @item @code{pd}: Partial dependence values. ## @item @code{x}: Query points for the first predictor variable in Vars. ## @item @code{y}: Query points for the second predictor variable in ## Vars (if applicable). ## @end itemize ## ## @seealso{fitcknn, ClassificationKNN} ## @end deftypefn function [pd, x, y] = partialDependence (this, Vars, Labels, varargin) if (nargin < 3) error ("ClassificationKNN.partialDependence: too few input arguments."); endif ## Validate Vars if (isnumeric (Vars)) if (! all (Vars > 0) || ! (numel (Vars) == 1 || numel (Vars) == 2)) error (["ClassificationKNN.partialDependence: vars must be a", ... " positive integer or vector of two positive integers."]); endif elseif (iscellstr (Vars)) if (! (numel (Vars) == 1 || numel (Vars) == 2)) error (["ClassificationKNN.partialDependence: vars must be a", ... " string array or cell array of one or two character", ... " vectors."]); endif Vars = cellfun (@(v) find (strcmp (this.PredictorNames, v)), Vars); elseif (ischar (Vars)) Vars = find (strcmp (this.PredictorNames, Vars)); if (isempty (Vars)) error (["ClassificationKNN.partialDependence: vars must", ... " match one of the predictor names."]); endif else error (["ClassificationKNN.partialDependence: vars must be", ... " a string, or cell array."]); endif ## Validate Labels if (! (ischar (Labels) || islogical (Labels) || ... isnumeric (Labels) || iscellstr (Labels))) error ("ClassificationKNN.partialDependence: invalid type for Labels."); endif ## Convert Labels to a cell array of strings if (ischar (Labels)) Labels = cellstr (Labels); elseif (isnumeric (Labels)) Labels = cellstr (num2str (Labels)); elseif (islogical (Labels)) Labels = cellstr (num2str (double (Labels))); elseif (iscell (Labels)) Labels = cellfun (@num2str, Labels, 'UniformOutput', false); else error (["ClassificationKNN.partialDependence: labels must be", ... " a numeric, logical, string, or cell array."]); endif ## Additional validation to match ClassNames if (! all (ismember (Labels, this.ClassNames))) error (["ClassificationKNN.partialDependence: labels must match", ... "the class names in the ClassNames property."]); endif ## Default values Data = this.X; UseParallel = false; NumObservationsToSample = size (Data, 1); QueryPoints = []; ## Check for Data and other optional arguments if (nargin > 3) if (size (varargin{1}) == size (this.X)) Data = varargin{1}; ## Ensure Data consistency if (! all (size (Data, 2) == numel (this.PredictorNames))) error (["ClassificationKNN.partialDependence: data must have", ... " the same number and order of columns as the", ... " predictor variables."]); endif ## Ensure Name-Value pairs are even length if (mod (nargin - 4, 2) != 0) error (["ClassificationKNN.partialDependence: name-value", ... " arguments must be in pairs."]); endif ## Set the number of observations to sample NumObservationsToSample = size (Data, 1); idx = 2; else ## Ensure Name-Value pairs are even length if (mod (nargin - 3, 2) != 0) error (["ClassificationKNN.partialDependence: name-value", ... " arguments must be in pairs."]); endif idx = 1; endif ## Handle name-value pair arguments for i = idx:2:length (varargin) if (! ischar (varargin{i})) error (["ClassificationKNN.partialDependence: name arguments", ... " must be strings."]); endif Value = varargin{i+1}; ## Parse name-value pairs switch (lower (varargin{i})) case 'numobservationstosample' if (! isnumeric (Value) || Value <= 0 || Value != round (Value)) error (["ClassificationKNN.partialDependence: ", ... "NumObservationsToSample must be a positive integer."]); endif NumObservationsToSample = Value; if (Value > size (Data, 1)) NumObservationsToSample = size (Data, 1); endif case 'querypoints' if (! isnumeric (Value) && ! iscell (Value)) error (["ClassificationKNN.partialDependence: QueryPoints", ... " must be a numeric column vector, numeric", ... " two-column matrix, or cell array of", ... " character column vectors."]); endif QueryPoints = Value; case 'useparallel' if (! islogical (UseParallel)) error (["ClassificationKNN.partialDependence: ", ... "UseParallel must be a logical value."]); endif UseParallel = Value; otherwise error (["ClassificationKNN.partialDependence: ", ... "name-value pair argument not recognized."]); endswitch endfor endif ## Sample observations if needed if (NumObservationsToSample < size (Data, 1)) Data = datasample (Data, NumObservationsToSample, 'Replace', false); endif ## Generate QueryPoints if not specified if (isempty (QueryPoints)) if (numel (Vars) == 1) if (isnumeric (Data(:, Vars))) QueryPoints = linspace(min (Data(:, Vars)), ... max (Data(:, Vars)), 100)'; else QueryPoints = unique (Data(:, Vars)); endif else QueryPoints = cell (1, numel (Vars)); for j = 1:numel (Vars) if (isnumeric (Data(:, Vars(j)))) QueryPoints{j} = linspace(min (Data(:, Vars(j))), ... max (Data(:, Vars(j))), 100)'; else QueryPoints{j} = unique (Data(:, Vars(j))); endif endfor endif endif ## Prepare grid points for predictions if (numel (Vars) == 1) gridPoints = QueryPoints; else if (ischar (QueryPoints)) [X1, X2] = meshgrid (QueryPoints(1), QueryPoints(2)); else [X1, X2] = meshgrid (QueryPoints{1}, QueryPoints{2}); endif gridPoints = [X1(:), X2(:)]; endif ## Predict responses for the grid points numClasses = numel (this.ClassNames); numQueryPoints = size (gridPoints, 1); predictions = zeros (numQueryPoints, numClasses); if (UseParallel) parfor i = 1:numQueryPoints tempData = Data; for j = 1:numel (Vars) tempData(:, Vars(j)) = repmat (gridPoints(i, j), ... NumObservationsToSample, 1); endfor [~, scores] = predict (this, tempData); predictions(i, :) = mean (scores, 1); endparfor else for i = 1:numQueryPoints tempData = Data; for j = 1:numel (Vars) tempData(:, Vars(j)) = repmat (gridPoints(i, j), ... NumObservationsToSample, 1); endfor [~, scores] = predict (this, tempData); predictions(i, :) = mean (scores, 1); endfor endif ## Compute partial dependence if (numel (Vars) == 1) if (numel (Labels) == 1) classIndex = find (strcmp (this.ClassNames, Labels)); pd = predictions(:, classIndex)'; else pd = zeros (numel (Labels), numel (QueryPoints)); for j = 1:numel (Labels) classIndex = find (strcmp (this.ClassNames, Labels{j})); pd(j, :) = predictions(:, classIndex)'; endfor endif x = QueryPoints; y = []; else if (numel (Labels) == 1) classIndex = find (strcmp (this.ClassNames, Labels)); pd = reshape (predictions(:, classIndex), numel (QueryPoints{1}), ... numel (QueryPoints{2})); else pd = zeros (numel (Labels), numel (QueryPoints{1}), ... numel (QueryPoints{2})); for j = 1:numel (Labels) classIndex = find (strcmp (this.ClassNames, Labels{j})); pd(j, :, :) = reshape (predictions(:, classIndex), ... numel (QueryPoints{1}), numel (QueryPoints{2})); endfor endif x = QueryPoints{1}; y = QueryPoints{2}; endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationKNN} {@var{CVMdl} =} crossval (@var{obj}) ## @deftypefnx {ClassificationKNN} {@var{CVMdl} =} crossval (@dots{}, @var{Name}, @var{Value}) ## ## Cross Validate a ClassificationKNN object. ## ## @code{@var{CVMdl} = crossval (@var{obj})} returns a cross-validated model ## object, @var{CVMdl}, from a trained model, @var{obj}, using 10-fold ## cross-validation by default. ## ## @code{@var{CVMdl} = crossval (@var{obj}, @var{name}, @var{value})} ## specifies additional name-value pair arguments to customize the ## cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"KFold"} @tab @tab Specify the number of folds to use in ## k-fold cross-validation. @code{"KFold", @var{k}}, where @var{k} is an ## integer greater than 1. ## ## @item @qcode{"Holdout"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{"Holdout", @var{p}}, where @var{p} is a ## scalar in the range @math{(0,1)}. ## ## @item @qcode{"Leaveout"} @tab @tab Specify whether to perform ## leave-one-out cross-validation. @code{"Leaveout", @var{Value}}, where ## @var{Value} is 'on' or 'off'. ## ## @item @qcode{"CVPartition"} @tab @tab Specify a @qcode{cvpartition} ## object used for cross-validation. @code{"CVPartition", @var{cv}}, where ## @code{isa (@var{cv}, "cvpartition")} = 1. ## ## @end multitable ## ## @seealso{fitcknn, ClassificationKNN, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function CVMdl = crossval (this, varargin) ## Check input if (nargin < 1) error ("ClassificationKNN.crossval: too few input arguments."); endif if (numel (varargin) == 1) error (strcat (["ClassificationKNN.crossval: Name-Value arguments"], ... [" must be in pairs."])); elseif (numel (varargin) > 2) error (strcat (["ClassificationKNN.crossval: specify only one of"], ... [" the optional Name-Value paired arguments."])); endif ## Add default values numFolds = 10; Holdout = []; Leaveout = 'off'; CVPartition = []; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case 'kfold' numFolds = varargin{2}; if (! (isnumeric (numFolds) && isscalar (numFolds) && (numFolds == fix (numFolds)) && numFolds > 1)) error (strcat (["ClassificationKNN.crossval: 'KFold' must"], ... [" be an integer value greater than 1."])); endif case 'holdout' Holdout = varargin{2}; if (! (isnumeric (Holdout) && isscalar (Holdout) && Holdout > 0 && Holdout < 1)) error (strcat (["ClassificationKNN.crossval: 'Holdout' must"], ... [" be a numeric value between 0 and 1."])); endif case 'leaveout' Leaveout = varargin{2}; if (! (ischar (Leaveout) && (strcmpi (Leaveout, 'on') || strcmpi (Leaveout, 'off')))) error (strcat (["ClassificationKNN.crossval: 'Leaveout'"], ... [" must be either 'on' or 'off'."])); endif case 'cvpartition' CVPartition = varargin{2}; if (! (isa (CVPartition, 'cvpartition'))) error (strcat (["ClassificationKNN.crossval: 'CVPartition'"],... [" must be a 'cvpartition' object."])); endif otherwise error (strcat (["ClassificationKNN.crossval: invalid"],... [" parameter name in optional paired arguments."])); endswitch varargin (1:2) = []; endwhile ## Determine the cross-validation method to use if (! isempty (CVPartition)) partition = CVPartition; elseif (! isempty (Holdout)) partition = cvpartition (this.Y, 'Holdout', Holdout); elseif (strcmpi (Leaveout, 'on')) partition = cvpartition (this.Y, 'LeaveOut'); else partition = cvpartition (this.Y, 'KFold', numFolds); endif ## Create a cross-validated model object CVMdl = ClassificationPartitionedModel (this, partition); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationKNN} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationKNN object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ClassificationKNN ## object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcknn, ClassificationKNN} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "ClassificationKNN"; ## Create variables from model properties X = this.X; Y = this.Y; NumObservations = this.NumObservations; RowsUsed = this.RowsUsed; Standardize = this.Standardize; Sigma = this.Sigma; Mu = this.Mu; NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; BreakTies = this.BreakTies; NumNeighbors = this.NumNeighbors; Distance = this.Distance; DistanceWeight = this.DistanceWeight; DistParameter = this.DistParameter; NSMethod = this.NSMethod; IncludeTies = this.IncludeTies; BucketSize = this.BucketSize; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "X", "Y", "NumObservations", "RowsUsed", ... "Standardize", "Sigma", "Mu", "NumPredictors", "PredictorNames", ... "ResponseName", "ClassNames", "Prior", "Cost", "ScoreTransform", ... "BreakTies", "NumNeighbors", "Distance", "DistanceWeight", ... "DistParameter", "NSMethod", "IncludeTies", "BucketSize"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationKNN object mdl = ClassificationKNN (1, 1); ## Check that fieldnames in DATA match properties in ClassificationKNN names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) error ("ClassificationKNN.load_model: invalid model in '%s'.", filename) endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction endmethods endclassdef ## Helper functions for ScoreTransform function out = ismax (score) out = score; out(score == max (score)) = 1; out(score != max (score)) = 0; endfunction function out = symmetricismax (score) out = score; out(score == max (score)) = 1; out(score != max (score)) = -1; endfunction %!demo %! ## Create a k-nearest neighbor classifier for Fisher's iris data with k = 5. %! ## Evaluate some model predictions on new data. %! %! load fisheriris %! x = meas; %! y = species; %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 5, "Standardize", 1); %! [label, score, cost] = predict (obj, xc) %!demo %! load fisheriris %! x = meas; %! y = species; %! obj = fitcknn (x, y, "NumNeighbors", 5, "Standardize", 1); %! %! ## Create a cross-validated model %! CVMdl = crossval (obj) %!demo %! load fisheriris %! x = meas; %! y = species; %! covMatrix = cov (x); %! %! ## Fit the k-NN model using the 'mahalanobis' distance %! ## and the custom covariance matrix %! obj = fitcknn(x, y, 'NumNeighbors', 5, 'Distance','mahalanobis', ... %! 'Cov', covMatrix); %! %! ## Create a partition model using cvpartition %! Partition = cvpartition (size (x, 1), 'kfold', 12); %! %! ## Create cross-validated model using 'cvPartition' name-value argument %! CVMdl = crossval (obj, 'cvPartition', Partition) %! %! ## Access the trained model from first fold of cross-validation %! CVMdl.Trained{1} %!demo %! X = [1, 2; 3, 4; 5, 6]; %! Y = {'A'; 'B'; 'A'}; %! model = fitcknn (X, Y); %! customLossFun = @(C, S, W, Cost) sum (W .* sum (abs (C - S), 2)); %! ## Calculate loss using custom loss function %! L = loss (model, X, Y, 'LossFun', customLossFun) %!demo %! X = [1, 2; 3, 4; 5, 6]; %! Y = {'A'; 'B'; 'A'}; %! model = fitcknn (X, Y); %! ## Calculate loss using 'mincost' loss function %! L = loss (model, X, Y, 'LossFun', 'mincost') %!demo %! X = [1, 2; 3, 4; 5, 6]; %! Y = ['1'; '2'; '3']; %! model = fitcknn (X, Y); %! X_test = [3, 3; 5, 7]; %! Y_test = ['1'; '2']; %! ## Specify custom Weights %! W = [1; 2]; %! L = loss (model, X_test, Y_test, 'LossFun', 'logit', 'Weights', W); %!demo %! load fisheriris %! mdl = fitcknn (meas, species); %! X = mean (meas); %! Y = {'versicolor'}; %! m = margin (mdl, X, Y) %!demo %! X = [1, 2; 4, 5; 7, 8; 3, 2]; %! Y = [2; 1; 3; 2]; %! ## Train the model %! mdl = fitcknn (X, Y); %! ## Specify Vars and Labels %! Vars = 1; %! Labels = 2; %! ## Calculate partialDependence %! [pd, x, y] = partialDependence (mdl, Vars, Labels); %!demo %! X = [1, 2; 4, 5; 7, 8; 3, 2]; %! Y = [2; 1; 3; 2]; %! ## Train the model %! mdl = fitcknn (X, Y); %! ## Specify Vars and Labels %! Vars = 1; %! Labels = 1; %! queryPoints = [linspace(0, 1, 3)', linspace(0, 1, 3)']; %! ## Calculate partialDependence using queryPoints %! [pd, x, y] = partialDependence (mdl, Vars, Labels, 'QueryPoints', ... %! queryPoints) ## Test constructor with NSMethod and NumNeighbors parameters %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y, "NSMethod", "exhaustive"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = ClassificationKNN (x, y, "NumNeighbors" ,k); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = ones (4, 11); %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = ClassificationKNN (x, y, "NumNeighbors" ,k); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = ClassificationKNN (x, y, "NumNeighbors" ,k, "NSMethod", "exhaustive"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = ClassificationKNN (x, y, "NumNeighbors" ,k, "Distance", "hamming"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "hamming"}) %! assert ({a.BucketSize}, {50}) ## Test constructor with Standardize and DistParameter parameters %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! weights = ones (4,1); %! a = ClassificationKNN (x, y, "Standardize", 1); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.Standardize}, {true}) %! assert ({a.Sigma}, {std(x, [], 1)}) %! assert ({a.Mu}, {[3.75, 4.25, 4.75]}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! weights = ones (4,1); %! a = ClassificationKNN (x, y, "Standardize", false); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.Standardize}, {false}) %! assert ({a.Sigma}, {[]}) %! assert ({a.Mu}, {[]}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! s = ones (1, 3); %! a = ClassificationKNN (x, y, "Scale" , s, "Distance", "seuclidean"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.DistParameter}, {s}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "seuclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y, "Exponent" , 5, "Distance", "minkowski"); %! assert (class (a), "ClassificationKNN"); %! assert (a.DistParameter, 5) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "minkowski"}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y, "Exponent" , 5, "Distance", "minkowski", ... %! "NSMethod", "exhaustive"); %! assert (class (a), "ClassificationKNN"); %! assert (a.DistParameter, 5) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "minkowski"}) ## Test constructor with BucketSize and IncludeTies parameters %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y, "BucketSize" , 20, "distance", "mahalanobis"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "mahalanobis"}) %! assert ({a.BucketSize}, {20}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y, "IncludeTies", true); %! assert (class (a), "ClassificationKNN"); %! assert (a.IncludeTies, true); %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y); %! assert (class (a), "ClassificationKNN"); %! assert (a.IncludeTies, false); %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) ## Test constructor with Prior and Cost parameters %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = ClassificationKNN (x, y); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, [0.5; 0.5]) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! prior = [0.5; 0.5]; %! a = ClassificationKNN (x, y, "Prior", "empirical"); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, prior) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "a"; "b"]; %! prior = [0.75; 0.25]; %! a = ClassificationKNN (x, y, "Prior", "empirical"); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, prior) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "a"; "b"]; %! prior = [0.5; 0.5]; %! a = ClassificationKNN (x, y, "Prior", "uniform"); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, prior) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! cost = eye (2); %! a = ClassificationKNN (x, y, "Cost", cost); %! assert (class (a), "ClassificationKNN") %! assert (a.Cost, [1, 0; 0, 1]) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! cost = eye (2); %! a = ClassificationKNN (x, y, "Cost", cost, "Distance", "hamming" ); %! assert (class (a), "ClassificationKNN") %! assert (a.Cost, [1, 0; 0, 1]) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "hamming"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2; 3, 4; 5,6; 5, 8]; %! y = {'9'; '9'; '6'; '7'}; %! a = ClassificationKNN (x, y); %! assert (a.Prior, [0.5; 0.25; 0.25]) ## Test constructor with ClassNames parameter %!test %! load fisheriris %! x = meas; %! y = species; %! ClassNames = {'setosa', 'versicolor', 'virginica'}; %! a = ClassificationKNN (x, y, 'ClassNames', ClassNames); %! assert (a.ClassNames, ClassNames') ## Test input validation for constructor %!error ClassificationKNN () %!error ... %! ClassificationKNN (ones(4, 1)) %!error ... %! ClassificationKNN (ones (4,2), ones (1,4)) %!error ... %! ClassificationKNN (ones (5,3), ones (5,1), "standardize", "a") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "scale", [1 1], "standardize", true) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "PredictorNames", ["A"]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "PredictorNames", "A") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "PredictorNames", {"A", "B", "C"}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "ResponseName", {"Y"}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "ResponseName", 1) %!error ... %! ClassificationKNN (ones(10,2), ones (10,1), "ClassNames", @(x)x) %!error ... %! ClassificationKNN (ones(10,2), ones (10,1), "ClassNames", ['a']) %!error ... %! ClassificationKNN (ones(10,2), ones (10,1), "ClassNames", [1, 2]) %!error ... %! ClassificationKNN (ones(5,2), {'a';'b';'a';'a';'b'}, "ClassNames", {'a','c'}) %!error ... %! ClassificationKNN (ones(10,2), logical (ones (10,1)), "ClassNames", [true, false]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BreakTies", 1) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BreakTies", {"1"}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BreakTies", "some") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Prior", {"1", "2"}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cost", [1, 2]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cost", "string") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cost", {eye(2)}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "NumNeighbors", 0) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "NumNeighbors", 15.2) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "NumNeighbors", "asd") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", "somemetric") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", ... %! @(v,m)sqrt(repmat(v,rows(m),1)-m,2)) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", ... %! @(v,m)sqrt(sum(sumsq(repmat(v,rows(m),1)-m,2)))) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", [1 2 3]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", {"mahalanobis"}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", logical (5)) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "DistanceWeight", @(x)sum(x)) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "DistanceWeight", "text") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "DistanceWeight", [1 2 3]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Scale", "scale") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Scale", {[1 2 3]}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "standardize", true, "scale", [1 1]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cov", ones (2), "Distance", "mahalanobis") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "scale", [1 1], "Cov", ones (2)) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Exponent", 12.5) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Exponent", -3) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Exponent", "three") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Exponent", {3}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "NSMethod", {"kdtree"}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "NSMethod", 3) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "NSMethod", "some") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "IncludeTies", "some") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BucketSize", 42.5) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BucketSize", -50) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BucketSize", "some") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "BucketSize", {50}) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "some", "some") %!error ... %! ClassificationKNN ([1;2;3;'a';4], ones (5,1)) %!error ... %! ClassificationKNN ([1;2;3;Inf;4], ones (5,1)) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Prior", [1 2]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cost", [1 2; 1 3]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Scale", [1 1]) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Scale", [1 1 1], "Distance", "seuclidean") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Scale", [1 -1], "Distance", "seuclidean") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cov", eye (2)) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Cov", eye (3), "Distance", "mahalanobis") %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Exponent", 3) %!error ... %! ClassificationKNN (ones (5,2), ones (5,1), "Distance", "hamming", "NSMethod", "kdtree") ## Test output for predict method %!shared x, y %! load fisheriris %! x = meas; %! y = species; %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 5); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"; "versicolor"; "virginica"}) %! assert (s, [1, 0, 0; 0, 1, 0; 0, 0, 1]) %! assert (c, [0, 1, 1; 1, 0, 1; 1, 1, 0]) %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 5, "Standardize", 1); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"; "versicolor"; "virginica"}) %! assert (s, [0.4, 0.6, 0; 0, 1, 0; 0, 0, 1]) %! assert (c, [0.6, 0.4, 1; 1, 0, 1; 1, 1, 0]) %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "mahalanobis"); %! [l, s, c] = predict (obj, xc); %! assert (s, [0.3, 0.7, 0; 0, 0.9, 0.1; 0.2, 0.2, 0.6], 1e-4) %! assert (c, [0.7, 0.3, 1; 1, 0.1, 0.9; 0.8, 0.8, 0.4], 1e-4) %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "cosine"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"; "versicolor"; "virginica"}) %! assert (s, [1, 0, 0; 0, 1, 0; 0, 0.3, 0.7], 1e-4) %! assert (c, [0, 1, 1; 1, 0, 1; 1, 0.7, 0.3], 1e-4) %!test %! xc = [5.2, 4.1, 1.5, 0.1; 5.1, 3.8, 1.9, 0.4; ... %! 5.1, 3.8, 1.5, 0.3; 4.9, 3.6, 1.4, 0.1]; %! obj = fitcknn (x, y, "NumNeighbors", 5); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"; "setosa"; "setosa"; "setosa"}) %! assert (s, [1, 0, 0; 1, 0, 0; 1, 0, 0; 1, 0, 0]) %! assert (c, [0, 1, 1; 0, 1, 1; 0, 1, 1; 0, 1, 1]) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 5); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0, 0.6, 0.4], 1e-4) %! assert (c, [1, 0.4, 0.6], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "minkowski", "Exponent", 5); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0, 0.5, 0.5], 1e-4) %! assert (c, [1, 0.5, 0.5], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "jaccard"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"}) %! assert (s, [0.9, 0.1, 0], 1e-4) %! assert (c, [0.1, 0.9, 1], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "mahalanobis"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0.1000, 0.5000, 0.4000], 1e-4) %! assert (c, [0.9000, 0.5000, 0.6000], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 5, "distance", "jaccard"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"}) %! assert (s, [0.8, 0.2, 0], 1e-4) %! assert (c, [0.2, 0.8, 1], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 5, "distance", "seuclidean"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0, 1, 0], 1e-4) %! assert (c, [1, 0, 1], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "chebychev"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0, 0.7, 0.3], 1e-4) %! assert (c, [1, 0.3, 0.7], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "cityblock"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0, 0.6, 0.4], 1e-4) %! assert (c, [1, 0.4, 0.6], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "cosine"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"virginica"}) %! assert (s, [0, 0.1, 0.9], 1e-4) %! assert (c, [1, 0.9, 0.1], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "correlation"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"virginica"}) %! assert (s, [0, 0.1, 0.9], 1e-4) %! assert (c, [1, 0.9, 0.1], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 30, "distance", "spearman"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"versicolor"}) %! assert (s, [0, 1, 0], 1e-4) %! assert (c, [1, 0, 1], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 30, "distance", "hamming"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"}) %! assert (s, [0.4333, 0.3333, 0.2333], 1e-4) %! assert (c, [0.5667, 0.6667, 0.7667], 1e-4) %!test %! xc = [5, 3, 5, 1.45]; %! obj = fitcknn (x, y, "NumNeighbors", 5, "distance", "hamming"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"}) %! assert (s, [0.8, 0.2, 0], 1e-4) %! assert (c, [0.2, 0.8, 1], 1e-4) %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "correlation"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa"; "versicolor"; "virginica"}) %! assert (s, [1, 0, 0; 0, 1, 0; 0, 0.4, 0.6], 1e-4) %! assert (c, [0, 1, 1; 1, 0, 1; 1, 0.6, 0.4], 1e-4) %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcknn (x, y, "NumNeighbors", 10, "distance", "hamming"); %! [l, s, c] = predict (obj, xc); %! assert (l, {"setosa";"setosa";"setosa"}) %! assert (s, [0.9, 0.1, 0; 1, 0, 0; 0.5, 0, 0.5], 1e-4) %! assert (c, [0.1, 0.9, 1; 0, 1, 1; 0.5, 1, 0.5], 1e-4) ## Test input validation for predict method %!error ... %! predict (ClassificationKNN (ones (4,2), ones (4,1))) %!error ... %! predict (ClassificationKNN (ones (4,2), ones (4,1)), []) %!error ... %! predict (ClassificationKNN (ones (4,2), ones (4,1)), 1) ## Test output for loss method %!test %! load fisheriris %! model = fitcknn (meas, species, 'NumNeighbors', 5); %! X = mean (meas); %! Y = {'versicolor'}; %! L = loss (model, X, Y); %! assert (L, 0) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = {'A'; 'B'; 'A'}; %! model = fitcknn (X, Y); %! X_test = [1, 6; 3, 3]; %! Y_test = {'A'; 'B'}; %! L = loss (model, X_test, Y_test); %! assert (abs (L - 0.6667) > 1e-5) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = {'A'; 'B'; 'A'}; %! model = fitcknn (X, Y); %! X_with_nan = [1, 2; NaN, 4]; %! Y_test = {'A'; 'B'}; %! L = loss (model, X_with_nan, Y_test); %! assert (abs (L - 0.3333) < 1e-4) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = {'A'; 'B'; 'A'}; %! model = fitcknn (X, Y); %! X_with_nan = [1, 2; NaN, 4]; %! Y_test = {'A'; 'B'}; %! L = loss (model, X_with_nan, Y_test, 'LossFun', 'logit'); %! assert (isnan (L)) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = {'A'; 'B'; 'A'}; %! model = fitcknn (X, Y); %! customLossFun = @(C, S, W, Cost) sum (W .* sum (abs (C - S), 2)); %! L = loss (model, X, Y, 'LossFun', customLossFun); %! assert (L, 0) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = [1; 2; 1]; %! model = fitcknn (X, Y); %! L = loss (model, X, Y, 'LossFun', 'classiferror'); %! assert (L, 0) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = [true; false; true]; %! model = fitcknn (X, Y); %! L = loss (model, X, Y, 'LossFun', 'binodeviance'); %! assert (abs (L - 0.1269) < 1e-4) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = ['1'; '2'; '1']; %! model = fitcknn (X, Y); %! L = loss (model, X, Y, 'LossFun', 'classiferror'); %! assert (L, 0) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = ['1'; '2'; '3']; %! model = fitcknn (X, Y); %! X_test = [3, 3]; %! Y_test = ['1']; %! L = loss (model, X_test, Y_test, 'LossFun', 'quadratic'); %! assert (L, 1) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = ['1'; '2'; '3']; %! model = fitcknn (X, Y); %! X_test = [3, 3; 5, 7]; %! Y_test = ['1'; '2']; %! L = loss (model, X_test, Y_test, 'LossFun', 'classifcost'); %! assert (L, 1) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = ['1'; '2'; '3']; %! model = fitcknn (X, Y); %! X_test = [3, 3; 5, 7]; %! Y_test = ['1'; '2']; %! L = loss (model, X_test, Y_test, 'LossFun', 'hinge'); %! assert (L, 1) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = ['1'; '2'; '3']; %! model = fitcknn (X, Y); %! X_test = [3, 3; 5, 7]; %! Y_test = ['1'; '2']; %! W = [1; 2]; %! L = loss (model, X_test, Y_test, 'LossFun', 'logit', 'Weights', W); %! assert (abs (L - 0.6931) < 1e-4) ## Test input validation for loss method %!error ... %! loss (ClassificationKNN (ones (4,2), ones (4,1))) %!error ... %! loss (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2)) %!error ... %! loss (ClassificationKNN (ones (40,2), randi ([1, 2], 40, 1)), [], zeros (2)) %!error ... %! loss (ClassificationKNN (ones (40,2), randi ([1, 2], 40, 1)), 1, zeros (2)) %!error ... %! loss (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2), ... %! ones (4,1), 'LossFun') %!error ... %! loss (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2), ones (3,1)) %!error ... %! loss (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2), ... %! ones (4,1), 'LossFun', 'a') %!error ... %! loss (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2), ... %! ones (4,1), 'Weights', 'w') ## Test output for margin method %!test %! load fisheriris %! mdl = fitcknn (meas, species, 'NumNeighbors', 5); %! X = mean (meas); %! Y = {'versicolor'}; %! m = margin (mdl, X, Y); %! assert (m, 1) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = [1; 2; 3]; %! mdl = fitcknn (X, Y); %! m = margin (mdl, X, Y); %! assert (m, [1; 1; 1]) %!test %! X = [7, 8; 9, 10]; %! Y = ['1'; '2']; %! mdl = fitcknn (X, Y); %! m = margin (mdl, X, Y); %! assert (m, [1; 1]) %!test %! X = [11, 12]; %! Y = {'1'}; %! mdl = fitcknn (X, Y); %! m = margin (mdl, X, Y); %! assert (isnan (m)) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = [1; 2; 3]; %! mdl = fitcknn (X, Y); %! X1 = [15, 16]; %! Y1 = [1]; %! m = margin (mdl, X1, Y1); %! assert (m, -1) ## Test input validation for margin method %!error ... %! margin (ClassificationKNN (ones (4,2), ones (4,1))) %!error ... %! margin (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2)) %!error ... %! margin (ClassificationKNN (ones (40,2), randi ([1, 2], 40, 1)), [], zeros (2)) %!error ... %! margin (ClassificationKNN (ones (40,2), randi ([1, 2], 40, 1)), 1, zeros (2)) %!error ... %! margin (ClassificationKNN (ones (4,2), ones (4,1)), ones (4,2), ones (3,1)) ## Test output for partialDependence %!shared X, Y, mdl %! X = [1, 2; 4, 5; 7, 8; 3, 2]; %! Y = [2; 1; 3; 2]; %! mdl = fitcknn (X, Y); %!test %! Vars = 1; %! Labels = 2; %! [pd, x, y] = partialDependence (mdl, Vars, Labels); %! pdm = [0.7500, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000]; %! assert (pd, pdm) %!test %! Vars = 1; %! Labels = 2; %! [pd, x, y] = partialDependence (mdl, Vars, Labels, ... %! 'NumObservationsToSample', 5); %! pdm = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; %! assert (abs (pdm - pd) < 1) %!test %! Vars = 1; %! Labels = 2; %! [pd, x, y] = partialDependence (mdl, Vars, Labels, 'UseParallel', true); %! pdm = [0.7500, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000]; %! assert (pd, pdm) %!test %! Vars = [1, 2]; %! Labels = 1; %! queryPoints = {linspace(0, 1, 3)', linspace(0, 1, 3)'}; %! [pd, x, y] = partialDependence (mdl, Vars, Labels, 'QueryPoints', ... %! queryPoints, 'UseParallel', true); %! pdm = [0, 0, 0; 0, 0, 0; 0, 0, 0]; %! assert (pd, pdm) %!test %! Vars = 1; %! Labels = [1; 2]; %! [pd, x, y] = partialDependence (mdl, Vars, Labels); %! pdm = [0.2500, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.2500, 0.2500, 0.2500, ... %! 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, ... %! 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, ... %! 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, ... %! 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, ... %! 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, 0.2500, ... %! 0.2500, 0.2500; 0.7500, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, ... %! 0.5000, 0.5000, 0.5000]; %! assert (pd, pdm) %!test %! Vars = [1, 2]; %! Labels = [1; 2]; %! queryPoints = {linspace(0, 1, 3)', linspace(0, 1, 3)'}; %! [pd, x, y] = partialDependence (mdl, Vars, Labels, 'QueryPoints', queryPoints); %! pdm(:,:,1) = [0, 0, 0; 1, 1, 1]; %! pdm(:,:,2) = [0, 0, 0; 1, 1, 1]; %! pdm(:,:,3) = [0, 0, 0; 1, 1, 1]; %! assert (pd, pdm) %!test %! X1 = [1; 2; 4; 5; 7; 8; 3; 2]; %! X2 = ['2'; '3'; '1'; '3'; '1'; '3'; '2'; '2']; %! X = [X1, double(X2)]; %! Y = [1; 2; 3; 3; 2; 1; 2; 1]; %! mdl = fitcknn (X, Y, 'ClassNames', {'1', '2', '3'}); %! Vars = 1; %! Labels = 1; %! [pd, x, y] = partialDependence (mdl, Vars, Labels); %! pdm = [1.0000, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, ... %! 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, ... %! 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.3750, ... %! 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, ... %! 0.3750, 0.3750, 0.3750, 0.3750, 0.7500, 0.7500, 0.7500, 0.7500, 0.7500, ... %! 0.7500, 0.7500, 0.7500]; %! assert (pd, pdm) %!test %! X1 = [1; 2; 4; 5; 7; 8; 3; 2]; %! X2 = ['2'; '3'; '1'; '3'; '1'; '3'; '2'; '2']; %! X = [X1, double(X2)]; %! Y = [1; 2; 3; 3; 2; 1; 2; 1]; %! predictorNames = {'Feature1', 'Feature2'}; %! mdl = fitcknn (X, Y, 'PredictorNames', predictorNames); %! Vars = 'Feature1'; %! Labels = 1; %! [pd, x, y] = partialDependence (mdl, Vars, Labels); %! pdm = [1.0000, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, ... %! 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, ... %! 0.6250, 0.6250, 0.6250, 0.6250, 0.6250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... %! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.3750, ... %! 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, 0.3750, ... %! 0.3750, 0.3750, 0.3750, 0.3750, 0.7500, 0.7500, 0.7500, 0.7500, 0.7500, ... %! 0.7500, 0.7500, 0.7500]; %! assert (pd, pdm) %!test %! X1 = [1; 2; 4; 5; 7; 8; 3; 2]; %! X2 = ['2'; '3'; '1'; '3'; '1'; '3'; '2'; '2']; %! X = [X1, double(X2)]; %! Y = [1; 2; 3; 3; 2; 1; 2; 1]; %! predictorNames = {'Feature1', 'Feature2'}; %! mdl = fitcknn (X, Y, 'PredictorNames', predictorNames); %! new_X1 = [10; 5; 6; 8; 9; 20; 35; 6]; %! new_X2 = ['2'; '2'; '1'; '2'; '1'; '3'; '3'; '2']; %! new_X = [new_X1, double(new_X2)]; %! Vars = 'Feature1'; %! Labels = 1; %! [pd, x, y] = partialDependence (mdl, Vars, Labels, new_X); %! pdm = [0, 0, 0, 0, 0, 0.2500, 0.2500, 0.2500, 0.2500, 0.7500, 0.7500, ... %! 0.7500, 0.7500, 0.7500, 0.7500, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, ... %! 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, ... %! 1.0000, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... %! 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... %! 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; %! assert (pd, pdm) ## Test input validation for partialDependence method %!error ... %! partialDependence (ClassificationKNN (ones (4,2), ones (4,1))) %!error ... %! partialDependence (ClassificationKNN (ones (4,2), ones (4,1)), 1) %!error ... %! partialDependence (ClassificationKNN (ones (4,2), ones (4,1)), 1, ... %! ones (4,1), 'NumObservationsToSample') %!error ... %! partialDependence (ClassificationKNN (ones (4,2), ones (4,1)), 1, ... %! ones (4,1), 2) ## Test output for crossval method %!shared x, y, obj %! load fisheriris %! x = meas; %! y = species; %! covMatrix = cov (x); %! obj = fitcknn (x, y, 'NumNeighbors', 5, 'Distance', ... %! 'mahalanobis', 'Cov', covMatrix); %!test %! CVMdl = crossval (obj); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 10) %! assert (CVMdl.ModelParameters.NumNeighbors == 5) %! assert (strcmp (CVMdl.ModelParameters.Distance, "mahalanobis")) %! assert (class (CVMdl.Trained{1}), "ClassificationKNN") %! assert (!CVMdl.ModelParameters.Standardize) %!test %! CVMdl = crossval (obj, "KFold", 5); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 5) %! assert (CVMdl.ModelParameters.NumNeighbors == 5) %! assert (strcmp (CVMdl.ModelParameters.Distance, "mahalanobis")) %! assert (class (CVMdl.Trained{1}), "ClassificationKNN") %! assert (CVMdl.ModelParameters.Standardize == obj.Standardize) %!test %! obj = fitcknn (x, y, "NumNeighbors", 5, "Distance", "cityblock"); %! CVMdl = crossval (obj, "HoldOut", 0.2); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.ModelParameters.NumNeighbors == 5) %! assert (strcmp (CVMdl.ModelParameters.Distance, "cityblock")) %! assert (class (CVMdl.Trained{1}), "ClassificationKNN") %! assert (CVMdl.ModelParameters.Standardize == obj.Standardize) %!test %! obj = fitcknn (x, y, "NumNeighbors", 10, "Distance", "cityblock"); %! CVMdl = crossval (obj, "LeaveOut", 'on'); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.ModelParameters.NumNeighbors == 10) %! assert (strcmp (CVMdl.ModelParameters.Distance, "cityblock")) %! assert (class (CVMdl.Trained{1}), "ClassificationKNN") %! assert (CVMdl.ModelParameters.Standardize == obj.Standardize) %!test %! obj = fitcknn (x, y, "NumNeighbors", 10, "Distance", "cityblock"); %! partition = cvpartition (y, 'KFold', 3); %! CVMdl = crossval (obj, 'cvPartition', partition); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert (CVMdl.KFold == 3) %! assert (CVMdl.ModelParameters.NumNeighbors == 10) %! assert (strcmp (CVMdl.ModelParameters.Distance, "cityblock")) %! assert (class (CVMdl.Trained{1}), "ClassificationKNN") %! assert (CVMdl.ModelParameters.Standardize == obj.Standardize) ## Test input validation for crossval method %!error ... %! crossval (ClassificationKNN (ones (4,2), ones (4,1)), "kfold") %!error... %! crossval (ClassificationKNN (ones (4,2), ones (4,1)), "kfold", 12, "holdout", 0.2) %!error ... %! crossval (ClassificationKNN (ones (4,2), ones (4,1)), "kfold", 'a') %!error ... %! crossval (ClassificationKNN (ones (4,2), ones (4,1)), "holdout", 2) %!error ... %! crossval (ClassificationKNN (ones (4,2), ones (4,1)), "leaveout", 1) %!error ... %! crossval (ClassificationKNN (ones (4,2), ones (4,1)), "cvpartition", 1) statistics-release-1.7.3/inst/Classification/ClassificationNeuralNetwork.m000066400000000000000000001251271475240274700271200ustar00rootroot00000000000000## Copyright (C) 2024 Pallav Purbia ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClassificationNeuralNetwork ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} ClassificationNeuralNetwork (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} ClassificationNeuralNetwork (@dots{}, @var{name}, @var{value}) ## ## Create a @qcode{ClassificationNeuralNetwork} class object containing a Neural ## Network classification model. ## ## @code{@var{obj} = ClassificationNeuralNetwork (@var{X}, @var{Y})} returns a ## ClassificationNeuralNetwork object, with @var{X} as the predictor data and ## @var{Y} containing the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or variables. ## @var{X} will be used to train the model. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can contain any type of ## categorical data. @var{Y} must have same numbers of rows as @var{X}. ## @end itemize ## ## @code{@var{obj} = ClassificationNeuralNetwork (@dots{}, @var{name}, ## @var{value})} returns a ClassificationNeuralNetwork object with parameters ## specified by @qcode{Name-Value} pair arguments. Type @code{help fitcnet} ## for more info. ## ## A @qcode{ClassificationNeuralNetwork} object, @var{obj}, stores the labelled ## training data and various parameters for the Neural Network classification ## model, which can be accessed in the following fields: ## ## @multitable @columnfractions 0.23 0.02 0.75 ## @headitem @var{Field} @tab @tab @var{Description} ## ## @item @qcode{X} @tab @tab Unstandardized predictor data, specified as a ## numeric matrix. Each column of @var{X} represents one predictor (variable), ## and each row represents one observation. ## ## @item @qcode{Y} @tab @tab Class labels, specified as a logical or ## numeric vector, or cell array of character vectors. Each value in @var{Y} is ## the observed class label for the corresponding row in @var{X}. ## ## @item @qcode{NumObservations} @tab @tab Number of observations used in ## training the model, specified as a positive integer scalar. This number can ## be less than the number of rows in the training data because rows containing ## @qcode{NaN} values are not part of the fit. ## ## @item @qcode{RowsUsed} @tab @tab Rows of the original training data ## used in fitting the ClassificationNeuralNetwork model, specified as a ## numerical vector. If you want to use this vector for indexing the training ## data in @var{X}, you have to convert it to a logical vector, i.e ## @qcode{X = obj.X(logical (obj.RowsUsed), :);} ## ## @item @qcode{Standardize} @tab @tab A boolean flag indicating whether ## the data in @var{X} have been standardized prior to training. ## ## @item @qcode{Sigma} @tab @tab Predictor standard deviations, specified ## as a numeric vector of the same length as the columns in @var{X}. If the ## predictor variables have not been standardized, then @qcode{"obj.Sigma"} is ## empty. ## ## @item @qcode{Mu} @tab @tab Predictor means, specified as a numeric ## vector of the same length as the columns in @var{X}. If the predictor ## variables have not been standardized, then @qcode{"obj.Mu"} is empty. ## ## @item @qcode{NumPredictors} @tab @tab The number of predictors ## (variables) in @var{X}. ## ## @item @qcode{PredictorNames} @tab @tab Predictor variable names, ## specified as a cell array of character vectors. The variable names are in ## the same order in which they appear in the training data @var{X}. ## ## @item @qcode{ResponseName} @tab @tab Response variable name, specified ## as a character vector. ## ## @item @qcode{ClassNames} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the ClassificationNeuralNetwork model. ## @qcode{ClassNames} are of the same type as the class labels in @var{Y}. ## ## @item @qcode{LayerSizes} @tab @tab Sizes of the fully connected layers ## in the neural network model, returned as a positive integer vector. The ith ## element of LayerSizes is the number of outputs in the ith fully connected ## layer of the neural network model. LayerSizes does not include the size of ## the final fully connected layer. This layer always has K outputs, where K ## is the number of classes in Y. ## ## @item @qcode{Activations} @tab @tab A character vector or a cell array of ## character vector specifying the activation functions used in the hidden ## layers of the neural network. ## ## @item @qcode{OutputLayerActivation} @tab @tab A character vector specifying ## the activation function of the output layer the neural network. ## ## @item @qcode{LearningRate} @tab @tab A positive scalar value defining the ## learning rate used by the gradient descend algorithm during training. ## ## @item @qcode{IterationLimit} @tab @tab A positive scalar value defining ## the number of epochs for training the model. ## ## @item @qcode{DisplayInfo} @tab @tab A boolean flag indicating whether to ## print information during training. ## ## @item @qcode{ModelParameters} @tab @tab A structure containing the ## parameters used to train the Neural Network classifier model containing the ## fields @code{LayerWeights} and @code{Activations} as generated by the ## @code{fcnntrain} function. ## ## @item @qcode{ConvergenceInfo} @tab @tab A structure containing the ## Convergence info of the Neural Network classifier model with the following ## fields: ## ## @multitable @columnfractions 0.05 0.30 0.75 ## @headitem @tab @var{Fields} @tab @var{Description} ## @item @tab @qcode{Accuracy} @tab The prediction accuracy at each ## iteration during the neural network model's training process. ## @item @tab @qcode{TrainingLoss} @tab The loss value recorded at each ## iteration during the neural network model's training process. ## @item @tab @qcode{Time} @tab The cumulative time taken for all iterations, ## measured in seconds. ## @end multitable ## ## @item @qcode{Solver} @tab @tab Solver used to train the neural network ## model, returned as 'Gradient Search'. ## ## @item @qcode{ScoreTransform} @tab @tab A function_handle which is used ## for transforming the Neural Network prediction score into a posterior ## probability. By default, it is @qcode{'none'}, in which case the ## @code{predict} and @code{resubPredict} methods return the prediction scores. ## ## @end multitable ## ## @seealso{fitcnet, fcnntrain, fcnnpredict} ## @end deftypefn properties (Access = public) X = []; # Predictor data Y = []; # Class labels NumObservations = []; # Number of observations in training dataset RowsUsed = []; # Rows used in fitting NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y ScoreTransform = []; # Transformation for classification scores Standardize = []; # Flag to standardize predictors Sigma = []; # Predictor standard deviations Mu = []; # Predictor means LayerSizes = []; # Size of fully connected layers Activations = []; # Activation functions for hidden layers OutputLayerActivation = []; # Activation function for output layer LearningRate = []; # Learning rate for gradient descend IterationLimit = []; # Number of training epochs ModelParameters = []; # Model parameters ConvergenceInfo = []; # Training history DisplayInfo = []; # Display information during training Solver = []; # Solver used endproperties methods (Access = public) ## Class object constructor function this = ClassificationNeuralNetwork (X, Y, varargin) ## Check for sufficient number of input arguments if (nargin < 2) error ("ClassificationNeuralNetwork: too few input arguments."); endif ## Check X and Y have the same number of observations if (rows (X) != rows (Y)) error (strcat (["ClassificationNeuralNetwork: number of rows in X"], ... [" and Y must be equal."])); endif ## Assign original X and Y data to the ClassificationNeuralNetwork object this.X = X; this.Y = Y; ## Get groups in Y [gY, gnY, glY] = grp2idx (Y); ## Set default values before parsing optional parameters Standardize = false; ResponseName = []; PredictorNames = []; ClassNames = []; LayerSizes = 10; Activations = 'sigmoid'; OutputLayerActivation = 'sigmoid'; LearningRate = 0.01; IterationLimit = 1000; DisplayInfo = false; this.ScoreTransform = 'none'; this.Solver = "Gradient Descend"; ## Supported activation functions acList = {"linear", "sigmoid", "relu", "tanh", "softmax", ... "lrelu", "prelu", "elu", "gelu"}; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "standardize" Standardize = varargin{2}; if (! (Standardize == true || Standardize == false)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'Standardize' must be either true or false."])); endif case "predictornames" PredictorNames = varargin{2}; if (! iscellstr (PredictorNames)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'PredictorNames' must be supplied as a"], ... [" cellstring array."])); elseif (columns (PredictorNames) != columns (X)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'PredictorNames' must have the same"], ... [" number of columns as X."])); endif case "responsename" ResponseName = varargin{2}; if (! ischar (ResponseName)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'ResponseName' must be a character vector."])); endif case "classnames" ClassNames = varargin{2}; if (! (iscellstr (ClassNames) || isnumeric (ClassNames) || islogical (ClassNames))) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'ClassNames' must be a cellstring,"], ... [" logical or numeric vector."])); endif ## Check that all class names are available in gnY if (iscellstr (ClassNames)) if (! all (cell2mat (cellfun (@(x) any (strcmp (x, gnY)), ClassNames, "UniformOutput", false)))) error (strcat (["ClassificationNeuralNetwork: not all"], ... [" 'ClassNames' are present in Y."])); endif else if (! all (cell2mat (arrayfun (@(x) any (x == glY), ClassNames, "UniformOutput", false)))) error (strcat (["ClassificationNeuralNetwork: not all"], ... [" 'ClassNames' are present in Y."])); endif endif case "scoretransform" name = "ClassificationNeuralNetwork"; this.ScoreTransform = parseScoreTransform (varargin{2}, name); case 'layersizes' LayerSizes = varargin{2}; if (! (isnumeric(LayerSizes) && isvector(LayerSizes) && all(LayerSizes > 0) && all(mod(LayerSizes, 1) == 0))) error (strcat (["ClassificationNeuralNetwork: 'LayerSizes'"], ... [" must be a positive integer vector."])); endif case 'learningrate' LearningRate = varargin{2}; if (! (isnumeric(LearningRate) && isscalar (LearningRate) && LearningRate > 0)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'LearningRate' must be a positive scalar."])); endif case 'activations' Activations = varargin{2}; if (! (ischar (Activations) || iscellstr (Activations))) error (strcat (["ClassificationNeuralNetwork: 'Activations'"], ... [" must be a character vector or a"], ... [" cellstring vector."])); endif if (ischar (Activations)) if (! any (strcmpi (Activations, acList))) error (strcat (["ClassificationNeuralNetwork: unsupported"], ... [" 'Activation' function."])); endif else if (! all (cell2mat (cellfun (@(x) any (strcmpi (x, acList)), Activations, "UniformOutput", false)))) error (strcat (["ClassificationNeuralNetwork: unsupported"], ... [" 'Activation' functions."])); endif endif Activations = tolower (Activations); case 'outputlayeractivation' OutputLayerActivation = varargin{2}; if (! (ischar (OutputLayerActivation))) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'OutputLayerActivation' must be a"], ... [" character vector."])); endif if (! any (strcmpi (OutputLayerActivation, acList))) error (strcat (["ClassificationNeuralNetwork: unsupported"], ... [" 'OutputLayerActivation' function."])); endif OutputLayerActivation = tolower (OutputLayerActivation); case 'iterationlimit' IterationLimit = varargin{2}; if (! (isnumeric(IterationLimit) && isscalar(IterationLimit) && (IterationLimit > 0) && mod(IterationLimit, 1) == 0)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'IterationLimit' must be a positive"], ... [" integer."])); endif case "displayinfo" DisplayInfo = varargin{2}; if (! (DisplayInfo == true || DisplayInfo == false)) error (strcat (["ClassificationNeuralNetwork:"], ... [" 'DisplayInfo' must be either true or false."])); endif otherwise error (strcat (["ClassificationNeuralNetwork: invalid"],... [" parameter name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Get number of variables in training data ndims_X = columns (X); ## Assign the number of predictors to the object this.NumPredictors = ndims_X; ## Handle class names if (! isempty (ClassNames)) if (iscellstr (ClassNames)) ru = find (! ismember (gnY, ClassNames)); else ru = find (! ismember (glY, ClassNames)); endif for i = 1:numel (ru) gY(gY == ru(i)) = NaN; endfor endif ## Remove missing values from X and Y RowsUsed = ! logical (sum (isnan ([X, gY]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [gY, gnY, glY] = grp2idx (Y); this.ClassNames = unique (Y); # Keep the same type as Y ## Force Y into numeric if (! isnumeric (Y)) Y = gY; endif ## Check X contains valid data if (! (isnumeric (X) && isfinite (X))) error ("ClassificationNeuralNetwork: invalid values in X."); endif ## Assign the number of observations and their correspoding indices ## on the original data, which will be used for training the model, ## to the ClassificationNeuralNetwork object this.NumObservations = rows (X); this.RowsUsed = cast (RowsUsed, "double"); ## Handle Standardize flag if (Standardize) this.Standardize = true; this.Sigma = std (X, [], 1); this.Sigma(this.Sigma == 0) = 1; # predictor is constant this.Mu = mean (X, 1); else this.Standardize = false; this.Sigma = []; this.Mu = []; endif ## Generate default predictors and response variabe names (if necessary) if (isempty (PredictorNames)) for i = 1:ndims_X PredictorNames{i} = strcat ("x", num2str (i)); endfor endif if (isempty (ResponseName)) ResponseName = "Y"; endif ## Assign predictors and response variable names this.PredictorNames = PredictorNames; this.ResponseName = ResponseName; ## Store training parameters this.LayerSizes = LayerSizes; this.Activations = Activations; this.OutputLayerActivation = OutputLayerActivation; this.LearningRate = LearningRate; this.IterationLimit = IterationLimit; this.DisplayInfo = DisplayInfo; ## Encode activations for fcnntrain (expand if needed) nlayers = numel (LayerSizes); if (ischar (Activations)) ActivationCodes = ones (1, nlayers) * activationCode (Activations); elseif (nlayers != numel (Activations)) error (strcat (["ClassificationNeuralNetwork: 'Activations'"], ... [" vector does not match the number of layers."])); else ActivationCodes = []; for i = 1:nlayers code = activationCode (Activations{i}); ActivationCodes = [ActivationCodes, code]; endfor endif code = activationCode (OutputLayerActivation); ActivationCodes = [ActivationCodes, code]; ## Start the training process NumThreads = nproc (); Alpha = 0.01; # used for ReLU and ELU activation layers cnn_timer_ = tic; Mdl = fcnntrain (X, Y, LayerSizes, ActivationCodes, NumThreads, Alpha, ... LearningRate, IterationLimit, DisplayInfo); ## Store training time, Iterations, and Loss ConvergenceInfo.Time = toc (cnn_timer_); ConvergenceInfo.Accuracy = Mdl.Accuracy; ConvergenceInfo.TrainingLoss = Mdl.Loss; ## Remove redundant fields Mdl = rmfield (Mdl, "Accuracy"); Mdl = rmfield (Mdl, "Loss"); ## Save ModelParameters and ConvergenceInfo this.ModelParameters = Mdl; this.ConvergenceInfo = ConvergenceInfo; endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationNeuralNetwork} {@var{labels} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {ClassificationNeuralNetwork} {[@var{labels}, @var{scores}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the Neural Network ## classification object. ## ## @code{@var{labels} = predict (@var{obj}, @var{XC})} returns the vector of ## labels predicted for the corresponding instances in @var{XC}, using the ## trained neural network classification model in @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationNeuralNetwork} class object. ## @item ## @var{X} must be an @math{MxP} numeric matrix with the same number of ## predictors @math{P} as the corresponding predictors of the trained neural ## network model in @var{obj}. ## @end itemize ## ## @code{[@var{labels}, @var{scores}] = predict (@var{obj}, @var{XC}} also ## returns @var{scores}, which represent the probability of each label ## belonging to a specific class. For each observation in X, the predicted ## class label is the one with the highest score among all classes. ## Alternatively, @var{scores} can contain the posterior probabilities if ## the ScoreTransform has been previously set. ## ## @seealso{fitcnet, ClassificationNeuralNetwork} ## @end deftypefn function [labels, scores] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error ("ClassificationNeuralNetwork.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("ClassificationNeuralNetwork.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["ClassificationNeuralNetwork.predict:"], ... [" XC must have the same number of"], ... [" predictors as the trained model."])); endif ## Standardize (if necessary) if (this.Standardize) XC = (XC - this.Mu) ./ this.Sigma; endif ## Predict labels from new data NumThreads = nproc (); [labels, scores] = fcnnpredict (this.ModelParameters, XC, NumThreads); # Get class labels labels = this.ClassNames(labels); if (nargout > 1) ## Apply ScoreTransform to return probability estimates if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["ClassificationNeuralNetwork.predict:"], ... [" 'ScoreTransform' must be a"], ... [" 'function_handle' object."])); endif scores = f (scores); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationNeuralNetwork} {@var{labels} =} resubPredict (@var{obj}) ## @deftypefnx {ClassificationNeuralNetwork} {[@var{labels}, @var{scores}] =} resubPredict (@var{obj}) ## ## Classify the training data using the trained Neural Network ## classification object. ## ## @code{@var{labels} = resubPredict (@var{obj})} returns the vector of ## labels predicted for the corresponding instances in the training data, ## using the predictor data in @code{obj.X} and corresponding labels, ## @code{obj.Y}, stored in the Neural Network classification model, ## @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationNeuralNetwork} class object. ## @end itemize ## ## @code{[@var{labels}, @var{scores}] = resubPredict (@var{obj}, @var{XC})} ## also returns @var{scores}, which represent the probability of each label ## belonging to a specific class. For each observation in X, the predicted ## class label is the one with the highest score among all classes. ## Alternatively, @var{scores} can contain the posterior probabilities if ## the ScoreTransform has been previously set. ## ## @seealso{fitcnet, ClassificationNeuralNetwork} ## @end deftypefn function [labels, scores] = resubPredict (this) ## Get used rows (if necessary) if (sum (this.RowsUsed) != rows (this.X)) RowsUsed = logical (this.RowsUsed); X = this.X(RowsUsed); else X = this.X; endif ## Standardize (if necessary) if (this.Standardize) X = (X - this.Mu) ./ this.Sigma; endif ## Predict labels from existing data NumThreads = nproc (); [labels, scores] = fcnnpredict (this.ModelParameters, X, NumThreads); # Get class labels labels = this.ClassNames(labels); if (nargout > 1) ## Apply ScoreTransform to return probability estimates if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["ClassificationNeuralNetwork.resubPredict:"], ... [" 'ScoreTransform' must be a"], ... [" 'function_handle' object."])); endif scores = f (scores); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationNeuralNetwork} {@var{CVMdl} =} crossval (@var{obj}) ## @deftypefnx {ClassificationNeuralNetwork} {@var{CVMdl} =} crossval (@dots{}, @var{Name}, @var{Value}) ## ## Cross Validate a Neural Network classification object. ## ## @code{@var{CVMdl} = crossval (@var{obj})} returns a cross-validated model ## object, @var{CVMdl}, from a trained model, @var{obj}, using 10-fold ## cross-validation by default. ## ## @code{@var{CVMdl} = crossval (@var{obj}, @var{name}, @var{value})} ## specifies additional name-value pair arguments to customize the ## cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"KFold"} @tab @tab Specify the number of folds to use in ## k-fold cross-validation. @code{"KFold", @var{k}}, where @var{k} is an ## integer greater than 1. ## ## @item @qcode{"Holdout"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{"Holdout", @var{p}}, where @var{p} is a ## scalar in the range @math{(0,1)}. ## ## @item @qcode{"Leaveout"} @tab @tab Specify whether to perform ## leave-one-out cross-validation. @code{"Leaveout", @var{Value}}, where ## @var{Value} is 'on' or 'off'. ## ## @item @qcode{"CVPartition"} @tab @tab Specify a @qcode{cvpartition} ## object used for cross-validation. @code{"CVPartition", @var{cv}}, where ## @code{isa (@var{cv}, "cvpartition")} = 1. ## ## @end multitable ## ## @seealso{fitcnet, ClassificationNeuralNetwork, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function CVMdl = crossval (this, varargin) ## Check for sufficient input arguments if (nargin < 1) error ("ClassificationNeuralNetwork.crossval: too few input arguments."); endif if (numel (varargin) == 1) error (strcat (["ClassificationNeuralNetwork.crossval: Name-Value"], ... [" arguments must be in pairs."])); elseif (numel (varargin) > 2) error (strcat (["ClassificationNeuralNetwork.crossval: specify"], ... [" only one of the optional Name-Value paired"], ... [" arguments."])); endif ## Add default values numFolds = 10; Holdout = []; Leaveout = 'off'; CVPartition = []; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case 'kfold' numFolds = varargin{2}; if (! (isnumeric (numFolds) && isscalar (numFolds) && (numFolds == fix (numFolds)) && numFolds > 1)) error (strcat (["ClassificationNeuralNetwork.crossval:"], ... [" 'KFold' must be an integer value greater"], ... [" than 1."])); endif case 'holdout' Holdout = varargin{2}; if (! (isnumeric (Holdout) && isscalar (Holdout) && Holdout > 0 && Holdout < 1)) error (strcat (["ClassificationNeuralNetwork.crossval:"], ... [" 'Holdout' must be a numeric value between"], ... [" 0 and 1."])); endif case 'leaveout' Leaveout = varargin{2}; if (! (ischar (Leaveout) && (strcmpi (Leaveout, 'on') || strcmpi (Leaveout, 'off')))) error (strcat (["ClassificationNeuralNetwork.crossval:"], ... [" 'Leaveout' must be either 'on' or 'off'."])); endif case 'cvpartition' CVPartition = varargin{2}; if (!(isa (CVPartition, 'cvpartition'))) error (strcat (["ClassificationNeuralNetwork.crossval:"], ... [" 'CVPartition' must be a 'cvpartition'"], ... [" object."])); endif otherwise error (strcat (["ClassificationNeuralNetwork.crossval: invalid"],... [" parameter name in optional paired arguments."])); endswitch varargin (1:2) = []; endwhile ## Determine the cross-validation method to use if (! isempty (CVPartition)) partition = CVPartition; elseif (! isempty (Holdout)) partition = cvpartition (this.Y, 'Holdout', Holdout); elseif (strcmpi (Leaveout, 'on')) partition = cvpartition (this.Y, 'LeaveOut'); else partition = cvpartition (this.Y, 'KFold', numFolds); endif ## Create a cross-validated model object CVMdl = ClassificationPartitionedModel (this, partition); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationNeuralNetwork} {@var{CVMdl} =} compact (@var{obj}) ## ## Create a CompactClassificationNeuralNetwork object. ## ## @code{@var{CVMdl} = compact (@var{obj})} creates a compact version of the ## ClassificationNeuralNetwork object, @var{obj}. ## ## @seealso{fitcnet, ClassificationNeuralNetwork, ## CompactClassificationNeuralNetwork} ## @end deftypefn function CVMdl = compact (this) ## Greate a compact model CVMdl = CompactClassificationNeuralNetwork (this); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationNeuralNetwork} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationNeuralNetwork object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ## ClassificationNeuralNetwork object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcnet, ClassificationNeuralNetwork, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "ClassificationNeuralNetwork"; ## Create variables from model properties X = this.X; Y = this.Y; NumObservations = this.NumObservations; RowsUsed = this.RowsUsed; NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; ScoreTransform = this.ScoreTransform; Standardize = this.Standardize; Sigma = this.Sigma; Mu = this.Mu; LayerSizes = this.LayerSizes; Activations = this.Activations; OutputLayerActivation = this.OutputLayerActivation; LearningRate = this.LearningRate; IterationLimit = this.IterationLimit; ModelParameters = this.ModelParameters; ConvergenceInfo = this.ConvergenceInfo; DislayInfo = this.DislayInfo; Solver = this.Solver; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "X", "Y", "NumObservations", "RowsUsed", ... "NumPredictors", "PredictorNames", "ResponseName", "ClassNames", ... "ScoreTransform", "Standardize", "Sigma", "Mu", "LayerSizes", ... "Activations", "OutputLayerActivation", "LearningRate", ... "IterationLimit", "Solver", "ModelParameters", ... "ConvergenceInfo", "DislayInfo"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationNeuralNetwork object mdl = ClassificationNeuralNetwork (1, 1); ## Get fieldnames from DATA (including private properties) names = fieldnames (data); ## Copy data into object for i = 1:numel (names) ## Check fieldnames in DATA match properties in ClassificationNeuralNetwork try mdl.(names{i}) = data.(names{i}); catch error (strcat (["ClassificationNeuralNetwork.load_model:"], ... [" invalid model in '%s'."]), filename) end_try_catch endfor endfunction endmethods endclassdef function numCode = activationCode (strCode) switch (strCode) case "linear" numCode = 0; case "sigmoid" numCode = 1; case "relu" numCode = 2; case "tanh" numCode = 3; case "softmax" numCode = 4; case {"lrelu", "prelu"} numCode = 5; case "elu" numCode = 6; case "gelu" numCode = 7; endswitch endfunction ## Test input validation for constructor %!error ... %! ClassificationNeuralNetwork () %!error ... %! ClassificationNeuralNetwork (ones(10,2)) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones (5,1)) %!error ... %! ClassificationNeuralNetwork (ones (5,3), ones (5,1), "standardize", "a") %!error ... %! ClassificationNeuralNetwork (ones (5,2), ones (5,1), "PredictorNames", ["A"]) %!error ... %! ClassificationNeuralNetwork (ones (5,2), ones (5,1), "PredictorNames", "A") %!error ... %! ClassificationNeuralNetwork (ones (5,2), ones (5,1), "PredictorNames", {"A", "B", "C"}) %!error ... %! ClassificationNeuralNetwork (ones (5,2), ones (5,1), "ResponseName", {"Y"}) %!error ... %! ClassificationNeuralNetwork (ones (5,2), ones (5,1), "ResponseName", 1) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones (10,1), "ClassNames", @(x)x) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones (10,1), "ClassNames", ['a']) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones (10,1), "ClassNames", [1, 2]) %!error ... %! ClassificationNeuralNetwork (ones(5,2), {'a';'b';'a';'a';'b'}, "ClassNames", {'a','c'}) %!error ... %! ClassificationNeuralNetwork (ones(10,2), logical (ones (10,1)), "ClassNames", [true, false]) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LayerSizes", -1) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LayerSizes", 0.5) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LayerSizes", [1,-2]) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LayerSizes", [10,20,30.5]) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LearningRate", -0.1) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LearningRate", [0.1, 0.01]) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LearningRate", "a") %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "Activations", 123) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "Activations", "unsupported_type") %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "LayerSizes", [10, 5], ... %! "Activations", {"sigmoid", "unsupported_type"}) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "Activations", {"sigmoid", "relu", "softmax"}) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "OutputLayerActivation", 123) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "OutputLayerActivation", "unsupported_type") %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "IterationLimit", -1) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "IterationLimit", 0.5) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "IterationLimit", [1,2]) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "ScoreTransform", [1,2]) %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "ScoreTransform", "unsupported_type") %!error ... %! ClassificationNeuralNetwork (ones(10,2), ones(10,1), "some", "some") %!error ... %! ClassificationNeuralNetwork ([1;2;3;'a';4], ones (5,1)) %!error ... %! ClassificationNeuralNetwork ([1;2;3;Inf;4], ones (5,1)) ## Test output for predict method %!shared x, y, objST, Mdl %! load fisheriris %! x = meas; %! y = grp2idx (species); %! Mdl = fitcnet (x, y, "IterationLimit", 100); ## Test input validation for predict method %!error ... %! predict (Mdl) %!error ... %! predict (Mdl, []) %!error ... %! predict (Mdl, 1) %!test %! objST = fitcnet (x, y, "IterationLimit", 100); %! objST.ScoreTransform = "a"; %!error ... %! [labels, scores] = predict (objST, x); ## Test input validation for resubPredict method %!error ... %! [labels, scores] = resubPredict (objST); ## Test output for crossval method %!test %! CVMdl = crossval (Mdl, "KFold", 5); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 5) %! assert (class (CVMdl.Trained{1}), "CompactClassificationNeuralNetwork") %! assert (CVMdl.CrossValidatedModel, "ClassificationNeuralNetwork") %!test %! CVMdl = crossval (Mdl, "HoldOut", 0.2); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationNeuralNetwork") %! assert (CVMdl.CrossValidatedModel, "ClassificationNeuralNetwork") ## Test input validation for crossval method %!error ... %! crossval (Mdl, "KFold") %!error ... %! crossval (Mdl, "KFold", 5, "leaveout", 'on') %!error ... %! crossval (Mdl, "KFold", 'a') %!error ... %! crossval (Mdl, "KFold", 1) %!error ... %! crossval (Mdl, "KFold", -1) %!error ... %! crossval (Mdl, "KFold", 11.5) %!error ... %! crossval (Mdl, "KFold", [1,2]) %!error ... %! crossval (Mdl, "Holdout", 'a') %!error ... %! crossval (Mdl, "Holdout", 11.5) %!error ... %! crossval (Mdl, "Holdout", -1) %!error ... %! crossval (Mdl, "Holdout", 0) %!error ... %! crossval (Mdl, "Holdout", 1) %!error ... %! crossval (Mdl, "Leaveout", 1) %!error ... %! crossval (Mdl, "CVPartition", 1) %!error ... %! crossval (Mdl, "CVPartition", 'a') %!error ... %! crossval (Mdl, "some", "some") statistics-release-1.7.3/inst/Classification/ClassificationPartitionedModel.m000066400000000000000000000735671475240274700275750ustar00rootroot00000000000000## Copyright (C) 2024 Ruchika Sonagote ## Copyright (C) 2024 Pallav Purbia ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClassificationPartitionedModel ## -*- texinfo -*- ## @deftypefn {statistics} {@var{CVMdl} =} ClassificationPartitionedModel (@var{Mdl}, @var{Partition}) ## ## Create a @qcode{ClassificationPartitionedModel} class for cross-validation ## of classification models. ## ## @code{@var{CVMdl} = ClassificationPartitionedModel (@var{Mdl}, ## @var{Partition})} returns a ClassificationPartitionedModel object, with ## @var{Mdl} as the trained ClassificationKNN or ClassificationSVM object and ## @var{Partition} as the partitioning object obtained using cvpartition ## function. ## ## A @qcode{ClassificationPartitionedModel} object, @var{CVMdl}, stores the ## classification models trained on cross-validated folds ## and various parameters for the cross-validated model, ## which can be accessed in the following fields: ## ## @multitable @columnfractions 0.23 0.02 0.75 ## @headitem @var{Field} @tab @tab @var{Description} ## ## @item @qcode{X} @tab @tab Unstandardized predictor data, specified as a ## numeric matrix. Each column of @var{X} represents one predictor (variable), ## and each row represents one observation. ## ## @item @qcode{Y} @tab @tab Class labels, specified as a logical or ## numeric vector, or cell array of character vectors. Each value in @var{Y} is ## the observed class label for the corresponding row in @var{X}. ## ## @item @qcode{ClassNames} @tab @tab Names of the classes in the training ## data @var{Y} with duplicates removed, specified as a cell array of character ## vectors. ## ## @item @qcode{Cost} @tab @tab Cost of the misclassification of a point, ## specified as a square matrix. @qcode{Cost(i,j)} is the cost of classifying a ## point into class @qcode{j} if its true class is @qcode{i} (that is, the rows ## correspond to the true class and the columns correspond to the predicted ## class). The order of the rows and columns in @qcode{Cost} corresponds to the ## order of the classes in @qcode{ClassNames}. The number of rows and columns ## in @qcode{Cost} is the number of unique classes in the response. By default, ## @qcode{Cost(i,j) = 1} if @qcode{i != j}, and @qcode{Cost(i,j) = 0} if ## @qcode{i = j}. In other words, the cost is 0 for correct classification and ## 1 for incorrect classification. ## ## @item @qcode{CrossValidatedModel} @tab @tab Class of the ## cross-validated model, specified as a character vector. This field ## contains the type of model that was ## used for the training, e.g., @qcode{"ClassificationKNN"}. ## ## @item @qcode{KFold} @tab @tab Number of cross-validated folds, ## specified as a positive interger scalar. Represents how many folds the ## data was divided into for cross-validation purposes. ## ## @item @qcode{ModelParameters} @tab @tab Model parameters used during ## training, specified as a structure. This includes any model-specific ## parameters that were configured prior to training, such as ## @qcode{NumNeighbors} or @qcode{Distance} in the case of a KNN model. ## ## @item @qcode{NumObservations} @tab @tab Number of observations used in ## training the ClassificationKNN model, specified as a positive integer scalar. ## This number can be less than the number of rows in the training data because ## rows containing @qcode{NaN} values are not part of the fit. ## ## @item @qcode{Partition} @tab @tab Partition configuration used for ## cross-validation, specified as a cvpartition object. This field stores the ## cvpartition instance that describes how the data was split into training and ## validation sets. ## ## @item @qcode{PredictorNames} @tab @tab Predictor variable names, ## specified as a cell array of character vectors. The variable names are in ## the same order in which they appear in the training data @var{X}. ## ## @item @qcode{Prior} @tab @tab Prior probabilities for each class, ## specified as a numeric vector. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{ResponseName} @tab @tab Response variable name, specified ## as a character vector. ## ## @item @qcode{Trained} @tab @tab Models trained on each fold, ## specified as a cell array of models. Each cell contains a model trained on ## the minus-one fold of the data (all but one fold used for training and the ## remaining fold used for validation). ## ## @end multitable ## ## @seealso{cvpartition, ClassificationDiscriminant, ClassificationGAM, ## ClassificationKNN, ClassificationNeuralNetwork, ClassificationSVM} ## @end deftypefn properties BinEdges = []; CategoricalPredictors = []; X = []; Y = []; ClassNames = []; Cost = []; CrossValidatedModel = []; KFold = []; ModelParameters = []; NumObservations = []; Partition = []; PredictorNames = []; Prior = []; ResponseName = []; ScoreTransform = []; Standardize = []; Trained = []; endproperties methods (Access = public) ## Constructor to initialize the partitioned model function this = ClassificationPartitionedModel (Mdl, Partition) ## Check input arguments if (nargin < 2) error ("ClassificationPartitionedModel: too few input arguments."); endif ## Check for valid Classification object validTypes = {'ClassificationDiscriminant', 'ClassificationGAM', ... 'ClassificationKNN', 'ClassificationNeuralNetwork', ... 'ClassificationSVM'}; if (! any (strcmp (class (Mdl), validTypes))) error ("ClassificationPartitionedModel: unsupported model type."); endif ## Check for valid cvpartition object if (! strcmp (class (Partition), "cvpartition")) error ("ClassificationPartitionedModel: invalid 'cvpartition' object."); endif ## Set properties this.X = Mdl.X; this.Y = Mdl.Y; this.KFold = get (Partition, "NumTestSets"); this.Trained = cell (this.KFold, 1); this.ClassNames = Mdl.ClassNames; this.ResponseName = Mdl.ResponseName; this.NumObservations = Mdl.NumObservations; this.PredictorNames = Mdl.PredictorNames; this.Partition = Partition; this.CrossValidatedModel = class (Mdl); this.ScoreTransform = Mdl.ScoreTransform; if (! strcmpi (class (Mdl), 'ClassificationNeuralNetwork')) this.Prior = Mdl.Prior; this.Cost = Mdl.Cost; endif is_valid = {'ClassificationKNN', 'ClassificationNeuralNetwork', ... 'ClassificationSVM'}; if (any (strcmpi (class (Mdl), is_valid))) this.Standardize = Mdl.Standardize; endif ## Switch Classification object types switch (this.CrossValidatedModel) case "ClassificationDiscriminant" ## Arguments to pass in fitcdiscr args = {}; ## List of acceptable parameters for fitcdiscr DiscrParams = {'PredictorNames', 'ResponseName', 'ClassNames', ... 'Cost', 'DiscrimType', 'Gamma'}; ## Set parameters for i = 1:numel (DiscrParams) paramName = DiscrParams{i}; paramValue = Mdl.(paramName); if (! isempty (paramValue)) args = [args, {paramName, paramValue}]; endif endfor ## Add 'FillCoeffs' parameter if (isempty (Mdl.Coeffs)) args = [args, {'FillCoeffs', 'off'}]; endif ## Train model according to partition object for k = 1:this.KFold idx = training (this.Partition, k); tmp = fitcdiscr (this.X(idx, :), this.Y(idx), args{:}); this.Trained{k} = compact (tmp); endfor ## Store ModelParameters to ClassificationPartitionedModel object params = struct(); paramList = {'DiscrimType', 'FillCoeffs', 'Gamma'}; for i = 1:numel (paramList) paramName = paramList{i}; if (isprop (Mdl, paramName)) params.(paramName) = Mdl.(paramName); endif endfor this.ModelParameters = params; case "ClassificationGAM" ## Arguments to pass in fitcgam args = {}; ## List of acceptable parameters for fitcdiscr GAMparams = {'PredictorNames', 'ResponseName', 'ClassNames', ... 'Cost', 'Formula', 'Interactions', 'Knots', 'Order', ... 'LearningRate', 'NumIterations'}; ## Set parameters for i = 1:numel (GAMparams) paramName = GAMparams{i}; paramValue = Mdl.(paramName); if (! isempty (paramValue)) args = [args, {paramName, paramValue}]; endif endfor ## Train model according to partition object for k = 1:this.KFold idx = training (this.Partition, k); tmp = fitcgam (this.X(idx, :), this.Y(idx), args{:}); this.Trained{k} = compact (tmp); endfor ## Store ModelParameters to ClassificationPartitionedModel object params = struct(); paramList = {'Formula', 'Interactions', 'Knots', 'Order', 'DoF', ... 'LearningRate', 'NumIterations'}; for i = 1:numel (paramList) paramName = paramList{i}; if (isprop (Mdl, paramName)) params.(paramName) = Mdl.(paramName); endif endfor this.ModelParameters = params; case 'ClassificationKNN' ## Arguments to pass in fitcknn args = {}; ## List of acceptable parameters for fitcknn KNNparams = {'PredictorNames', 'ResponseName', 'ClassNames', ... 'Prior', 'Cost', 'ScoreTransform', 'BreakTies', ... 'NSMethod', 'BucketSize', 'NumNeighbors', 'Exponent', ... 'Scale', 'Cov', 'Distance', 'DistanceWeight', ... 'IncludeTies'}; ## Set parameters for i = 1:numel (KNNparams) paramName = KNNparams{i}; if (isprop (Mdl, paramName)) paramValue = Mdl.(paramName); if (! isempty (paramValue)) args = [args, {paramName, paramValue}]; endif else switch (paramName) case 'Cov' if (strcmpi (Mdl.Distance, 'mahalanobis') && ... (! isempty (Mdl.DistParameter))) args = [args, {'Cov', Mdl.DistParameter}]; endif case 'Exponent' if (strcmpi (Mdl.Distance,'minkowski') && ... (! isempty (Mdl.DistParameter))) args = [args, {'Exponent', Mdl.DistParameter}]; endif case 'Scale' if (strcmpi (Mdl.Distance,'seuclidean') && ... (! isempty (Mdl.DistParameter))) args = [args, {'Scale', Mdl.DistParameter}]; endif endswitch endif endfor ## Train model according to partition object for k = 1:this.KFold idx = training (this.Partition, k); this.Trained{k} = fitcknn (this.X(idx, :), this.Y(idx), args{:}); endfor ## Store ModelParameters to ClassificationPartitionedModel object params = struct(); paramList = {'NumNeighbors', 'Distance', 'DistParameter', ... 'NSMethod', 'DistanceWeight', 'Standardize'}; for i = 1:numel (paramList) paramName = paramList{i}; if (isprop (Mdl, paramName)) params.(paramName) = Mdl.(paramName); endif endfor this.ModelParameters = params; case 'ClassificationNeuralNetwork' ## Arguments to pass in fitcnet args = {}; ## List of acceptable parameters for fitcnet NNparams = {'PredictorNames', 'ResponseName', 'ClassNames', ... 'ScoreTransform', 'Standardize', 'LayerSizes', ... 'Activations', 'OutputLayerActivation', ... 'LearningRate', 'IterationLimit', 'DisplayInfo'}; ## Set parameters for i = 1:numel (NNparams) paramName = NNparams{i}; paramValue = Mdl.(paramName); if (! isempty (paramValue)) args = [args, {paramName, paramValue}]; endif endfor ## Train model according to partition object for k = 1:this.KFold idx = training (this.Partition, k); tmp = fitcnet (this.X(idx, :), this.Y(idx), args{:}); this.Trained{k} = compact (tmp); endfor ## Store ModelParameters to ClassificationPartitionedModel object params = struct(); paramList = {'LayerSizes', 'Activations', 'OutputLayerActivation', ... 'LearningRate', 'IterationLimit', 'Solver'}; for i = 1:numel (paramList) paramName = paramList{i}; if (isprop (Mdl, paramName)) params.(paramName) = Mdl.(paramName); endif endfor this.ModelParameters = params; case 'ClassificationSVM' ## Get ModelParameters structure from ClassificationKNN object params = Mdl.ModelParameters; ## Train model according to partition object for k = 1:this.KFold idx = training (this.Partition, k); ## Pass all arguments directly to fitcsvm tmp = fitcsvm (this.X(idx, :), this.Y(idx), ... 'Standardize', Mdl.Standardize, ... 'PredictorNames', Mdl.PredictorNames, ... 'ResponseName', Mdl.ResponseName, ... 'ClassNames', Mdl.ClassNames, ... 'Prior', Mdl.Prior, ... 'Cost', Mdl.Cost, ... 'SVMtype', params.SVMtype, ... 'KernelFunction', params.KernelFunction, ... 'PolynomialOrder', params.PolynomialOrder, ... 'KernelScale', params.KernelScale, ... 'KernelOffset', params.KernelOffset, ... 'BoxConstraint', params.BoxConstraint, ... 'Nu', params.Nu, ... 'CacheSize', params.CacheSize, ... 'Tolerance', params.Tolerance, ... 'Shrinking', params.Shrinking); this.Trained{k} = compact (tmp); endfor ## Store ModelParameters to ClassificationPartitionedModel object this.ModelParameters = params; endswitch endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationPartitionedModel} {@var{label} =} kfoldPredict (@var{CVMdl}) ## @deftypefnx {ClassificationPartitionedModel} {[@var{label}, @var{score}, @var{cost}] =} kfoldPredict (@var{CVMdl}) ## ## Predict responses for observations not used for training in a ## cross-validated classification model. ## ## @code{@var{[label, Score, Cost]} = kfoldPredict (@var{CVMdl})} ## returns the predicted class labels, classification scores, and ## classification costs for the data used ## to train the cross-validated model @var{CVMdl}. ## ## @var{CVMdl} is a @code{ClassificationPartitionedModel} object. ## The function predicts the response for each observation that was ## held out during training in the cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Output} @tab @tab @var{Description} ## ## @item @qcode{label} @tab @tab Predicted class labels, returned as a ## vector or cell array. The type of @var{label} matches the type of ## @var{Y} in the original training data. Each element of @var{label} ## corresponds to the predicted class ## label for the corresponding row in @var{X}. ## ## @item @qcode{Score} @tab @tab Classification scores, returned as a ## numeric matrix. Each row of @var{Score} corresponds to an observation, ## and each column corresponds to a class. The value in row @var{i} and ## column @var{j} is the ## classification score for class @var{j} for observation @var{i}. ## ## @item @qcode{Cost} @tab @tab Classification costs, returned as a ## numeric matrix. Each row of @var{Cost} corresponds to an observation, ## and each column corresponds to a class. The value in row @var{i} ## and column @var{j} is the classification cost for class @var{j} for ## observation @var{i}. This output is ## optional and only returned if requested. ## ## @end multitable ## ## @seealso{ClassificationKNN, ClassificationSVM, ## ClassificationPartitionedModel} ## @end deftypefn function [label, Score, Cost] = kfoldPredict (this) ## Input validation no_cost_models = {'ClassificationNeuralNetwork', 'ClassificationSVM'}; no_cost = any (strcmp (this.CrossValidatedModel, no_cost_models)); if (no_cost && nargout > 2) error (strcat (["ClassificationPartitionedModel.kfoldPredict:"], ... [" 'Cost' output is not supported for %s cross"], ... [" validated models."]), this.CrossValidatedModel); endif ## Initialize the label vector based on the type of Y if (iscellstr (this.Y)) label = cell (this.NumObservations, 1); elseif (islogical (this.Y)) label = false (this.NumObservations, 1); elseif (isnumeric (this.Y)) label = zeros (this.NumObservations, 1); elseif (ischar (this.Y)) label = char (zeros (this.NumObservations, size (this.Y, 2))); endif ## Initialize the score and cost matrices Score = nan (this.NumObservations, numel (this.ClassNames)); Cost = nan (this.NumObservations, numel (this.ClassNames)); ## Predict label, score, and cost (if applicable) for each KFold partition for k = 1:this.KFold ## Get data and trained model for this fold testIdx = test (this.Partition, k); model = this.Trained{k}; ## Train if (no_cost) [predictedLabel, score] = predict (model, this.X(testIdx, :)); else [predictedLabel, score, cost] = predict (model, this.X(testIdx, :)); endif ## Convert cell array of labels to appropriate type (if applicable) if (iscell (predictedLabel)) if (isnumeric (this.Y)) predictedLabel = cellfun (@str2num, predictedLabel); elseif (islogical (this.Y)) predictedLabel = cellfun (@logical, predictedLabel); elseif (iscellstr (this.Y)) predictedLabel = predictedLabel; endif endif ## Get labels, score, and cost (if applicable) label(testIdx) = predictedLabel; Score(testIdx, :) = score; if (nargout > 2) Cost(testIdx, :) = cost; endif endfor ## Handle single fold case (holdout) if (this.KFold == 1) testIdx = test (this.Partition, 1); label(testIdx) = mode (this.Y); Score(testIdx, :) = NaN; Cost(testIdx, :) = NaN; return; endif endfunction endmethods endclassdef %!demo %! %! load fisheriris %! x = meas; %! y = species; %! %! ## Create a KNN classifier model %! obj = fitcknn (x, y, "NumNeighbors", 5, "Standardize", 1); %! %! ## Create a partition for 5-fold cross-validation %! partition = cvpartition (y, "KFold", 5); %! %! ## Create the ClassificationPartitionedModel object %! cvModel = crossval (obj, 'cvPartition', partition) %!demo %! %! load fisheriris %! x = meas; %! y = species; %! %! ## Create a KNN classifier model %! obj = fitcknn (x, y, "NumNeighbors", 5, "Standardize", 1); %! %! ## Create the ClassificationPartitionedModel object %! cvModel = crossval (obj); %! %! ## Predict the class labels for the observations not used for training %! [label, score, cost] = kfoldPredict (cvModel); %! fprintf ("Cross-validated accuracy = %1.2f%% (%d/%d)\n", ... %! sum (strcmp (label, y)) / numel (y) *100, ... %! sum (strcmp (label, y)), numel (y)) ## Tests %!test %! load fisheriris %! a = fitcdiscr (meas, species, "gamma", 0.3); %! cvModel = crossval (a, "KFold", 5); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert (cvModel.NumObservations, 150); %! assert (numel (cvModel.Trained), 5); %! assert (class (cvModel.Trained{1}), "CompactClassificationDiscriminant"); %! assert (cvModel.CrossValidatedModel, "ClassificationDiscriminant"); %! assert (cvModel.KFold, 5); %!test %! load fisheriris %! a = fitcdiscr (meas, species, "gamma", 0.5, "fillcoeffs", "off"); %! cvModel = crossval (a, "HoldOut", 0.3); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert ({cvModel.X, cvModel.Y}, {meas, species}); %! assert (cvModel.NumObservations, 150); %! assert (numel (cvModel.Trained), 1); %! assert (class (cvModel.Trained{1}), "CompactClassificationDiscriminant"); %! assert (cvModel.CrossValidatedModel, "ClassificationDiscriminant"); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcgam (x, y, "Interactions", "all"); %! cvModel = crossval (a, "KFold", 5); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert (cvModel.NumObservations, 4); %! assert (numel (cvModel.Trained), 5); %! assert (class (cvModel.Trained{1}), "CompactClassificationGAM"); %! assert (cvModel.CrossValidatedModel, "ClassificationGAM"); %! assert (cvModel.KFold, 5); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcgam (x, y); %! cvModel = crossval (a, "LeaveOut", "on"); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert ({cvModel.X, cvModel.Y}, {x, y}); %! assert (cvModel.NumObservations, 4); %! assert (numel (cvModel.Trained), 4); %! assert (class (cvModel.Trained{1}), "CompactClassificationGAM"); %! assert (cvModel.CrossValidatedModel, "ClassificationGAM"); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y); %! partition = cvpartition (y, "KFold", 5); %! cvModel = ClassificationPartitionedModel (a, partition); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert (class (cvModel.Trained{1}), "ClassificationKNN"); %! assert (cvModel.NumObservations, 4); %! assert (cvModel.ModelParameters.NumNeighbors, 1); %! assert (cvModel.ModelParameters.NSMethod, "kdtree"); %! assert (cvModel.ModelParameters.Distance, "euclidean"); %! assert (! cvModel.ModelParameters.Standardize); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "NSMethod", "exhaustive"); %! partition = cvpartition (y, "HoldOut", 0.2); %! cvModel = ClassificationPartitionedModel (a, partition); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert (class (cvModel.Trained{1}), "ClassificationKNN"); %! assert ({cvModel.X, cvModel.Y}, {x, y}); %! assert (cvModel.NumObservations, 4); %! assert (cvModel.ModelParameters.NumNeighbors, 1); %! assert (cvModel.ModelParameters.NSMethod, "exhaustive"); %! assert (cvModel.ModelParameters.Distance, "euclidean"); %! assert (! cvModel.ModelParameters.Standardize); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 3; %! a = fitcknn (x, y, "NumNeighbors" ,k); %! partition = cvpartition (y, "LeaveOut"); %! cvModel = ClassificationPartitionedModel (a, partition); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert (class (cvModel.Trained{1}), "ClassificationKNN"); %! assert ({cvModel.X, cvModel.Y}, {x, y}); %! assert (cvModel.NumObservations, 4); %! assert (cvModel.ModelParameters.NumNeighbors, k); %! assert (cvModel.ModelParameters.NSMethod, "kdtree"); %! assert (cvModel.ModelParameters.Distance, "euclidean"); %! assert (! cvModel.ModelParameters.Standardize); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = {"a"; "a"; "b"; "b"}; %! a = fitcnet (x, y, "IterationLimit", 50); %! cvModel = crossval (a, "KFold", 5); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert (cvModel.NumObservations, 4); %! assert (numel (cvModel.Trained), 5); %! assert (class (cvModel.Trained{1}), "CompactClassificationNeuralNetwork"); %! assert (cvModel.CrossValidatedModel, "ClassificationNeuralNetwork"); %! assert (cvModel.KFold, 5); %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = {"a"; "a"; "b"; "b"}; %! a = fitcnet (x, y, "LayerSizes", [5, 3]); %! cvModel = crossval (a, "LeaveOut", "on"); %! assert (class (cvModel), "ClassificationPartitionedModel"); %! assert ({cvModel.X, cvModel.Y}, {x, y}); %! assert (cvModel.NumObservations, 4); %! assert (numel (cvModel.Trained), 4); %! assert (class (cvModel.Trained{1}), "CompactClassificationNeuralNetwork"); %! assert (cvModel.CrossValidatedModel, "ClassificationNeuralNetwork"); %!test %! load fisheriris %! inds = ! strcmp (species, 'setosa'); %! x = meas(inds, 3:4); %! y = grp2idx (species(inds)); %! SVMModel = fitcsvm (x,y); %! CVMdl = crossval (SVMModel, "KFold", 5); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 5) %! assert (class (CVMdl.Trained{1}), "CompactClassificationSVM") %! assert (CVMdl.CrossValidatedModel, "ClassificationSVM"); %!test %! load fisheriris %! inds = ! strcmp (species, 'setosa'); %! x = meas(inds, 3:4); %! y = grp2idx (species(inds)); %! obj = fitcsvm (x, y); %! CVMdl = crossval (obj, "HoldOut", 0.2); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationSVM") %! assert (CVMdl.CrossValidatedModel, "ClassificationSVM"); %!test %! load fisheriris %! inds = ! strcmp (species, 'setosa'); %! x = meas(inds, 3:4); %! y = grp2idx (species(inds)); %! obj = fitcsvm (x, y); %! CVMdl = crossval (obj, "LeaveOut", 'on'); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationSVM") %! assert (CVMdl.CrossValidatedModel, "ClassificationSVM"); ## Test input validation for ClassificationPartitionedModel %!error ... %! ClassificationPartitionedModel () %!error ... %! ClassificationPartitionedModel (ClassificationKNN (ones (4,2), ones (4,1))) %!error ... %! ClassificationPartitionedModel (RegressionGAM (ones (40,2), ... %! randi ([1, 2], 40, 1)), cvpartition (randi ([1, 2], 40, 1), 'Holdout', 0.3)) %!error ... %! ClassificationPartitionedModel (ClassificationKNN (ones (4,2), ... %! ones (4,1)), 'Holdout') ## Test for kfoldPredict %!test %! load fisheriris %! a = fitcdiscr (meas, species, "gamma", 0.5, "fillcoeffs", "off"); %! cvModel = crossval (a, "Kfold", 4); %! [label, score, cost] = kfoldPredict (cvModel); %! assert (class(cvModel), "ClassificationPartitionedModel"); %! assert ({cvModel.X, cvModel.Y}, {meas, species}); %! assert (cvModel.NumObservations, 150); %!# assert (label, {"b"; "b"; "a"; "a"}); %!# assert (score, [4.5380e-01, 5.4620e-01; 2.4404e-01, 7.5596e-01; ... %!# 9.9392e-01, 6.0844e-03; 9.9820e-01, 1.8000e-03], 1e-4); %!# assert (cost, [5.4620e-01, 4.5380e-01; 7.5596e-01, 2.4404e-01; ... %!# 6.0844e-03, 9.9392e-01; 1.8000e-03, 9.9820e-01], 1e-4); %!test %! x = ones(4, 11); %! y = {"a"; "a"; "b"; "b"}; %! k = 3; %! a = fitcknn (x, y, "NumNeighbors", k); %! partition = cvpartition (y, "LeaveOut"); %! cvModel = ClassificationPartitionedModel (a, partition); %! [label, score, cost] = kfoldPredict (cvModel); %! assert (class(cvModel), "ClassificationPartitionedModel"); %! assert ({cvModel.X, cvModel.Y}, {x, y}); %! assert (cvModel.NumObservations, 4); %! assert (cvModel.ModelParameters.NumNeighbors, k); %! assert (cvModel.ModelParameters.NSMethod, "exhaustive"); %! assert (cvModel.ModelParameters.Distance, "euclidean"); %! assert (! cvModel.ModelParameters.Standardize); %! assert (label, {"b"; "b"; "a"; "a"}); %! assert (score, [0.3333, 0.6667; 0.3333, 0.6667; 0.6667, 0.3333; ... %! 0.6667, 0.3333], 1e-4); %! assert (cost, [0.6667, 0.3333; 0.6667, 0.3333; 0.3333, 0.6667; ... %! 0.3333, 0.6667], 1e-4); ## Test input validation for kfoldPredict %!error ... %! [label, score, cost] = kfoldPredict (crossval (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)))) %!error ... %! [label, score, cost] = kfoldPredict (crossval (ClassificationNeuralNetwork (ones (40,2), randi ([1, 2], 40, 1)))) statistics-release-1.7.3/inst/Classification/ClassificationSVM.m000066400000000000000000002374471475240274700247760ustar00rootroot00000000000000## Copyright (C) 2024 Pallav Purbia ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClassificationSVM ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} ClassificationSVM (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} ClassificationSVM (@dots{}, @var{name}, @var{value}) ## ## Create a @qcode{ClassificationSVM} class object containing a Support Vector ## Machine classification model for one-class or two-class problems. ## ## @code{@var{obj} = ClassificationSVM (@var{X}, @var{Y})} returns a ## ClassificationSVM object, with @var{X} as the predictor data and @var{Y} ## containing the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or variables. ## @var{X} will be used to train the SVM model. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can be either numeric, ## logical, or cell array of character vectors. It must have same numbers of ## rows as @var{X}. ## @end itemize ## ## @code{@var{obj} = ClassificationSVM (@dots{}, @var{name}, @var{value})} ## returns a ClassificationSVM object with parameters specified by ## @qcode{Name-Value} pair arguments. Type @code{help fitcsvm} for more info. ## ## A @qcode{ClassificationSVM} object, @var{obj}, stores the labelled training ## data and various parameters for the Support Vector machine classification ## model, which can be accessed in the following fields: ## ## @multitable @columnfractions 0.23 0.02 0.75 ## @headitem @var{Field} @tab @tab @var{Description} ## ## @item @qcode{X} @tab @tab Unstandardized predictor data, specified as a ## numeric matrix. Each column of @var{X} represents one predictor (variable), ## and each row represents one observation. ## ## @item @qcode{Y} @tab @tab Class labels, specified as a logical or ## numeric vector, or cell array of character vectors. Each value in @var{Y} is ## the observed class label for the corresponding row in @var{X}. ## ## @item @qcode{NumObservations} @tab@tab Number of observations used in ## training the ClassificationSVM model, specified as a positive integer scalar. ## This number can be less than the number of rows in the training data because ## rows containing @qcode{NaN} values are not part of the fit. ## ## @item @qcode{RowsUsed} @tab @tab Rows of the original training data ## used in fitting the ClassificationSVM model, specified as a numerical vector. ## If you want to use this vector for indexing the training data in @var{X}, you ## have to convert it to a logical vector, i.e ## @qcode{X = obj.X(logical (obj.RowsUsed), :);} ## ## @item @qcode{Standardize} @tab @tab A boolean flag indicating whether ## the data in @var{X} have been standardized prior to training. ## ## @item @qcode{Sigma} @tab @tab Predictor standard deviations, specified ## as a numeric vector of the same length as the columns in @var{X}. If the ## predictor variables have not been standardized, then @qcode{"obj.Sigma"} is ## empty. ## ## @item @qcode{Mu} @tab @tab Predictor means, specified as a numeric ## vector of the same length as the columns in @var{X}. If the predictor ## variables have not been standardized, then @qcode{"obj.Mu"} is empty. ## ## @item @qcode{NumPredictors} @tab @tab The number of predictors ## (variables) in @var{X}. ## ## @item @qcode{PredictorNames} @tab @tab Predictor variable names, ## specified as a cell array of character vectors. The variable names are in ## the same order in which they appear in the training data @var{X}. ## ## @item @qcode{ResponseName} @tab @tab Response variable name, specified ## as a character vector. ## ## @item @qcode{ClassNames} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the SVM model. @qcode{ClassNames} are of ## the same type as the class labels in @var{Y}. ## ## @item @qcode{Prior} @tab @tab Prior probabilities for each class, ## specified as a numeric vector. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{Cost} @tab @tab Cost of the misclassification of a point, ## specified as a square matrix. @qcode{Cost(i,j)} is the cost of classifying a ## point into class @qcode{j} if its true class is @qcode{i} (that is, the rows ## correspond to the true class and the columns correspond to the predicted ## class). The order of the rows and columns in @qcode{Cost} corresponds to the ## order of the classes in @qcode{ClassNames}. The number of rows and columns ## in @qcode{Cost} is the number of unique classes in the response. By default, ## @qcode{Cost(i,j) = 1} if @qcode{i != j}, and @qcode{Cost(i,j) = 0} if ## @qcode{i = j}. In other words, the cost is 0 for correct classification and ## 1 for incorrect classification. ## ## @item @qcode{ScoreTransform} @tab @tab A function_handle which is used ## for transforming the SVM prediction score into a posterior probability. By ## default, it is @qcode{'none'}, in which case the @code{predict} and ## @code{resubPredict} methods return the prediction scores. Use the ## @code{fitPosterior} method to compute the appropriate @code{ScoreTransform}, ## in which case the @code{predict} and @code{resubPredict} methods return the ## posterior probabilities. ## ## @item @qcode{ModelParameters} @tab @tab A structure containing the ## parameters used to train the SVM model with the following fields: ## @code{SVMtype}, @code{BoxConstraint}, @code{CacheSize}, @code{KernelScale}, ## @code{KernelOffset}, @code{KernelFunction}, @code{PolynomialOrder}, ## @code{Nu}, @code{Tolerance}, and @code{Shrinking}. Type @code{help fitcsvm} ## for more info on their usage and default values. ## ## @item @qcode{Model} @tab @tab A structure containing the trained model in ## @qcode{'libsvm'} format. ## ## @item @qcode{Alpha} @tab @tab The coefficients of the trained SVM ## classifier specified as an @math{sx1} numeric vector, where @math{s} is the ## number of support vectors equal to @qcode{sum (obj.IsSupportVector)}. If the ## SVM classifier was trained with a @qcode{'linear'} kernel function, then ## @qcode{obj.Alpha} is left empty. ## ## @item @qcode{Beta} @tab @tab The linear predictor coefficients specified ## as an @math{sx1} numeric vector, where @math{s} is the number of support ## vectors equal to @qcode{sum (obj.IsSupportVector)}. If the SVM classifier ## was trained with a kernel function other than @qcode{'linear'}, then ## @qcode{obj.Beta} is left empty. ## ## @item @qcode{Bias} @tab @tab The bias term specified as a scalar. ## ## @item @qcode{IsSupportVector} @tab @tab Support vector indicator, ## specified as an @math{Nx1} logical vector that flags whether a corresponding ## observation in the predictor data matrix is a Support Vector. @math{N} is ## the number of observations in the training data (see @code{NumObservations}). ## ## @item @qcode{SupportVectorLabels} @tab @tab The support vector class ## labels specified as an @math{sx1} numeric vector, where @math{s} is the ## number of support vectors equal to @qcode{sum (obj.IsSupportVector)}. A ## value of +1 in @code{SupportVectorLabels} indicates that the corresponding ## support vector belongs to the positive class @qcode{(ClassNames@{2@})}. A ## value of -1 indicates that the corresponding support vector belongs to the ## negative class @qcode{(ClassNames@{1@})}. ## ## @item @qcode{SupportVectors} @tab @tab The support vectors of the ## trained SVM classifier specified an @math{sxp} numeric matrix, where @math{s} ## is the number of support vectors equal to @qcode{sum (obj.IsSupportVector)}, ## and @math{p} is the number of predictor variables in the predictor data. ## ## @end multitable ## ## @seealso{fitcsvm, svmtrain, svmpredict} ## @end deftypefn properties (Access = public) X = []; # Predictor data Y = []; # Class labels NumObservations = []; # Number of observations in training dataset RowsUsed = []; # Rows used in fitting NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of misclassification ScoreTransform = []; # Transformation for classification scores Standardize = []; # Flag to standardize predictors Sigma = []; # Predictor standard deviations Mu = []; # Predictor means ModelParameters = []; # SVM parameters Model = []; # Stores the 'libsvm' trained model Alpha = []; # Trained classifier coefficients Beta = []; # Linear predictor coefficients Bias = []; # Bias term IsSupportVector = []; # Indices of Support vectors SupportVectorLabels = []; # Support vector class labels SupportVectors = []; # Support vectors endproperties methods (Access = public) ## Class object constructor function this = ClassificationSVM (X, Y, varargin) ## Check for sufficient number of input arguments if (nargin < 2) error ("ClassificationSVM: too few input arguments."); endif ## Check X and Y have the same number of observations if (rows (X) != rows (Y)) error ("ClassificationSVM: number of rows in X and Y must be equal."); endif ## Assign original X and Y data to the ClassificationSVM object this.X = X; this.Y = Y; ## Get groups in Y [gY, gnY, glY] = grp2idx (Y); ## Set default values before parsing optional parameters SVMtype = 'c_svc'; KernelFunction = []; KernelScale = 1; KernelOffset = 0; PolynomialOrder = 3; BoxConstraint = 1; Nu = 0.5; CacheSize = 1000; Tolerance = 1e-6; Shrinking = 1; Standardize = false; ResponseName = []; PredictorNames = []; ClassNames = []; Prior = []; Cost = []; this.ScoreTransform = 'none'; ## Parse extra parameters SVMtype_override = true; while (numel (varargin) > 0) switch (tolower (varargin {1})) case "standardize" Standardize = varargin{2}; if (! (Standardize == true || Standardize == false)) error (strcat (["ClassificationSVM: 'Standardize' must"], ... [" be either true or false."])); endif case "predictornames" PredictorNames = varargin{2}; if (! iscellstr (PredictorNames)) error (strcat (["ClassificationSVM: 'PredictorNames' must"], ... [" be supplied as a cellstring array."])); elseif (columns (PredictorNames) != columns (X)) error (strcat (["ClassificationSVM: 'PredictorNames' must"], ... [" have the same number of columns as X."])); endif case "responsename" ResponseName = varargin{2}; if (! ischar (ResponseName)) error (strcat (["ClassificationSVM: 'ResponseName' must"], ... [" be a character vector."])); endif case "classnames" ClassNames = varargin{2}; if (! (iscellstr (ClassNames) || isnumeric (ClassNames) || islogical (ClassNames))) error (strcat (["ClassificationSVM: 'ClassNames' must be a"], ... [" cellstring, logical or numeric vector."])); endif ## Check that all class names are available in gnY if (iscellstr (ClassNames)) if (! all (cell2mat (cellfun (@(x) any (strcmp (x, gnY)), ClassNames, "UniformOutput", false)))) error (strcat (["ClassificationSVM: not all 'ClassNames'"], ... [" are present in Y."])); endif else if (! all (cell2mat (arrayfun (@(x) any (x == glY), ClassNames, "UniformOutput", false)))) error (strcat (["ClassificationSVM: not all 'ClassNames'"], ... [" are present in Y."])); endif endif case "prior" Prior = varargin{2}; if (! ((isnumeric (Prior) && isvector (Prior)) || (strcmpi (Prior, "empirical") || strcmpi (Prior, "uniform")))) error (strcat (["ClassificationSVM: 'Prior' must be either"], ... [" a numeric vector or a character vector."])); endif case "cost" Cost = varargin{2}; if (! (isnumeric (Cost) && issquare (Cost))) error (strcat (["ClassificationSVM: 'Cost' must be"], ... [" a numeric square matrix."])); endif case "scoretransform" name = "ClassificationSVM"; this.ScoreTransform = parseScoreTransform (varargin{2}, name); case "svmtype" SVMtype = varargin{2}; SVMtype_override = false; if (! any (strcmp (SVMtype, {"c_svc", "nu_svc", "one_class_svm"}))) error (strcat (["ClassificationSVM: 'SVMtype' must be"], ... [" 'c_svc', 'nu_svc', or 'one_class_svm'."])); endif case "outlierfraction" Nu = varargin{2}; if (! (isscalar (Nu) && Nu >= 0 && Nu < 1)) error (strcat (["ClassificationSVM: 'OutlierFraction' must"], ... [" be a positive scalar in the range 0 =<"], ... [" OutlierFraction < 1."])); endif if (Nu > 0) SVMtype = 'nu_svc'; endif case "kernelfunction" KernelFunction = varargin{2}; if (! ischar (KernelFunction)) error (strcat (["ClassificationSVM: 'KernelFunction' must"], ... [" be a character vector."])); endif KernelFunction = tolower (KernelFunction); if (! any (strcmpi (KernelFunction, ... {"linear", "rbf", "gaussian", "polynomial", "sigmoid"}))) error ("ClassificationSVM: unsupported Kernel function."); endif case "polynomialorder" PolynomialOrder = varargin{2}; if (! (isnumeric (PolynomialOrder) && isscalar (PolynomialOrder) && PolynomialOrder > 0 && mod (PolynomialOrder, 1) == 0)) error (strcat (["ClassificationSVM: 'PolynomialOrder' must"], ... [" be a positive integer."])); endif case "kernelscale" KernelScale = varargin{2}; if (! (isscalar (KernelScale) && KernelScale > 0)) error (strcat (["ClassificationSVM: 'KernelScale'"], ... [" must be a positive scalar."])); endif case "kerneloffset" KernelOffset = varargin{2}; if (! (isnumeric (KernelOffset) && isscalar (KernelOffset) && KernelOffset >= 0)) error (strcat (["ClassificationSVM: 'KernelOffset' must"], ... [" be a non-negative scalar."])); endif case "boxconstraint" BoxConstraint = varargin{2}; if (! (isscalar (BoxConstraint) && BoxConstraint > 0)) error (strcat (["ClassificationSVM: 'BoxConstraint' must"], ... [" be a positive scalar."])); endif case "nu" Nu = varargin{2}; if (SVMtype_override) SVMtype = 'one_class_svm'; endif if (! (isscalar (Nu) && Nu > 0 && Nu <= 1)) error (strcat (["ClassificationSVM: 'Nu' must be a positive"], ... [" scalar in the range 0 < Nu <= 1."])); endif case "cachesize" CacheSize = varargin{2}; if (! (isscalar (CacheSize) && CacheSize > 0)) error (strcat (["ClassificationSVM: 'CacheSize' must"], ... [" be a positive scalar."])); endif case "tolerance" Tolerance = varargin{2}; if (! (isscalar (Tolerance) && Tolerance >= 0)) error (strcat (["ClassificationSVM: 'Tolerance' must"], ... [" be a positive scalar."])); endif case "shrinking" Shrinking = varargin{2}; if (! (ismember (Shrinking, [0, 1]) && isscalar (Shrinking))) error ("ClassificationSVM: 'Shrinking' must be either 0 or 1."); endif otherwise error (strcat (["ClassificationSVM: invalid parameter name"], ... [" in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Get number of variables in training data ndims_X = columns (X); ## Assign the number of predictors to the ClassificationKNN object this.NumPredictors = ndims_X; ## Handle class names if (! isempty (ClassNames)) if (iscellstr (ClassNames)) ru = find (! ismember (gnY, ClassNames)); else ru = find (! ismember (glY, ClassNames)); endif for i = 1:numel (ru) gY(gY == ru(i)) = NaN; endfor endif ## Remove missing values from X and Y RowsUsed = ! logical (sum (isnan ([X, gY]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [gY, gnY, glY] = grp2idx (Y); nclasses = numel (gnY); this.ClassNames = glY; # Keep the same type as Y ## If only one class available, force 'SVMtype' to 'one_class_svm' if (nclasses == 1) if (! SVMtype_override && ! strcmp (SVMtype, 'one_class_svm')) error (strcat (["ClassificationSVM: cannot train a binary"], ... [" problem with only one class available."])); endif SVMtype = 'one_class_svm'; if (isempty (KernelFunction)) KernelFunction = 'rbf'; endif else if (isempty (KernelFunction)) KernelFunction = 'linear'; endif endif ## Check that we are dealing only with one-class or binary classification if (nclasses > 2) error (strcat (["ClassificationSVM: can only be used for"], ... [" one-class or two-class learning."])); endif ## Force Y into numeric if (! isnumeric (Y)) Y = gY; endif ## Force Y labels to -1 and +1 to avoid numeric issues with different ## compiling options; see https://github.com/cjlin1/libsvm/issues/220 if (nclasses == 2) Y(Y == 2) = -1; endif ## Check X contains valid data if (! (isnumeric (X) && isfinite (X))) error ("ClassificationSVM: invalid values in X."); endif ## Assign the number of observations and their correspoding indices ## on the original data, which will be used for training the model, ## to the ClassificationSVM object this.NumObservations = rows (X); this.RowsUsed = cast (RowsUsed, "double"); ## Handle Standardize flag if (Standardize) this.Standardize = true; this.Sigma = std (X, [], 1); this.Sigma(this.Sigma == 0) = 1; # predictor is constant this.Mu = mean (X, 1); else this.Standardize = false; this.Sigma = []; this.Mu = []; endif ## Handle Prior and Cost if (strcmpi ("uniform", Prior)) this.Prior = ones (size (gnY)) ./ nclasses; elseif (isempty (Prior) || strcmpi ("empirical", Prior)) pr = []; for i = 1:nclasses pr = [pr; sum(gY==i)]; endfor this.Prior = pr ./ sum (pr); elseif (isnumeric (Prior)) if (nclasses != numel (Prior)) error (strcat (["ClassificationSVM: the elements in 'Prior' do"], ... [" not correspond to selected classes in Y."])); endif this.Prior = Prior ./ sum (Prior); endif if (isempty (Cost)) this.Cost = cast (! eye (nclasses), "double"); else if (nclasses != sqrt (numel (Cost))) error (strcat (["ClassificationSVM: the number of rows and"], ... [" columns in 'Cost' must correspond to"], ... [" the selected classes in Y."])); endif this.Cost = Cost; endif ## Generate default predictors and response variabe names (if necessary) if (isempty (PredictorNames)) for i = 1:ndims_X PredictorNames {i} = strcat ("x", num2str (i)); endfor endif if (isempty (ResponseName)) ResponseName = "Y"; endif ## Assign predictors and response variable names this.PredictorNames = PredictorNames; this.ResponseName = ResponseName; ## Set svmtrain parameters for SVMtype and KernelFunction switch (SVMtype) case "c_svc" s = 0; case "nu_svc" s = 1; case "one_class_svm" s = 2; endswitch switch (KernelFunction) case "linear" t = 0; case "polynomial" t = 1; case {"rbf", "gaussian"} t = 2; case "sigmoid" t = 3; endswitch ## Set svmtrain parameters for gamma g = KernelScale / ndims_X; ## svmpredict: ## '-s': SVMtype ## '-t': KernelFunction ## '-g': Gamma ## '-d': PolynomialOrder ## '-r': KernelOffset ## '-c': BoxConstraint ## '-n': Nu ## '-m': CacheSize ## '-e': Tolerance ## '-h': Shrinking ## Build options string for svmtrain function str_options = strcat (["-s %d -t %d -g %f -d %d -r %f"], ... [" -c %f -n %f -m %f -e %e -h %d -q"]); svm_options = sprintf (str_options, s, t, g, PolynomialOrder, ... KernelOffset, BoxConstraint, Nu, ... CacheSize, Tolerance, Shrinking); ## Train the SVM model using svmtrain from libsvm Model = svmtrain (Y, X, svm_options); this.Model = Model; ## Populate ClassificationSVM object properties if (t == 0) # linear kernel this.Alpha = Model.sv_coef; else # other kernels this.Beta = Model.sv_coef; endif this.Bias = Model.rho; this.IsSupportVector = zeros (this.NumObservations, 1); this.IsSupportVector(Model.sv_indices) = 1; this.SupportVectorLabels = zeros (size (Model.sv_indices)); ## Handle one class if (isempty (Model.nSV)) this.SupportVectorLabels(Model.sv_indices) = -1; else idx = Model.nSV(1); this.SupportVectorLabels(Model.sv_indices([1:idx])) = -1; this.SupportVectorLabels(Model.sv_indices([idx+1:end])) = 1; endif this.SupportVectors = Model.SVs; ## Populate ModelParameters structure params = struct ('SVMtype', SVMtype, 'BoxConstraint', BoxConstraint, ... 'CacheSize', CacheSize, 'KernelScale', KernelScale, ... 'KernelOffset', KernelOffset, 'KernelFunction', ... KernelFunction, 'PolynomialOrder', PolynomialOrder, ... 'Nu', Nu, 'Tolerance',Tolerance, ... 'Shrinking', Shrinking); this.ModelParameters = params; endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{labels} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {ClassificationSVM} {[@var{labels}, @var{scores}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the Support Vector Machine ## classification object. ## ## @code{@var{labels} = predict (@var{obj}, @var{XC})} returns the vector of ## labels predicted for the corresponding instances in @var{XC}, using the ## trained Support Vector Machine classification model, @var{obj}. ## For one-class SVM model, +1 or -1 is returned. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationSVM} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix with the same number of ## predictors @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @end itemize ## ## @code{[@var{labels}, @var{scores}] = predict (@var{obj}, @var{XC}} also ## returns @var{scores}, which contains the desicion values for each each ## prediction. Alternatively, @var{scores} can contain the posterior ## probabilities if the ScoreTransform has been previously set using the ## @code{fitPosterior} method. ## ## @seealso{fitcsvm, ClassificationSVM.fitPosterior} ## @end deftypefn function [labels, scores] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error ("ClassificationSVM.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("ClassificationSVM.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["ClassificationSVM.predict:"], ... [" XC must have the same number of"], ... [" predictors as the trained model."])); endif ## Standardize (if necessary) if (this.Standardize) XC = (XC - this.Mu) ./ this.Sigma; endif ## Predict labels and scores from new data [out, ~, scores] = svmpredict (ones (rows (XC), 1), XC, this.Model, '-q'); ## Expand scores for two classes if (numel (this.ClassNames) == 2) scores = [scores, -scores]; endif ## Translate labels to classnames if (iscellstr (this.Y)) labels = cell (rows (XC), 1); labels(out==1) = this.ClassNames{1}; labels(out!=1) = this.ClassNames{2}; elseif (islogical (this.Y)) labels = false (rows (XC), 1); elseif (isnumeric (this.Y)) labels = zeros (rows (XC), 1); elseif (ischar (this.Y)) labels = char (zeros (rows (XC), size (this.Y, 2))); endif if (! iscellstr (this.Y)) labels(out==1) = this.ClassNames(1); labels(out!=1) = this.ClassNames(2); endif if (nargout > 1) ## Apply ScoreTransform to return probability estimates if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["ClassificationSVM.predict: 'ScoreTransform'"], ... [" must be a 'function_handle' object."])); endif scores = f (scores); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{labels} =} resubPredict (@var{obj}) ## @deftypefnx {ClassificationSVM} {[@var{labels}, @var{score}] =} resubPredict (@var{obj}) ## ## Classify the training data using the trained Support Vector Machine ## classification object. ## ## @code{@var{labels} = resubPredict (@var{obj})} returns the vector of ## labels predicted for the corresponding instances in the training data, ## using the predictor data in @code{obj.X} and corresponding labels, ## @code{obj.Y}, stored in the Support Vector Machine classification model, ## @var{obj}. For one-class model, +1 or -1 is returned. ## ## @itemize ## @item ## @var{obj} must be a @qcode{ClassificationSVM} class object. ## @end itemize ## ## @code{[@var{labels}, @var{scores}] = resubPredict (@var{obj}} also ## returns @var{scores}, which contains the desicion values for each each ## prediction. Alternatively, @var{scores} can contain the posterior ## probabilities if the ScoreTransform has been previously set using the ## @code{fitPosterior} method. ## ## @seealso{fitcsvm, ClassificationSVM.fitPosterior} ## @end deftypefn function [labels, scores] = resubPredict (this) ## Get used rows (if necessary) if (sum (this.RowsUsed) != rows (this.X)) RowsUsed = logical (this.RowsUsed); X = this.X(RowsUsed); Y = this.Y(RowsUsed); else X = this.X; Y = this.Y; endif ## Standardize (if necessary) if (this.Standardize) X = (X - this.Mu) ./ this.Sigma; endif ## Predict labels and scores from new data [out, ~, scores] = svmpredict (ones (rows (X), 1), X, this.Model, '-q'); ## Expand scores for two classes if (numel (this.ClassNames) == 2) scores = [scores, -scores]; endif ## Translate labels to classnames if (iscellstr (this.Y)) labels = cell (rows (X), 1); labels(out==1) = this.ClassNames{1}; labels(out!=1) = this.ClassNames{2}; elseif (islogical (this.Y)) labels = false (rows (X), 1); elseif (isnumeric (this.Y)) labels = zeros (rows (X), 1); elseif (ischar (this.Y)) labels = char (zeros (rows (X), size (this.Y, 2))); endif if (! iscellstr (this.Y)) labels(out==1) = this.ClassNames(1); labels(out!=1) = this.ClassNames(2); endif if (nargout > 1) ## Apply ScoreTransform to return probability estimates if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["ClassificationSVM.resubPredict: 'Score"], ... ["Transform' must be a 'function_handle' object."])); endif scores = f (scores); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{m} =} margin (@var{obj}, @var{X}, @var{Y}) ## ## Determine the classification margins for a Support Vector Machine ## classification object. ## ## @code{@var{m} = margin (@var{obj}, @var{X}, @var{Y})} returns the ## classification margins for the trained support vector machine (SVM) ## classifier @var{obj} using the sample data in @var{X} and the class ## labels in @var{Y}. It supports only binary classifier models. The ## classification margin is commonly defined as @var{m} = @var{y}f(@var{x}), ## where @var{f(x)} is the classification score and @var{y} is the true ## class label corresponding to @var{x}. A greater margin indicates a better ## model. ## ## @itemize ## @item ## @var{obj} must be a binary class @qcode{ClassificationSVM} object. ## @item ## @var{X} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @item ## @var{Y} must be @math{Mx1} numeric vector containing the class labels ## corresponding to the predictor data in @var{X}. @var{Y} must have same ## number of rows as @var{X}. ## @end itemize ## ## @seealso{fitcsvm, ClassificationSVM} ## @end deftypefn function m = margin (this, X, Y) ## Check for sufficient input arguments if (nargin < 3) error ("ClassificationSVM.margin: too few input arguments."); endif ## Check for valid X if (isempty (X)) error ("ClassificationSVM.margin: X is empty."); elseif (columns (this.X) != columns (X)) error (strcat (["ClassificationSVM.margin: X must have the same"], ... [" number of predictors as the trained model."])); endif ## Check for valid Y if (isempty (Y)) error ("ClassificationSVM.margin: Y is empty."); elseif (rows (X) != rows (Y)) error (strcat (["ClassificationSVM.margin: Y must have"], ... [" the same number of rows as X."])); endif [~, ~, dec_values_L] = svmpredict (Y, X, this.Model, '-q'); m = 2 * Y .* dec_values_L; endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{L} =} loss (@var{obj}, @var{X}, @var{Y}) ## @deftypefnx {ClassificationSVM} {@var{L} =} loss (@dots{}, @var{name}, @var{value}) ## ## Determine the classification error for a Support Vector Machine ## classifier. ## ## @code{@var{L} = loss (@var{obj}, @var{X}, @var{Y})} returns the ## predictive accuracy of support vector machine (SVM) classification models. ## Comparing the same type of loss across multiple models allows you to ## identify which model is more accurate, with a lower loss indicating ## superior predictive performance. It supports only binary classifier ## models. ## ## @itemize ## @item ## @var{obj} must be a binary class @qcode{ClassificationSVM} object. ## @item ## @var{X} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @item ## @var{Y} must be @math{Mx1} numeric vector containing the class labels ## corresponding to the predictor data in @var{X}. @var{Y} must have same ## number of rows as @var{X}. ## @end itemize ## ## @code{@var{L} = loss (@dots{}, @var{Name}, @var{Value})} returns the ## aforementioned results with additional properties specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"LossFun"} @tab @tab Loss function, specified as a built-in ## loss function name. It accepts the following options: (Default is ## 'classiferror') ## ## @itemize ## ## @item 'binodeviance': Binomial deviance: ## The binomial deviance loss function is used to evaluate the performance ## of a binary classifier. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \log \{1 + \exp [-2m_j]\}} ## ## @item 'classiferror': Misclassification rate in decimal ## The classification error measures the fraction of misclassified instances ## out of the total instances. It is calculated as: ## @math{L = \frac{1}{n} \sum_{j=1}^{n} \mathbb{I}(m_j \leq 0)} ## ## @item 'exponential': Exponential loss: ## The exponential loss function is used to penalize misclassified instances ## exponentially. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \exp [-m_j]} ## ## @item 'hinge': Hinge loss: ## The hinge loss function is often used for maximum-margin classification, ## particularly for support vector machines. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \max (0, 1 - m_j)} ## ## @item 'logit': Logistic loss: ## The logistic loss function, also known as log loss, measures the ## performance of a classification model where the prediction is a ## probability value. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \log \{1 + \exp [-m_j]\}} ## ## @item 'quadratic': Quadratic loss: ## The quadratic loss function penalizes the square of the margin. ## It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j (1 - m_j)^2} ## ## @end itemize ## ## @item @qcode{"Weights"} @tab @tab Specified as a numeric vector which ## weighs each observation (row) in X. The size of Weights must be equal ## to the number of rows in X. The default value is: ones(size(X,1),1) ## ## @end multitable ## ## @seealso{fitcsvm, ClassificationSVM} ## @end deftypefn function L = loss (this, X, Y, varargin) ## Check for sufficient input arguments if (nargin < 3) error ("ClassificationSVM.loss: too few input arguments."); endif if (mod (nargin, 2) == 0) error ("ClassificationSVM.loss: Name-Value arguments must be in pairs."); endif ## Check for valid X if (isempty (X)) error ("ClassificationSVM.loss: X is empty."); elseif (columns (this.X) != columns (X)) error (strcat (["ClassificationSVM.loss: X must have the same"], ... [" number of predictors as the trained model."])); endif ## Check for valid Y if (isempty (Y)) error ("ClassificationSVM.loss: Y is empty."); elseif (rows (X)!= rows (Y)) error (strcat (["ClassificationSVM.loss: Y must have the same"], ... [" number of rows as X."])); endif ## Set default values before parsing optional parameters LossFun = 'classiferror'; Weights = ones (size (X, 1), 1); ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "lossfun" LossFun = varargin{2}; if (! (ischar (LossFun))) error (strcat (["ClassificationSVM.loss: 'LossFun'"], ... [" must be a character vector."])); endif LossFun = tolower (LossFun); if (! any (strcmpi (LossFun, {"binodeviance", "classiferror", ... "exponential", "hinge", "logit", ... "quadratic"}))) error ("ClassificationSVM.loss: unsupported Loss function."); endif case "weights" Weights = varargin{2}; ## Validate if weights is a numeric vector if(! (isnumeric (Weights) && isvector (Weights))) error (strcat (["ClassificationSVM.loss: 'Weights'"], ... [" must be a numeric vector."])); endif ## Check if the size of weights matches the number of rows in X if (numel (Weights) != size (X, 1)) error (strcat (["ClassificationSVM.loss: size of 'Weights'"], ... [" must be equal to the number of rows in X."])); endif otherwise error (strcat (["ClassificationSVM.loss: invalid parameter"], ... [" name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Compute the classification score [~, ~, dec_values_L] = svmpredict (Y, X, this.Model, '-q'); ## Compute the margin margin = Y .* dec_values_L; ## Compute the loss based on the specified loss function switch (LossFun) case "classiferror" L = mean ((margin <= 0) .* Weights); case "hinge" L = mean (max (0, 1 - margin) .* Weights); case "logit" L = mean (log (1 + exp (-margin)) .* Weights); case "exponential" L = mean (exp (-margin) .* Weights); case "quadratic" L = mean (((1 - margin) .^2) .* Weights); case "binodeviance" L = mean (log (1 + exp (-2 * margin)) .* Weights); otherwise error ("ClassificationSVM.loss: unsupported Loss function."); endswitch endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{L} =} resubLoss (@var{obj}) ## @deftypefnx {ClassificationSVM} {@var{L} =} resubLoss (@dots{}, @var{name}, @var{value}) ## ## Compute the resubstitution classification loss for the trained Support ## Vector Machine classification object. ## ## @code{@var{L} = resubLoss (@var{obj})} returns the classification loss by ## resubstitution (L), or the in-sample classification loss, for the trained ## classification model @var{obj} using the training data stored in ## @code{obj.X} and the corresponding class labels stored in @code{obj.Y}. ## ## @itemize ## @item ## @var{obj} must be a binary class @qcode{ClassificationSVM} object. ## @end itemize ## ## @code{@var{l} = resubLoss (@dots{}, @var{Name}, @var{Value})} returns the ## aforementioned results with additional properties specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"LossFun"} @tab @tab Loss function, specified as a built-in ## loss function name. It accepts the following options: (Default is ## 'classiferror') ## ## @itemize ## ## @item 'binodeviance': Binomial deviance: ## The binomial deviance loss function is used to evaluate the performance ## of a binary classifier. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \log \{1 + \exp [-2m_j]\}} ## ## @item 'classiferror': Misclassification rate in decimal ## The classification error measures the fraction of misclassified instances ## out of the total instances. It is calculated as: ## @math{L = \frac{1}{n} \sum_{j=1}^{n} \mathbb{I}(m_j \leq 0)} ## ## @item 'exponential': Exponential loss: ## The exponential loss function is used to penalize misclassified instances ## exponentially. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \exp [-m_j]} ## ## @item 'hinge': Hinge loss: ## The hinge loss function is often used for maximum-margin classification, ## particularly for support vector machines. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \max (0, 1 - m_j)} ## ## @item 'logit': Logistic loss: ## The logistic loss function, also known as log loss, measures the ## performance of a classification model where the prediction is a ## probability value. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \log \{1 + \exp [-m_j]\}} ## ## @item 'quadratic': Quadratic loss: ## The quadratic loss function penalizes the square of the margin. ## It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j (1 - m_j)^2} ## ## @end itemize ## ## @item @qcode{"Weights"} @tab @tab Specified as a numeric vector which ## weighs each observation (row) in X. The size of Weights must be equal ## to the number of rows in X. The default value is: ones(size(X,1),1) ## ## @end multitable ## ## @seealso{fitcsvm, ClassificationSVM} ## @end deftypefn function L = resubLoss (this, varargin) if (mod(nargin, 2) != 1) error (strcat (["ClassificationSVM.resubLoss: Name-Value"], ... [" arguments must be in pairs."])); endif ## Set default values before parsing optional parameters LossFun = 'classiferror'; Weights = ones (size (this.X, 1), 1); ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin{1})) case "lossfun" LossFun = varargin{2}; if (! ischar (LossFun)) error (strcat (["ClassificationSVM.resubLoss: 'LossFun'"], ... [" must be a character vector."])); endif LossFun = tolower (LossFun); if (! any (strcmpi (LossFun, {"binodeviance", "classiferror", ... "exponential", "hinge", "logit", ... "quadratic"}))) error (strcat (["ClassificationSVM.resubLoss: unsupported"], ... [" Loss function."])); endif case "weights" Weights = varargin{2}; ## Validate if weights is a numeric vector if (! (isnumeric (Weights) && isvector (Weights))) error (strcat (["ClassificationSVM.resubLoss: 'Weights'"], ... [" must be a numeric vector."])); endif ## Check if the size of weights matches the number of rows in X if (numel (Weights) != size (this.X, 1)) error (strcat (["ClassificationSVM.resubLoss: size"], ... [" of 'Weights' must be equal to the"], ... [" number of rows in X."])); endif otherwise error (strcat (["ClassificationSVM.resubLoss: invalid"], ... [" parameter name in optional pair arguments."])); endswitch varargin(1:2) = []; endwhile ## Compute the classification score [~, ~, dec_values_L] = svmpredict (this.Y, this.X, this.Model, '-q'); ## Compute the margin margin = this.Y .* dec_values_L; ## Compute the loss based on the specified loss function switch tolower(LossFun) case "classiferror" L = mean ((margin <= 0) .* Weights); case "hinge" L = mean (max (0, 1 - margin) .* Weights); case "logit" L = mean (log (1 + exp (-margin)) .* Weights); case "exponential" L = mean (exp (-margin) .* Weights); case "quadratic" L = mean ((1 - margin).^2 .* Weights); case "binodeviance" L = mean (log (1 + exp (-2 * margin)) .* Weights); otherwise error ("ClassificationSVM.resubloss: unsupported Loss function."); endswitch endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{CVMdl} =} crossval (@var{obj}) ## @deftypefnx {ClassificationSVM} {@var{CVMdl} =} crossval (@dots{}, @var{Name}, @var{Value}) ## ## Cross Validate a Support Vector Machine classification object. ## ## @code{@var{CVMdl} = crossval (@var{obj})} returns a cross-validated model ## object, @var{CVMdl}, from a trained model, @var{obj}, using 10-fold ## cross-validation by default. ## ## @code{@var{CVMdl} = crossval (@var{obj}, @var{name}, @var{value})} ## specifies additional name-value pair arguments to customize the ## cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"KFold"} @tab @tab Specify the number of folds to use in ## k-fold cross-validation. @code{"KFold", @var{k}}, where @var{k} is an ## integer greater than 1. ## ## @item @qcode{"Holdout"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{"Holdout", @var{p}}, where @var{p} is a ## scalar in the range @math{(0,1)}. ## ## @item @qcode{"Leaveout"} @tab @tab Specify whether to perform ## leave-one-out cross-validation. @code{"Leaveout", @var{Value}}, where ## @var{Value} is 'on' or 'off'. ## ## @item @qcode{"CVPartition"} @tab @tab Specify a @qcode{cvpartition} ## object used for cross-validation. @code{"CVPartition", @var{cv}}, where ## @code{isa (@var{cv}, "cvpartition")} = 1. ## ## @end multitable ## ## @seealso{fitcsvm, ClassificationSVM, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function CVMdl = crossval (this, varargin) ## Check for sufficient input arguments if (nargin < 1) error ("ClassificationSVM.crossval: too few input arguments."); endif if (numel (varargin) == 1) error (strcat (["ClassificationSVM.crossval: Name-Value arguments"], ... [" must be in pairs."])); elseif (numel (varargin) > 2) error (strcat (["ClassificationSVM.crossval: specify only one of"], ... [" the optional Name-Value paired arguments."])); endif ## Add default values numFolds = 10; Holdout = []; Leaveout = 'off'; CVPartition = []; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case 'kfold' numFolds = varargin{2}; if (! (isnumeric (numFolds) && isscalar (numFolds) && (numFolds == fix (numFolds)) && numFolds > 1)) error (strcat (["ClassificationSVM.crossval: 'KFold' must"], ... [" be an integer value greater than 1."])); endif case 'holdout' Holdout = varargin{2}; if (! (isnumeric (Holdout) && isscalar (Holdout) && Holdout > 0 && Holdout < 1)) error (strcat (["ClassificationSVM.crossval: 'Holdout' must"], ... [" be a numeric value between 0 and 1."])); endif case 'leaveout' Leaveout = varargin{2}; if (! (ischar (Leaveout) && (strcmpi (Leaveout, 'on') || strcmpi (Leaveout, 'off')))) error (strcat (["ClassificationSVM.crossval: 'Leaveout'"], ... [" must be either 'on' or 'off'."])); endif case 'cvpartition' CVPartition = varargin{2}; if (!(isa (CVPartition, 'cvpartition'))) error (strcat (["ClassificationSVM.crossval: 'CVPartition'"],... [" must be a 'cvpartition' object."])); endif otherwise error (strcat (["ClassificationSVM.crossval: invalid"],... [" parameter name in optional paired arguments."])); endswitch varargin (1:2) = []; endwhile ## Determine the cross-validation method to use if (! isempty (CVPartition)) partition = CVPartition; elseif (! isempty (Holdout)) partition = cvpartition (this.Y, 'Holdout', Holdout); elseif (strcmpi (Leaveout, 'on')) partition = cvpartition (this.Y, 'LeaveOut'); else partition = cvpartition (this.Y, 'KFold', numFolds); endif ## Create a cross-validated model object CVMdl = ClassificationPartitionedModel (this, partition); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{Mdl} =} fitPosterior (@var{obj}) ## @deftypefnx {ClassificationSVM} {@var{CVMdl} =} fitPosterior (@var{obj}, @var{name}, @var{value}) ## ## Fit posterior probabilities to a Support Vector Machine model. ## ## @code{@var{Mdl} = fitPosterior (@var{obj})} returns the ClassificationSVM ## object, @var{Mdl}, from an already trained SVM model, @var{obj}, after ## fitting a posterior probabilities ScoreTransform. ## ## @code{@var{CVMdl} = fitPosterior (@var{obj}, @var{name}, @var{value})} ## returns the ClassificationPartitionedModel, @var{CVMdl}, from an already ## trained SVM model, @var{obj}, after fitting a posterior probabilities ## ScoreTransform. Use the additional name-value pair arguments to customize ## the cross-validation process. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"KFold"} @tab @tab Specify the number of folds to use in ## k-fold cross-validation. @code{'kfold', @var{k}} where @var{k} is an ## integer greater than 1. ## ## @item @qcode{"Holdout"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{'holdout', @var{p}} where @var{p} is a scalar ## in the range (0,1). ## ## @item @qcode{"CVPartition"} @tab @tab Specify the fraction of the data to ## hold out for testing. @code{'holdout', @var{p}} where @var{p} is a scalar ## in the range (0,1). ## ## @item @qcode{"Leaveout"} @tab @tab Specify whether to perform ## leave-one-out cross-validation. @code{'leaveout', @var{Value}} where ## @var{Value} is 'on' or 'off'. ## ## @end multitable ## ## @seealso{cvpartition, fitcsvm, ClassificationSVM} ## @end deftypefn function CVMdl = fitPosterior (this, varargin) ## Check for ScoreTransform and emit reset warning ## Cross-validate SVM model and get labels and scores CVMdl = crossval (this, varargin{:}); [~, score] = kfoldPredict (CVMdl); ## Get class labels at 0 and 1 Y = grp2idx (CVMdl.Y(logical (this.RowsUsed))) - 1; ## Get prior probability for second class prior = this.Prior(2); ## Determine perfect separation or overlapping ub = max (score(Y==0)); lb = min (score(Y==1)); if (ub <= lb) warning ("ClassificationSVM.fitPosterior: PerfectSeparation."); f = eval (sprintf ('@(S) ClassificationSVM.step (S, %e, %e, %e)', ... ub, lb, prior)); else coeff = glmfit (score(:,2), Y, 'binomial', 'link', 'logit'); f = eval (sprintf ('@(S) ClassificationSVM.sigmoid (S, %e, %e)', ... -coeff(2), -coeff(1))); endif ## Decide returning model type if (isempty (varargin)) this.ScoreTransform = f; CVMdl = this; else CVMdl.ScoreTransform = f; endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{CVMdl} =} compact (@var{obj}) ## ## Create a CompactClassificationSVM object. ## ## @code{@var{CVMdl} = compact (@var{obj})} creates a compact version of the ## ClassificationSVM object, @var{obj}. ## ## @seealso{fitcnet, ClassificationSVM, CompactClassificationSVM} ## @end deftypefn function CVMdl = compact (this) ## Greate a compact model CVMdl = CompactClassificationSVM (this); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationSVM object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ClassificationSVM ## object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcsvm, ClassificationSVM} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "ClassificationSVM"; ## Create variables from model properties X = this.X; Y = this.Y; NumObservations = this.NumObservations; RowsUsed = this.RowsUsed; NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; Standardize = this.Standardize; Sigma = this.Sigma; Mu = this.Mu; ModelParameters = this.ModelParameters; Model = this.Model; Alpha = this.Alpha; Beta = this.Beta; Bias = this.Bias; IsSupportVector = this.IsSupportVector; SupportVectorLabels = this.SupportVectorLabels; SupportVectors = this.SupportVectors; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "X", "Y", "NumObservations", "RowsUsed", ... "NumPredictors", "PredictorNames", "ResponseName", "ClassNames", ... "Prior", "Cost", "ScoreTransform", "Standardize", "Sigma", "Mu", ... "ModelParameters", "Model", "Alpha", "Beta", "Bias", ... "IsSupportVector", "SupportVectorLabels", "SupportVectors"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationSVM object mdl = ClassificationSVM (1, 1); ## Check that fieldnames in DATA match properties in ClassificationSVM names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) error ("ClassificationSVM.load_model: invalid model in '%s'.", filename) endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction ## Helper functions for fitPosterior function prob = step (score, ub, lb, prior) prob = zeros (size (score)); prob(score > lb) = 1; prob(score >= ub & score <= lb) = prior; endfunction function prob = sigmoid (score, a, b) prob = zeros (size (score)); prob = 1 ./ (1 + exp (-a * score + b)); endfunction endmethods endclassdef %!demo %! ## Create a Support Vector Machine classifier and determine margin for test %! ## data. %! load fisheriris %! rng(1); ## For reproducibility %! %! ## Select indices of the non-setosa species %! inds = !strcmp(species, 'setosa'); %! %! ## Select features and labels for non-setosa species %! X = meas(inds, 3:4); %! Y = grp2idx(species(inds)); %! %! ## Convert labels to +1 and -1 %! unique_classes = unique(Y); %! Y(Y == unique_classes(1)) = -1; %! Y(Y == unique_classes(2)) = 1; %! %! ## Partition data for training and testing %! cv = cvpartition(Y, 'HoldOut', 0.15); %! X_train = X(training(cv), :); %! Y_train = Y(training(cv)); %! X_test = X(test(cv), :); %! Y_test = Y(test(cv)); %! %! ## Train the SVM model %! CVSVMModel = fitcsvm(X_train, Y_train); %! %! ## Calculate margins %! m = margin(CVSVMModel, X_test, Y_test); %! disp(m); %!demo %! ## Create a Support Vector Machine classifier and determine loss for test %! ## data. %! load fisheriris %! rng(1); ## For reproducibility %! %! ## Select indices of the non-setosa species %! inds = !strcmp(species, 'setosa'); %! %! ## Select features and labels for non-setosa species %! X = meas(inds, 3:4); %! Y = grp2idx(species(inds)); %! %! ## Convert labels to +1 and -1 %! unique_classes = unique(Y); %! Y(Y == unique_classes(1)) = -1; %! Y(Y == unique_classes(2)) = 1; %! %! ## Randomly partition the data into training and testing sets %! cv = cvpartition(Y, 'HoldOut', 0.3); # 30% data for testing, 60% for training %! %! X_train = X(training(cv), :); %! Y_train = Y(training(cv)); %! %! X_test = X(test(cv), :); %! Y_test = Y(test(cv)); %! %! ## Train the SVM model %! SVMModel = fitcsvm(X_train, Y_train); %! %! ## Calculate loss %! %! L = loss(SVMModel,X_test,Y_test,'LossFun','binodeviance') %! L = loss(SVMModel,X_test,Y_test,'LossFun','classiferror') %! L = loss(SVMModel,X_test,Y_test,'LossFun','exponential') %! L = loss(SVMModel,X_test,Y_test,'LossFun','hinge') %! L = loss(SVMModel,X_test,Y_test,'LossFun','logit') %! L = loss(SVMModel,X_test,Y_test,'LossFun','quadratic') ## Test output of constructor %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1; 4, 5, 6; 7, 8, 9; ... %! 3, 2, 1; 4, 5, 6; 7, 8, 9; 3, 2, 1; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [1; 2; 3; 4; 2; 3; 4; 2; 3; 4; 2; 3; 4]; %! a = ClassificationSVM (x, y, "ClassNames", [1, 2]); %! assert (class (a), "ClassificationSVM"); %! assert (a.RowsUsed, [1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]'); %! assert ({a.X, a.Y}, {x, y}) %! assert (a.NumObservations, 5) %! assert ({a.ResponseName, a.PredictorNames}, {"Y", {"x1", "x2", "x3"}}) %! assert ({a.ClassNames, a.ModelParameters.SVMtype}, {[1; 2], "c_svc"}) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = ClassificationSVM (x, y); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "linear"}) %! assert (a.ModelParameters.BoxConstraint, 1) %! assert (a.ClassNames, [1; -1]) %! assert (a.ModelParameters.KernelOffset, 0) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = ClassificationSVM (x, y, "KernelFunction", "rbf", "BoxConstraint", 2, ... %! "KernelOffset", 2); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "rbf"}) %! assert (a.ModelParameters.BoxConstraint, 2) %! assert (a.ModelParameters.KernelOffset, 2) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = ClassificationSVM (x, y, "KernelFunction", "polynomial", ... %! "PolynomialOrder", 3); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "polynomial"}) %! assert (a.ModelParameters.PolynomialOrder, 3) ## Test input validation for constructor %!error ClassificationSVM () %!error ... %! ClassificationSVM (ones(10,2)) %!error ... %! ClassificationSVM (ones(10,2), ones (5,1)) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "Standardize", 'a') %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "PredictorNames", ['x1';'x2']) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "PredictorNames", {'x1','x2','x3'}) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "ResponseName", {'Y'}) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "ResponseName", 21) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "ClassNames", @(x)x) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "ClassNames", ['a']) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "ClassNames", [1, 2]) %!error ... %! ClassificationSVM (ones(5,2), {'a';'b';'a';'a';'b'}, "ClassNames", {'a','c'}) %!error ... %! ClassificationSVM (ones(10,2), logical (ones (10,1)), "ClassNames", [true, false]) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Prior", {"asd"}) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Prior", ones (2)) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Cost", [1:4]) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Cost", {0,1;1,0}) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Cost", 'a') %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "svmtype", 123) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "svmtype", 'some_type') %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "OutlierFraction", -1) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "KernelFunction", 123) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "KernelFunction", "fcn") %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "PolynomialOrder", -1) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "PolynomialOrder", 0.5) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "PolynomialOrder", [1,2]) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "KernelScale", -1) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "KernelScale", 0) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "KernelScale", [1, 2]) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "KernelScale", "invalid") %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "KernelOffset", -1) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "KernelOffset", [1,2]) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "BoxConstraint", -1) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "BoxConstraint", 0) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "BoxConstraint", [1, 2]) %!error ... %! ClassificationSVM (ones(10,2), ones (10,1), "BoxConstraint", "invalid") %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "nu", -0.5) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "nu", 0) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "nu", 1.5) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "CacheSize", -1) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "CacheSize", [1,2]) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Tolerance", -0.1) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "Tolerance", [0.1,0.2]) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "shrinking", 2) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "shrinking", -1) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "shrinking", [1 0]) %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "invalid_name", 'c_svc') %!error ... %! ClassificationSVM (ones(10,2), ones(10,1), "SVMtype", 'c_svc') %!error ... %! ClassificationSVM (ones(10,2), [1;1;1;1;2;2;2;2;3;3]) %!error ... %! ClassificationSVM ([ones(9,2);2,Inf], ones(10,1)) %!error ... %! ClassificationSVM (ones (5,2), ones (5,1), "Prior", [0,1]) %!error ... %! ClassificationSVM (ones (5,2), [1;1;2;2;3], "ClassNames", [1,2], "Prior", [0,0.4,0.6]) %!error ... %! ClassificationSVM (ones (5,2), [1;1;2;2;3], "ClassNames", [1,2], "Cost", ones (3)) ## Test output for predict method %!shared x, y, x_train, x_test, y_train, y_test, objST %! load fisheriris %! inds = ! strcmp (species, 'setosa'); %! x = meas(inds, 3:4); %! y = grp2idx (species(inds)); %!test %! xc = [min(x); mean(x); max(x)]; %! obj = fitcsvm (x, y, 'KernelFunction', 'rbf', 'Tolerance', 1e-7); %! assert (isempty (obj.Alpha), true) %! assert (sum (obj.IsSupportVector), numel (obj.Beta)) %! [label, score] = predict (obj, xc); %! assert (label, [1; 2; 2]); %! assert (score(:,1), [0.99285; -0.080296; -0.93694], 2e-5); %! assert (score(:,1), -score(:,2), eps) %! obj = fitPosterior (obj); %! [label, probs] = predict (obj, xc); %! assert (probs(:,2), [0.97555; 0.428164; 0.030385], 2e-5); %! assert (probs(:,1) + probs(:,2), [1; 1; 1], 0.05) %!test %! obj = fitcsvm (x, y); %! assert (isempty (obj.Beta), true) %! assert (sum (obj.IsSupportVector), numel (obj.Alpha)) %! assert (numel (obj.Alpha), 24) %! assert (obj.Bias, -14.415, 1e-3) %! xc = [min(x); mean(x); max(x)]; %! label = predict (obj, xc); %! assert (label, [1; 2; 2]); ## Test input validation for predict method %!error ... %! predict (ClassificationSVM (ones (40,2), ones (40,1))) %!error ... %! predict (ClassificationSVM (ones (40,2), ones (40,1)), []) %!error ... %! predict (ClassificationSVM (ones (40,2), ones (40,1)), 1) %!test %! objST = fitcsvm (x, y); %! objST.ScoreTransform = "a"; %!error ... %! [labels, scores] = predict (objST, x); ## Test input validation for resubPredict method %!error ... %! [labels, scores] = resubPredict (objST); ## Test output for margin method %!test %! rand ("seed", 1); %! CVSVMModel = fitcsvm (x, y, 'KernelFunction', 'rbf', 'HoldOut', 0.15, ... %! 'Tolerance', 1e-7); %! obj = CVSVMModel.Trained{1}; %! testInds = test (CVSVMModel.Partition); %! expected_margin = [2.0000; 0.8579; 1.6690; 3.4141; 3.4552; ... %! 2.6605; 3.5251; -4.0000; -6.3411; -6.4511; ... %! -3.0532; -7.5054; -1.6700; -5.6227; -7.3640]; %! computed_margin = margin (obj, x(testInds,:), y(testInds,:)); %! assert (computed_margin, expected_margin, 1e-4); ## Test input validation for margin method %!error ... %! margin (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1))) %!error ... %! margin (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2)) %!error ... %! margin (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), [], zeros (2)) %!error ... %! margin (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), 1, zeros (2)) %!error ... %! margin (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), []) %!error ... %! margin (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), 1) ## Test output for loss method %!test %! rand ("seed", 1); %! CVSVMModel = fitcsvm (x, y, 'KernelFunction', 'rbf', 'HoldOut', 0.15); %! obj = CVSVMModel.Trained{1}; %! testInds = test (CVSVMModel.Partition); %! L1 = loss (obj, x(testInds,:), y(testInds,:), 'LossFun', 'binodeviance'); %! L2 = loss (obj, x(testInds,:), y(testInds,:), 'LossFun', 'classiferror'); %! L3 = loss (obj, x(testInds,:), y(testInds,:), 'LossFun', 'exponential'); %! L4 = loss (obj, x(testInds,:), y(testInds,:), 'LossFun', 'hinge'); %! L5 = loss (obj, x(testInds,:), y(testInds,:), 'LossFun', 'logit'); %! L6 = loss (obj, x(testInds,:), y(testInds,:), 'LossFun', 'quadratic'); %! assert (L1, 2.8711, 1e-4); %! assert (L2, 0.5333, 1e-4); %! assert (L3, 10.9685, 1e-4); %! assert (L4, 1.9827, 1e-4); %! assert (L5, 1.5849, 1e-4); %! assert (L6, 7.6739, 1e-4); ## Test input validation for loss method %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1))) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2)) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones(2,1), "LossFun") %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), [], zeros (2)) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), 1, zeros (2)) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), []) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), 1) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "LossFun", 1) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "LossFun", "some") %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "Weights", ['a','b']) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "Weights", 'a') %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "Weights", [1,2,3]) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "Weights", 3) %!error ... %! loss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), zeros (2), ... %! ones (2,1), "some", "some") ## Test input validation for resubLoss method %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "LossFun") %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "LossFun", 1) %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "LossFun", "some") %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "Weights", ['a','b']) %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "Weights", 'a') %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "Weights", [1,2,3]) %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "Weights", 3) %!error ... %! resubLoss (ClassificationSVM (ones (40,2), randi ([1, 2], 40, 1)), "some", "some") ## Test output for crossval method %!test %! SVMModel = fitcsvm (x, y); %! CVMdl = crossval (SVMModel, "KFold", 5); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (CVMdl.KFold == 5) %! assert (class (CVMdl.Trained{1}), "CompactClassificationSVM") %! assert (CVMdl.CrossValidatedModel, "ClassificationSVM") %!test %! obj = fitcsvm (x, y); %! CVMdl = crossval (obj, "HoldOut", 0.2); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationSVM") %! assert (CVMdl.CrossValidatedModel, "ClassificationSVM") %!test %! obj = fitcsvm (x, y); %! CVMdl = crossval (obj, "LeaveOut", 'on'); %! assert (class (CVMdl), "ClassificationPartitionedModel") %! assert ({CVMdl.X, CVMdl.Y}, {x, y}) %! assert (class (CVMdl.Trained{1}), "CompactClassificationSVM") %! assert (CVMdl.CrossValidatedModel, "ClassificationSVM") ## Test input validation for crossval method %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "KFold") %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), ... %! "KFold", 5, "leaveout", 'on') %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "KFold", 'a') %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "KFold", 1) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "KFold", -1) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "KFold", 11.5) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "KFold", [1,2]) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "Holdout", 'a') %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "Holdout", 11.5) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "Holdout", -1) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "Holdout", 0) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "Holdout", 1) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "Leaveout", 1) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "CVPartition", 1) %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "CVPartition", 'a') %!error ... %! crossval (ClassificationSVM (ones (40,2),randi([1, 2], 40, 1)), "some", "some") statistics-release-1.7.3/inst/Classification/CompactClassificationDiscriminant.m000066400000000000000000000772661475240274700302650ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef CompactClassificationDiscriminant ## -*- texinfo -*- ## @deftypefn {statistics} CompactClassificationDiscriminant ## ## A @qcode{CompactClassificationDiscriminant} object is a compact version of a ## discriminant analysis model, @qcode{ClassificationDiscriminant}. ## ## The @qcode{CompactClassificationDiscriminant} does not include the training ## data resulting to a smaller classifier size, which can be used for making ## predictions from new data, but not for tasks such as cross validation. It ## can only be created from a @qcode{ClassificationDiscriminant} model by using ## the @code{compact} object method. ## ## The available methods for a @qcode{CompactClassificationDiscriminant} object ## are: ## @itemize ## @item ## @code{predict} ## @item ## @code{loss} ## @item ## @code{margin} ## @item ## @code{savemodel} ## @end itemize ## ## @seealso{fitcdiscr, compact, ClassificationDiscriminant} ## @end deftypefn properties (Access = public) NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of Misclassification ScoreTransform = []; # Transformation for classification scores Sigma = []; # Within-class covariance Mu = []; # Class means Coeffs = []; # Coefficient matrices Delta = []; # Threshold for linear discriminant model DiscrimType = []; # Discriminant type Gamma = []; # Gamma regularization parameter MinGamma = []; # Minmum value of Gamma LogDetSigma = []; # Log of det of within-class covariance matrix XCentered = []; # X data with class means subtracted endproperties methods (Hidden) ## constructor function this = CompactClassificationDiscriminant (Mdl = []) ## Check for appropriate class if (isempty (Mdl)) return; elseif (! strcmpi (class (Mdl), "ClassificationDiscriminant")) error (strcat (["CompactClassificationDiscriminant: invalid"], ... [" classification object."])); endif ## Save properties to compact model this.NumPredictors = Mdl.NumPredictors; this.PredictorNames = Mdl.PredictorNames; this.ResponseName = Mdl.ResponseName; this.ClassNames = Mdl.ClassNames; this.Prior = Mdl.Prior; this.Cost = Mdl.Cost; this.ScoreTransform = Mdl.ScoreTransform; this.Sigma = Mdl.Sigma; this.Mu = Mdl.Mu; this.Coeffs = Mdl.Coeffs; this.Delta = Mdl.Delta; this.DiscrimType = Mdl.DiscrimType; this.Gamma = Mdl.Gamma; this.MinGamma = Mdl.MinGamma; this.LogDetSigma = Mdl.LogDetSigma; this.XCentered = Mdl.XCentered; endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {CompactClassificationDiscriminant} {@var{label} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {CompactClassificationDiscriminant} {[@var{label}, @var{score}, @var{cost}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the discriminant ## analysis model from a CompactClassificationDiscriminant object. ## ## @code{@var{label} = predict (@var{obj}, @var{XC})} returns the vector of ## labels predicted for the corresponding instances in @var{XC}, using the ## corresponding labels from the trained @qcode{ClassificationDiscriminant}, ## model, @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{CompactClassificationDiscriminant} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the discriminant ## model in @var{obj}. ## @end itemize ## ## @code{[@var{label}, @var{score}, @var{cost}] = predict (@var{obj}, ## @var{XC})} also returns @var{score}, which contains the predicted class ## scores or posterior probabilities for each instance of the corresponding ## unique classes, and @var{cost}, which is a matrix containing the expected ## cost of the classifications. ## ## The @var{score} matrix contains the posterior probabilities for each ## class, calculated using the multivariate normal probability density ## function and the prior probabilities of each class. These scores are ## normalized to ensure they sum to 1 for each observation. ## ## The @var{cost} matrix contains the expected classification cost for each ## class, computed based on the posterior probabilities and the specified ## misclassification costs. ## ## @seealso{CompactClassificationDiscriminant, fitcdiscr} ## @end deftypefn function [label, score, cost] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error ("CompactClassificationDiscriminant.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("CompactClassificationDiscriminant.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["CompactClassificationDiscriminant.predict: XC"], ... [" must have the same number of features as the"], ... [" trained model."])); endif ## Initialize matrices numObservations = rows (XC); numClasses = numel (this.ClassNames); score = zeros (numObservations, numClasses); cost = zeros (numObservations, numClasses); ## Calculate discriminant score (posterior probabilities) for i = 1:numClasses for j = 1:numObservations P_x_given_k = mvnpdf (XC(j, :), this.Mu(i, :), this.Sigma); score(j, i) = P_x_given_k * this.Prior(i); endfor endfor ## Normalize score to get posterior probabilities scoreSum = sum (score, 2); score = bsxfun (@rdivide, score, scoreSum); ## Handle numerical issues score(isnan (score)) = 0; ## Calculate expected classification cost for i = 1:numClasses cost(:, i) = sum (bsxfun (@times, score, this.Cost(:, i)'), 2); endfor ## Predict the class labels based on the minimum cost [~, minIdx] = min (cost, [], 2); label = this.ClassNames(minIdx); endfunction ## -*- texinfo -*- ## @deftypefn {CompactClassificationDiscriminant} {@var{L} =} loss (@var{obj}, @var{X}, @var{Y}) ## @deftypefnx {CompactClassificationDiscriminant} {@var{L} =} loss (@dots{}, @var{name}, @var{value}) ## ## Compute loss for a trained CompactClassificationDiscriminant object. ## ## @code{@var{L} = loss (@var{obj}, @var{X}, @var{Y})} computes the loss, ## @var{L}, using the default loss function @qcode{'mincost'}. ## ## @itemize ## @item ## @code{obj} is a @var{CompactClassificationDiscriminant} object trained on ## @code{X} and @code{Y}. ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or ## variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels ## of corresponding predictor data in @var{X}. @var{Y} must have same ## numbers of Rows as @var{X}. ## @end itemize ## ## @code{@var{L} = loss (@dots{}, @var{name}, @var{value})} allows ## additional options specified by @var{name}-@var{value} pairs: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"LossFun"} @tab @tab Specifies the loss function to use. ## Can be a function handle with four input arguments (C, S, W, Cost) ## which returns a scalar value or one of: ## 'binodeviance', 'classifcost', 'classiferror', 'exponential', ## 'hinge', 'logit','mincost', 'quadratic'. ## @itemize ## @item ## @code{C} is a logical matrix of size @math{NxK}, where @math{N} is the ## number of observations and @math{K} is the number of classes. ## The element @code{C(i,j)} is true if the class label of the i-th ## observation is equal to the j-th class. ## @item ## @code{S} is a numeric matrix of size @math{NxK}, where each element ## represents the classification score for the corresponding class. ## @item ## @code{W} is a numeric vector of length @math{N}, representing ## the observation weights. ## @item ## @code{Cost} is a @math{KxK} matrix representing the misclassification ## costs. ## @end itemize ## ## @item @qcode{"Weights"} @tab @tab Specifies observation weights, must be ## a numeric vector of length equal to the number of rows in X. ## Default is @code{ones (size (X, 1))}. loss normalizes the weights so that ## observation weights in each class sum to the prior probability of that ## class. When you supply Weights, loss computes the weighted ## classification loss. ## ## @end multitable ## ## @seealso{CompactClassificationDiscriminant} ## @end deftypefn function L = loss (this, X, Y, varargin) ## Check for sufficient input arguments if (nargin < 3) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" too few input arguments."])); elseif (mod (nargin - 3, 2) != 0) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" name-value arguments must be in pairs."])); elseif (nargin > 7) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" too many input arguments."])); endif ## Default values LossFun = 'mincost'; Weights = []; ## Validate Y valid_types = {'char', 'string', 'logical', 'single', 'double', 'cell'}; if (! (any (strcmp (class (Y), valid_types)))) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" Y must be of a valid type."])); endif ## Validate size of Y if (size (Y, 1) != size (X, 1)) error (strcat (["CompactClassificationDiscriminant.loss: Y must"], ... [" have the same number of rows as X."])); endif ## Parse name-value arguments while (numel (varargin) > 0) Value = varargin{2}; switch (tolower (varargin{1})) case 'lossfun' lf_opt = {"binodeviance", "classifcost", "classiferror", ... "exponential", "hinge","logit", "mincost", "quadratic"}; if (isa (Value, 'function_handle')) ## Check if the loss function is valid if (nargin (Value) != 4) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" custom loss function must accept"], ... [" exactly four input arguments."])); endif try n = 1; K = 2; C_test = false (n, K); S_test = zeros (n, K); W_test = ones (n, 1); Cost_test = ones (K) - eye (K); test_output = Value (C_test, S_test, W_test, Cost_test); if (! isscalar (test_output)) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" custom loss function must return"], ... [" a scalar value."])); endif catch error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" custom loss function is not valid or"], ... [" does not produce correct output."])); end_try_catch LossFun = Value; elseif (ischar (Value) && any (strcmpi (Value, lf_opt))) LossFun = Value; else error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" invalid loss function."])); endif case 'weights' if (isnumeric (Value) && isvector (Value)) if (numel (Value) != size (X ,1)) error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" number of 'Weights' must be equal to"], ... [" the number of rows in X."])); elseif (numel (Value) == size (X, 1)) Weights = Value; endif else error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" invalid 'Weights'."])); endif otherwise error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" invalid parameter name in optional pair"], ... [" arguments."])); endswitch varargin (1:2) = []; endwhile ## Check for missing values in X if (! isa (LossFun, 'function_handle')) lossfun = tolower (LossFun); if (! strcmp (lossfun, 'mincost') && ! strcmp (lossfun, 'classiferror') && ! strcmp (lossfun, 'classifcost') && any (isnan (X(:)))) L = NaN; return; endif endif ## Convert Y to a cell array of strings if (ischar (Y)) Y = cellstr (Y); elseif (isnumeric (Y)) Y = cellstr (num2str (Y)); elseif (islogical (Y)) Y = cellstr (num2str (double (Y))); elseif (iscell (Y)) Y = cellfun (@num2str, Y, 'UniformOutput', false); else error (strcat (["CompactClassificationDiscriminant.loss: Y must be"], ... [" a numeric, logical, char, string, or cell array."])); endif ## Check if Y contains correct classes if (! all (ismember (unique (Y), this.ClassNames))) error (strcat (["CompactClassificationDiscriminant.loss: Y must"], ... [" contain only the classes in ClassNames."])); endif ## Set default weights if not specified if (isempty (Weights)) Weights = ones (size (X, 1), 1); endif ## Normalize Weights unique_classes = this.ClassNames; class_prior_probs = this.Prior; norm_weights = zeros (size (Weights)); for i = 1:numel (unique_classes) class_idx = ismember (Y, unique_classes{i}); if (sum (Weights(class_idx)) > 0) norm_weights(class_idx) = ... Weights(class_idx) * class_prior_probs(i) / sum (Weights(class_idx)); endif endfor Weights = norm_weights / sum (norm_weights); ## Number of observations n = size (X, 1); ## Predict classification scores [label, scores] = predict (this, X); ## C is vector of K-1 zeros, with 1 in the ## position corresponding to the true class K = numel (this.ClassNames); C = false (n, K); for i = 1:n class_idx = find (ismember (this.ClassNames, Y{i})); C(i, class_idx) = true; endfor Y_new = C'; ## Compute the loss using custom loss function if (isa (LossFun, 'function_handle')) L = LossFun (C, scores, Weights, this.Cost); return; endif ## Compute the scalar classification score for each observation m_j = zeros (n, 1); for i = 1:n m_j(i) = scores(i,:) * Y_new(:,i); endfor ## Compute the loss switch (tolower (LossFun)) case 'binodeviance' b = log (1 + exp (-2 * m_j)); L = (Weights') * b; case 'hinge' h = max (0, 1 - m_j); L = (Weights') * h; case 'exponential' e = exp (-m_j); L = (Weights') * e; case 'logit' l = log (1 + exp (-m_j)); L = (Weights') * l; case 'quadratic' q = (1 - m_j) .^ 2; L = (Weights') * q; case 'classiferror' L = 0; for i = 1:n L = L + Weights(i) * (! isequal (Y(i), label(i))); endfor case 'mincost' Cost = this.Cost; L = 0; for i = 1:n f_Xj = scores(i, :); gamma_jk = f_Xj * Cost; [~, min_cost_class] = min (gamma_jk); cj = Cost(find (ismember (this.ClassNames, Y(i))), min_cost_class); L = L + Weights(i) * cj; endfor case 'classifcost' Cost = this.Cost; L = 0; for i = 1:n y_idx = find (ismember (this.ClassNames, Y(i))); y_hat_idx = find (ismember (this.ClassNames, label(i))); L = L + Weights(i) * Cost(y_idx, y_hat_idx); endfor otherwise error (strcat (["CompactClassificationDiscriminant.loss:"], ... [" invalid loss function."])); endswitch endfunction ## -*- texinfo -*- ## @deftypefn {CompactClassificationDiscriminant} {@var{m} =} margin (@var{obj}, @var{X}, @var{Y}) ## ## @code{@var{m} = margin (@var{obj}, @var{X}, @var{Y})} returns ## the classification margins for @var{obj} with data @var{X} and ## classification @var{Y}. @var{m} is a numeric vector of length size (X,1). ## ## @itemize ## @item ## @code{obj} is a @var{CompactClassificationDiscriminant} object trained on @code{X} ## and @code{Y}. ## @item ## @code{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or ## variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels ## of corresponding predictor data in @var{X}. @var{Y} must have same ## numbers of Rows as @var{X}. ## @end itemize ## ## The classification margin for each observation is the difference between ## the classification score for the true class and the maximal ## classification score for the false classes. ## ## @seealso{fitcdiscr, CompactClassificationDiscriminant} ## @end deftypefn function m = margin (this, X, Y) ## Check for sufficient input arguments if (nargin < 3) error (strcat (["CompactClassificationDiscriminant.margin:"], ... [" too few input arguments."])); endif ## Validate Y valid_types = {'char', 'string', 'logical', 'single', 'double', 'cell'}; if (! (any (strcmp (class (Y), valid_types)))) error (strcat (["CompactClassificationDiscriminant.margin:"], ... [" Y must be of a valid type."])); endif ## Validate X valid_types = {'single', 'double'}; if (! (any (strcmp (class (X), valid_types)))) error (strcat (["CompactClassificationDiscriminant.margin:"], ... [" X must be of a valid type."])); endif ## Validate size of Y if (size (Y, 1) != size (X, 1)) error (strcat (["CompactClassificationDiscriminant.margin: Y must"], ... [" have the same number of rows as X."])); endif ## Convert Y to a cell array of strings if (ischar (Y)) Y = cellstr (Y); elseif (isnumeric (Y)) Y = cellstr (num2str (Y)); elseif (islogical (Y)) Y = cellstr (num2str (double (Y))); elseif (iscell (Y)) Y = cellfun (@num2str, Y, 'UniformOutput', false); else error (strcat (["CompactClassificationDiscriminant.margin: Y must be"], ... [" a numeric, logical, char, string, or cell array."])); endif ## Check if Y contains correct classes if (! all (ismember (unique (Y), this.ClassNames))) error (strcat (["CompactClassificationDiscriminant.margin: Y must"], ... [" contain only the classes in ClassNames."])); endif ## Number of Observations n = size (X, 1); ## Initialize the margin vector m = zeros (n, 1); ## Calculate the classification scores [~, scores] = predict (this, X); ## Loop over each observation to compute the margin for i = 1:n ## True class index true_class_idx = find (ismember (this.ClassNames, Y{i})); ## Score for the true class true_class_score = scores(i, true_class_idx); ## Get the maximal score for the false classes scores(i, true_class_idx) = -Inf; # Temporarily max_false_class_score = max (scores(i, :)); if (max_false_class_score == -Inf) m = NaN; return; endif scores(i, true_class_idx) = true_class_score; # Restore ## Calculate the margin m(i) = true_class_score - max_false_class_score; endfor endfunction ## -*- texinfo -*- ## @deftypefn {CompactClassificationDiscriminant} {} savemodel (@var{obj}, @var{filename}) ## ## Save a CompactClassificationDiscriminant object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ## CompactClassificationDiscriminant object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcdiscr, CompactClassificationDiscriminant} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "CompactClassificationDiscriminant"; ## Create variables from model properties NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; Sigma = this.Sigma; Mu = this.Mu; Coeffs = this.Coeffs; Delta = this.Delta; DiscrimType = this.DiscrimType; Gamma = this.Gamma; MinGamma = this.MinGamma; LogDetSigma = this.LogDetSigma; XCentered = this.XCentered; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "NumPredictors", "PredictorNames", ... "ResponseName", "ClassNames", "Prior", "Cost", "ScoreTransform", ... "Sigma", "Mu", "Coeffs", "Delta", "DiscrimType", "Gamma", ... "MinGamma", "LogDetSigma", "XCentered"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a CompactClassificationDiscriminant object mdl = CompactClassificationDiscriminant (); ## Check that fieldnames in DATA match properties in ## CompactClassificationDiscriminant names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) msg = strcat (["CompactClassificationDiscriminant.load_model:"], ... [" invalid model in '%s'."]); error (msg, filename); endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction endmethods endclassdef %!demo %! ## Create a discriminant analysis classifier and its compact version %! # and compare their size %! %! load fisheriris %! X = meas; %! Y = species; %! %! Mdl = fitcdiscr (X, Y, 'ClassNames', unique (species)) %! CMdl = crossval (Mdl) ## Test constructor %!test %! load fisheriris %! x = meas; %! y = species; %! PredictorNames = {'Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width'}; %! Mdl = fitcdiscr (x, y, "PredictorNames", PredictorNames); %! CMdl = compact (Mdl); %! sigma = [0.265008, 0.092721, 0.167514, 0.038401; ... %! 0.092721, 0.115388, 0.055244, 0.032710; ... %! 0.167514, 0.055244, 0.185188, 0.042665; ... %! 0.038401, 0.032710, 0.042665, 0.041882]; %! mu = [5.0060, 3.4280, 1.4620, 0.2460; ... %! 5.9360, 2.7700, 4.2600, 1.3260; ... %! 6.5880, 2.9740, 5.5520, 2.0260]; %! xCentered = [ 9.4000e-02, 7.2000e-02, -6.2000e-02, -4.6000e-02; ... %! -1.0600e-01, -4.2800e-01, -6.2000e-02, -4.6000e-02; ... %! -3.0600e-01, -2.2800e-01, -1.6200e-01, -4.6000e-02]; %! assert (class (CMdl), "CompactClassificationDiscriminant"); %! assert ({CMdl.DiscrimType, CMdl.ResponseName}, {"linear", "Y"}) %! assert ({CMdl.Gamma, CMdl.MinGamma}, {0, 0}, 1e-15) %! assert (CMdl.ClassNames, unique (species)) %! assert (CMdl.Sigma, sigma, 1e-6) %! assert (CMdl.Mu, mu, 1e-14) %! assert (CMdl.XCentered([1:3],:), xCentered, 1e-14) %! assert (CMdl.LogDetSigma, -9.9585, 1e-4) %! assert (CMdl.PredictorNames, PredictorNames) %!test %! load fisheriris %! x = meas; %! y = species; %! Mdl = fitcdiscr (x, y, "Gamma", 0.5); %! CMdl = compact (Mdl); %! sigma = [0.265008, 0.046361, 0.083757, 0.019201; ... %! 0.046361, 0.115388, 0.027622, 0.016355; ... %! 0.083757, 0.027622, 0.185188, 0.021333; ... %! 0.019201, 0.016355, 0.021333, 0.041882]; %! mu = [5.0060, 3.4280, 1.4620, 0.2460; ... %! 5.9360, 2.7700, 4.2600, 1.3260; ... %! 6.5880, 2.9740, 5.5520, 2.0260]; %! xCentered = [ 9.4000e-02, 7.2000e-02, -6.2000e-02, -4.6000e-02; ... %! -1.0600e-01, -4.2800e-01, -6.2000e-02, -4.6000e-02; ... %! -3.0600e-01, -2.2800e-01, -1.6200e-01, -4.6000e-02]; %! assert (class (CMdl), "CompactClassificationDiscriminant"); %! assert ({CMdl.DiscrimType, CMdl.ResponseName}, {"linear", "Y"}) %! assert ({CMdl.Gamma, CMdl.MinGamma}, {0.5, 0}) %! assert (CMdl.ClassNames, unique (species)) %! assert (CMdl.Sigma, sigma, 1e-6) %! assert (CMdl.Mu, mu, 1e-14) %! assert (CMdl.XCentered([1:3],:), xCentered, 1e-14) %! assert (CMdl.LogDetSigma, -8.6884, 1e-4) ## Test input validation for constructor %!error ... %! CompactClassificationDiscriminant (1) ## Test predict method %!test %! load fisheriris %! x = meas; %! y = species; %! Mdl = fitcdiscr (meas, species, "Gamma", 0.5); %! CMdl = compact (Mdl); %! [label, score, cost] = predict (CMdl, [2, 2, 2, 2]); %! assert (label, {'versicolor'}) %! assert (score, [0, 0.9999, 0.0001], 1e-4) %! assert (cost, [1, 0.0001, 0.9999], 1e-4) %! [label, score, cost] = predict (CMdl, [2.5, 2.5, 2.5, 2.5]); %! assert (label, {'versicolor'}) %! assert (score, [0, 0.6368, 0.3632], 1e-4) %! assert (cost, [1, 0.3632, 0.6368], 1e-4) %!test %! load fisheriris %! x = meas; %! y = species; %! xc = [min(x); mean(x); max(x)]; %! Mdl = fitcdiscr (x, y); %! CMdl = compact (Mdl); %! [label, score, cost] = predict (CMdl, xc); %! l = {'setosa'; 'versicolor'; 'virginica'}; %! s = [1, 0, 0; 0, 1, 0; 0, 0, 1]; %! c = [0, 1, 1; 1, 0, 1; 1, 1, 0]; %! assert (label, l) %! assert (score, s, 1e-4) %! assert (cost, c, 1e-4) %!shared MODEL %! X = rand (10,2); %! Y = [ones(5,1);2*ones(5,1)]; %! MODEL = compact (ClassificationDiscriminant (X, Y)); ## Test input validation for predict method %!error ... %! predict (MODEL) %!error ... %! predict (MODEL, []) %!error ... %! predict (MODEL, 1) ## Test loss method %!test %! load fisheriris %! model = fitcdiscr (meas, species); %! x = mean (meas); %! y = {'versicolor'}; %! L = loss (model, x, y); %! assert (L, 0) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y, "Gamma", 0.4); %! x_test = [1, 6; 3, 3]; %! y_test = {'A'; 'B'}; %! L = loss (model, x_test, y_test); %! assert (L, 0.3333, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3]; %! y_test = ['1']; %! L = loss (model, x_test, y_test, 'LossFun', 'quadratic'); %! assert (L, 0.2423, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3; 5, 7]; %! y_test = ['1'; '2']; %! L = loss (model, x_test, y_test, 'LossFun', 'classifcost'); %! assert (L, 0.3333, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3; 5, 7]; %! y_test = ['1'; '2']; %! L = loss (model, x_test, y_test, 'LossFun', 'hinge'); %! assert (L, 0.5886, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8]; %! y = ['1'; '2'; '3'; '1']; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_test = [3, 3; 5, 7]; %! y_test = ['1'; '2']; %! W = [1; 2]; %! L = loss (model, x_test, y_test, 'LossFun', 'logit', 'Weights', W); %! assert (L, 0.5107, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y, "gamma" , 0.5); %! x_with_nan = [1, 2; NaN, 4]; %! y_test = {'A'; 'B'}; %! L = loss (model, x_with_nan, y_test); %! assert (L, 0.3333, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y); %! x_with_nan = [1, 2; NaN, 4]; %! y_test = {'A'; 'B'}; %! L = loss (model, x_with_nan, y_test, 'LossFun', 'logit'); %! assert (isnan (L)) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = {'A'; 'B'; 'A'}; %! model = fitcdiscr (x, y); %! customLossFun = @(C, S, W, Cost) sum (W .* sum (abs (C - S), 2)); %! L = loss (model, x, y, 'LossFun', customLossFun); %! assert (L, 0.8889, 1e-4) %!test %! x = [1, 2; 3, 4; 5, 6]; %! y = [1; 2; 1]; %! model = fitcdiscr (x, y); %! L = loss (model, x, y, 'LossFun', 'classiferror'); %! assert (L, 0.3333, 1e-4) ## Test input validation for loss method %!error ... %! loss (MODEL) %!error ... %! loss (MODEL, ones (4,2)) %!error ... %! loss (MODEL, ones (4,2), ones (4,1), 'LossFun') %!error ... %! loss (MODEL, ones (4,2), ones (3,1)) %!error ... %! loss (MODEL, ones (4,2), ones (4,1), 'LossFun', 'a') %!error ... %! loss (MODEL, ones (4,2), ones (4,1), 'Weights', 'w') ## Test margin method %! load fisheriris %! mdl = fitcdiscr (meas, species); %! X = mean (meas); %! Y = {'versicolor'}; %! m = margin (mdl, X, Y); %! assert (m, 1, 1e-6) %!test %! X = [1, 2; 3, 4; 5, 6]; %! Y = [1; 2; 1]; %! mdl = fitcdiscr (X, Y, "gamma", 0.5); %! m = margin (mdl, X, Y); %! assert (m, [0.3333; -0.3333; 0.3333], 1e-4) ## Test input validation for margin method %!error ... %! margin (MODEL) %!error ... %! margin (MODEL, ones (4,2)) %!error ... %! margin (MODEL, ones (4,2), ones (3,1)) statistics-release-1.7.3/inst/Classification/CompactClassificationGAM.m000066400000000000000000000366621475240274700262400ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef CompactClassificationGAM ## -*- texinfo -*- ## @deftypefn {statistics} CompactClassificationGAM ## ## A @qcode{CompactClassificationGAM} object is a compact version of a ## Generalized Additive Model, @qcode{ClassificationGAM}. ## ## The @qcode{CompactClassificationDiscriminant} does not include the training ## data resulting to a smaller classifier size, which can be used for making ## predictions from new data, but not for tasks such as cross validation. It ## can only be created from a @qcode{ClassificationDiscriminant} model by using ## the @code{compact} object method. ## ## The available methods for a @qcode{CompactClassificationGAM} object ## are: ## @itemize ## @item ## @code{predict} ## @item ## @code{savemodel} ## @end itemize ## ## @seealso{fitcgam, compact, ClassificationGAM} ## @end deftypefn properties (Access = public) NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variable names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of Misclassification ScoreTransform = []; # Transformation for classification scores Formula = []; # Formula for GAM model Interactions = []; # Number or matrix of interaction terms Knots = []; # Knots of spline fitting Order = []; # Order of spline fitting DoF = []; # Degrees of freedom for fitting spline LearningRate = []; # Learning rate for training NumIterations = []; # Max number of iterations for training BaseModel = []; # Base model parameters (no interactions) ModelwInt = []; # Model parameters with interactions IntMatrix = []; # Interactions matrix applied to predictor data endproperties methods (Hidden) ## Class object constructor function this = CompactClassificationGAM (Mdl = []) ## Check for appropriate class if (isempty (Mdl)) return; elseif (! strcmpi (class (Mdl), "ClassificationGAM")) error ("CompactClassificationGAM: invalid classification object."); endif ## Save properties to compact model this.NumPredictors = Mdl.NumPredictors; this.PredictorNames = Mdl.PredictorNames; this.ResponseName = Mdl.ResponseName; this.ClassNames = Mdl.ClassNames; this.Prior = Mdl.Prior; this.Cost = Mdl.Cost; this.ScoreTransform = Mdl.ScoreTransform; this.Formula = Mdl.Formula; this.Interactions = Mdl.Interactions; this.Knots = Mdl.Knots; this.Order = Mdl.Order; this.DoF = Mdl.DoF; this.LearningRate = Mdl.LearningRate; this.NumIterations = Mdl.NumIterations; this.BaseModel = Mdl.BaseModel; this.ModelwInt = Mdl.ModelwInt; this.IntMatrix = Mdl.IntMatrix; endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {CompactClassificationGAM} {@var{label} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {CompactClassificationGAM} {@var{label} =} predict (@dots{}, @qcode{'IncludeInteractions'}, @var{includeInteractions}) ## @deftypefnx {CompactClassificationGAM} {[@var{label}, @var{score}] =} predict (@dots{}) ## ## Predict labels for new data using the Generalized Additive Model (GAM) ## stored in a CompactClassificationGAM object. ## ## @code{@var{label} = predict (@var{obj}, @var{XC})} returns the predicted ## labels for the data in @var{XC} based on the model stored in the ## CompactClassificationGAM object, @var{obj}. ## ## @code{@var{label} = predict (@var{obj}, @var{XC}, 'IncludeInteractions', ## @var{includeInteractions})} allows you to specify whether interaction ## terms should be included when making predictions. ## ## @code{[@var{label}, @var{score}] = predict (@dots{})} also returns ## @var{score}, which contains the predicted class scores or posterior ## probabilities for each observation. ## ## @itemize ## @item ## @var{obj} must be a @qcode{CompactClassificationGAM} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix where each row is an ## observation and each column corresponds to a predictor variable. ## @item ## @var{includeInteractions} is a 'true' or 'false' indicating whether to ## include interaction terms in the predictions. ## @end itemize ## ## @seealso{CompactClassificationGAM, fitcgam} ## @end deftypefn function [labels, scores] = predict (this, XC, varargin) ## Check for sufficient input arguments if (nargin < 2) error ("CompactClassificationGAM.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("CompactClassificationGAM.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["CompactClassificationGAM.predict: XC must have"], ... [" the same number of features as the trained model."])); endif ## Clean XC data notnansf = ! logical (sum (isnan (XC), 2)); XC = XC (notnansf, :); ## Default values for Name-Value Pairs incInt = ! isempty (this.IntMatrix); Cost = this.Cost; ## Parse optional arguments while (numel (varargin) > 0) switch (tolower (varargin {1})) case "includeinteractions" tmpInt = varargin{2}; if (! islogical (tmpInt) || (tmpInt != 0 && tmpInt != 1)) error (strcat (["CompactClassificationGAM.predict:"], ... [" includeinteractions must be a logical value."])); endif ## Check model for interactions if (tmpInt && isempty (this.IntMatrix)) error (strcat (["CompactClassificationGAM.predict: trained"], ... [" model does not include any interactions."])); endif incInt = tmpInt; otherwise error (strcat (["CompactClassificationGAM.predict: invalid"], ... [" NAME in optional pairs of arguments."])); endswitch varargin (1:2) = []; endwhile ## Choose whether interactions must be included if (incInt) if (! isempty (this.Interactions)) ## Append interaction terms to the predictor matrix for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = XC(:,tindex); Xinter = ones (rows (XC), 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append interaction terms XC = [XC, Xinter]; endfor else ## Add selected predictors and interaction terms XN = []; for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = XC(:,tindex); Xinter = ones (rows (XC), 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append selected predictors and interaction terms XN = [XN, Xinter]; endfor XC = XN; endif ## Get parameters and intercept vectors from model with interactions params = this.ModelwInt.Parameters; Interc = this.ModelwInt.Intercept; else ## Get parameters and intercept vectors from base model params = this.BaseModel.Parameters; Interc = this.BaseModel.Intercept; endif ## Predict probabilities from testing data scores = predict_val (params, XC, Interc); ## Compute the expected misclassification cost matrix numObservations = size (XC, 1); CE = zeros (numObservations, 2); for k = 1:2 for i = 1:2 CE(:, k) = CE(:, k) + scores(:, i) * Cost(k, i); endfor endfor ## Select the class with the minimum expected misclassification cost [~, minIdx] = min (CE, [], 2); labels = this.ClassNames (minIdx); endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationGAM} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationGAM object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ClassificationGAM ## object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcgam, ClassificationGAM, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "ClassificationGAM"; ## Create variables from model properties NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; Formula = this.Formula; Interactions = this.Interactions; Knots = this.Knots; Order = this.Order; DoF = this.DoF; LearningRate = this.LearningRate; NumIterations = this.NumIterations; BaseModel = this.BaseModel; ModelwInt = this.ModelwInt; IntMatrix = this.IntMatrix; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "NumPredictors", "PredictorNames", ... "ResponseName", "ClassNames", "Prior", "Cost", "ScoreTransform", ... "Formula", "Interactions", "Knots", "Order", "DoF", "BaseModel", ... "ModelwInt", "IntMatrix", "LearningRate", "NumIterations"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationGAM object mdl = CompactClassificationGAM (); ## Check that fieldnames in DATA match properties in ClassificationGAM names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) error ("ClassificationGAM.load_model: invalid model in '%s'.", filename) endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction endmethods endclassdef ## Helper function function scores = predict_val (params, XC, intercept) [nsample, ndims_X] = size (XC); ypred = ones (nsample, 1) * intercept; ## Add the remaining terms for j = 1:ndims_X ypred = ypred + ppval (params(j), XC (:,j)); endfor ## Apply the sigmoid function to get probabilities pos_prob = 1 ./ (1 + exp (-ypred)); neg_prob = 1 - pos_prob; scores = [neg_prob, pos_prob]; endfunction %!demo %! ## Create a generalized additive model classifier and its compact version %! # and compare their size %! %! load fisheriris %! X = meas; %! Y = species; %! %! Mdl = fitcdiscr (X, Y, 'ClassNames', unique (species)) %! CMdl = crossval (Mdl) ## Test constructor %!test %! Mdl = CompactClassificationGAM (); %! assert (class (Mdl), "CompactClassificationGAM") %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [0; 0; 1; 1]; %! PredictorNames = {'Feature1', 'Feature2', 'Feature3'}; %! Mdl = fitcgam (x, y, "PredictorNames", PredictorNames); %! CMdl = compact (Mdl); %! assert (class (CMdl), "CompactClassificationGAM"); %! assert ({CMdl.NumPredictors, CMdl.ResponseName}, {3, "Y"}) %! assert (CMdl.ClassNames, {'0'; '1'}) %! assert (CMdl.PredictorNames, PredictorNames) %! assert (CMdl.BaseModel.Intercept, 0) %!test %! load fisheriris %! inds = strcmp (species,'versicolor') | strcmp (species,'virginica'); %! X = meas(inds, :); %! Y = species(inds, :)'; %! Y = strcmp (Y, 'virginica')'; %! Mdl = fitcgam (X, Y, 'Formula', 'Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3'); %! CMdl = compact (Mdl); %! assert (class (CMdl), "CompactClassificationGAM"); %! assert ({CMdl.NumPredictors, CMdl.ResponseName}, {4, "Y"}) %! assert (CMdl.ClassNames, {'0'; '1'}) %! assert (CMdl.Formula, 'Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3') %! assert (CMdl.PredictorNames, {'x1', 'x2', 'x3', 'x4'}) %! assert (CMdl.ModelwInt.Intercept, 0) %!test %! X = [2, 3, 5; 4, 6, 8; 1, 2, 3; 7, 8, 9; 5, 4, 3]; %! Y = [0; 1; 0; 1; 1]; %! Mdl = fitcgam (X, Y, 'Knots', [4, 4, 4], 'Order', [3, 3, 3]); %! CMdl = compact (Mdl); %! assert (class (CMdl), "CompactClassificationGAM"); %! assert ({CMdl.NumPredictors, CMdl.ResponseName}, {3, "Y"}) %! assert (CMdl.ClassNames, {'0'; '1'}) %! assert (CMdl.PredictorNames, {'x1', 'x2', 'x3'}) %! assert (CMdl.Knots, [4, 4, 4]) %! assert (CMdl.Order, [3, 3, 3]) %! assert (CMdl.DoF, [7, 7, 7]) %! assert (CMdl.BaseModel.Intercept, 0.4055, 1e-1) ## Test input validation for constructor %!error ... %! CompactClassificationGAM (1) ## Test predict method %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8; 9, 10]; %! y = [1; 0; 1; 0; 1]; %! Mdl = fitcgam (x, y, "interactions", "all"); %! CMdl = compact (Mdl); %! l = {'0'; '0'; '0'; '0'; '0'}; %! s = [0.3760, 0.6240; 0.4259, 0.5741; 0.3760, 0.6240; ... %! 0.4259, 0.5741; 0.3760, 0.6240]; %! [labels, scores] = predict (CMdl, x); %! assert (class (CMdl), "CompactClassificationGAM"); %! assert ({CMdl.NumPredictors, CMdl.ResponseName}, {2, "Y"}) %! assert (CMdl.ClassNames, {'1'; '0'}) %! assert (CMdl.PredictorNames, {'x1', 'x2'}) %! assert (CMdl.ModelwInt.Intercept, 0.4055, 1e-1) %! assert (labels, l) %! assert (scores, s, 1e-1) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [0; 0; 1; 1]; %! interactions = [false, true, false; true, false, true; false, true, false]; %! Mdl = fitcgam (x, y, "learningrate", 0.2, "interactions", interactions); %! CMdl = compact (Mdl); %! [label, score] = predict (CMdl, x, "includeinteractions", true); %! l = {'0'; '0'; '1'; '1'}; %! s = [0.5106, 0.4894; 0.5135, 0.4865; 0.4864, 0.5136; 0.4847, 0.5153]; %! assert (class (CMdl), "CompactClassificationGAM"); %! assert ({CMdl.NumPredictors, CMdl.ResponseName}, {3, "Y"}) %! assert (CMdl.ClassNames, {'0'; '1'}) %! assert (CMdl.PredictorNames, {'x1', 'x2', 'x3'}) %! assert (CMdl.ModelwInt.Intercept, 0) %! assert (label, l) %! assert (score, s, 1e-1) ## Test input validation for predict method %!shared CMdl %! Mdl = fitcgam (ones (4,2), ones (4,1)); %! CMdl = compact (Mdl); %!error ... %! predict (CMdl) %!error ... %! predict (CMdl, []) %!error ... %! predict (CMdl, 1) statistics-release-1.7.3/inst/Classification/CompactClassificationNeuralNetwork.m000066400000000000000000000254751475240274700304340ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef CompactClassificationNeuralNetwork ## -*- texinfo -*- ## @deftypefn {statistics} CompactClassificationNeuralNetwork ## ## A @qcode{CompactClassificationNeuralNetwork} object is a compact version of a ## discriminant analysis model, @qcode{CompactClassificationNeuralNetwork}. ## ## The @qcode{CompactClassificationDiscriminant} does not include the training ## data resulting to a smaller classifier size, which can be used for making ## predictions from new data, but not for tasks such as cross validation. It ## can only be created from a @qcode{ClassificationNeuralNetwork} model by using ## the @code{compact} object method. ## ## The available methods for a @qcode{CompactClassificationNeuralNetwork} object ## are: ## @itemize ## @item ## @code{predict} ## @item ## @code{savemodel} ## @end itemize ## ## @seealso{fitcdiscr, ClassificationDiscriminant} ## @end deftypefn properties (Access = public) NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y ScoreTransform = []; # Transformation for classification scores Standardize = []; # Flag to standardize predictors Sigma = []; # Predictor standard deviations Mu = []; # Predictor means LayerSizes = []; # Size of fully connected layers Activations = []; # Activation functions for hidden layers OutputLayerActivation = []; # Activation function for output layer LearningRate = []; # Learning rate for gradient descend IterationLimit = []; # Number of training epochs ModelParameters = []; # Model parameters ConvergenceInfo = []; # Training history DisplayInfo = []; # Display information during training Solver = []; # Solver used endproperties methods (Hidden) ## constructor function this = CompactClassificationNeuralNetwork (Mdl = []) ## Check for appropriate class if (isempty (Mdl)) return; elseif (! strcmpi (class (Mdl), "ClassificationNeuralNetwork")) error (strcat (["CompactClassificationNeuralNetwork: invalid"], ... [" classification object."])); endif ## Save properties to compact model this.NumPredictors = Mdl.NumPredictors; this.PredictorNames = Mdl.PredictorNames; this.ResponseName = Mdl.ResponseName; this.ClassNames = Mdl.ClassNames; this.ScoreTransform = Mdl.ScoreTransform; this.Standardize = Mdl.Standardize; this.Sigma = Mdl.Sigma; this.Mu = Mdl.Mu; this.LayerSizes = Mdl.LayerSizes; this.Activations = Mdl.Activations; this.OutputLayerActivation = Mdl.OutputLayerActivation; this.LearningRate = Mdl.LearningRate; this.IterationLimit = Mdl.IterationLimit; this.ModelParameters = Mdl.ModelParameters; this.ConvergenceInfo = Mdl.ConvergenceInfo; this.DisplayInfo = Mdl.DisplayInfo; this.Solver = Mdl.Solver; endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {CompactClassificationNeuralNetwork} {@var{labels} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {CompactClassificationNeuralNetwork} {[@var{labels}, @var{scores}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the Neural Network ## classification object. ## ## @code{@var{labels} = predict (@var{obj}, @var{XC})} returns the vector of ## labels predicted for the corresponding instances in @var{XC}, using the ## trained neural network classification compact model in @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{CompactClassificationNeuralNetwork} class ## object. ## @item ## @var{X} must be an @math{MxP} numeric matrix with the same number of ## predictors @math{P} as the corresponding predictors of the trained neural ## network compact model in @var{obj}. ## @end itemize ## ## @code{[@var{labels}, @var{scores}] = predict (@var{obj}, @var{XC}} also ## returns @var{scores}, which represent the probability of each label ## belonging to a specific class. For each observation in X, the predicted ## class label is the one with the highest score among all classes. ## Alternatively, @var{scores} can contain the posterior probabilities if ## the ScoreTransform has been previously set. ## ## @seealso{fitcnet, ClassificationNeuralNetwork, ## CompactClassificationNeuralNetwork} ## @end deftypefn function [labels, scores] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error (strcat (["CompactClassificationNeuralNetwork.predict:"], ... [" too few input arguments."])); endif ## Check for valid XC if (isempty (XC)) error ("CompactClassificationNeuralNetwork.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["CompactClassificationNeuralNetwork.predict:"], ... [" XC must have the same number of predictors"], ... [" as the trained neural network model."])); endif ## Standardize (if necessary) if (this.Standardize) XC = (XC - this.Mu) ./ this.Sigma; endif ## Predict labels from new data [labels, scores] = fcnnpredict (this.ModelParameters, XC); # Get class labels labels = this.ClassNames(labels); if (nargout > 1) ## Apply ScoreTransform to return probability estimates if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["CompactClassificationNeuralNetwork.predict:"], ... [" 'ScoreTransform' must be a"], ... [" 'function_handle' object."])); endif scores = f (scores); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {ClassificationNeuralNetwork} {} savemodel (@var{obj}, @var{filename}) ## ## Save a ClassificationNeuralNetwork object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ## ClassificationNeuralNetwork object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcnet, ClassificationNeuralNetwork, cvpartition, ## ClassificationPartitionedModel} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "CompactClassificationNeuralNetwork"; ## Create variables from model properties NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; ScoreTransform = this.ScoreTransform; Standardize = this.Standardize; Sigma = this.Sigma; Mu = this.Mu; LayerSizes = this.LayerSizes; Activations = this.Activations; OutputLayerActivation = this.OutputLayerActivation; LearningRate = this.LearningRate; IterationLimit = this.IterationLimit; ModelParameters = this.ModelParameters; ConvergenceInfo = this.ConvergenceInfo; DislayInfo = this.DislayInfo; Solver = this.Solver; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "NumPredictors", "PredictorNames", ... "ResponseName", "ClassNames", "ScoreTransform", "Standardize", ... "Sigma", "Mu", "LayerSizes", "Activations", ... "OutputLayerActivation", "LearningRate", "IterationLimit", ... "Solver", "ModelParameters", "ConvergenceInfo", "DislayInfo"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationNeuralNetwork object mdl = CompactClassificationNeuralNetwork (); ## Get fieldnames from DATA (including private properties) names = fieldnames (data); ## Copy data into object for i = 1:numel (names) ## Check fieldnames in DATA match properties in ## CompactClassificationNeuralNetwork try mdl.(names{i}) = data.(names{i}); catch error (strcat (["CompactClassificationNeuralNetwork.load_model:"], ... [" invalid model in '%s'."]), filename) end_try_catch endfor endfunction endmethods endclassdef %!demo %! ## Create a neural network classifier and its compact version %! # and compare their size %! %! load fisheriris %! X = meas; %! Y = species; %! %! Mdl = fitcnet (X, Y, 'ClassNames', unique (species)) %! CMdl = crossval (Mdl) ## Test input validation for constructor %!error ... %! CompactClassificationDiscriminant (1) ## Test output for predict method %!shared x, y, CMdl %! load fisheriris %! x = meas; %! y = grp2idx (species); %! Mdl = fitcnet (x, y, "IterationLimit", 100); %! CMdl = compact (Mdl); ## Test input validation for predict method %!error ... %! predict (CMdl) %!error ... %! predict (CMdl, []) %!error ... %! predict (CMdl, 1) %!test %! CMdl.ScoreTransform = "a"; %!error ... %! [labels, scores] = predict (CMdl, x); statistics-release-1.7.3/inst/Classification/CompactClassificationSVM.m000066400000000000000000000615361475240274700262770ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef CompactClassificationSVM ## -*- texinfo -*- ## @deftypefn {statistics} CompactClassificationSVM ## ## A @qcode{CompactClassificationSVM} object is a compact version of a support ## vectors machine model, @qcode{CompactClassificationSVM}. ## ## The @qcode{CompactClassificationSVM} does not include the training data ## resulting to a smaller classifier size, which can be used for making ## predictions from new data, but not for tasks such as cross validation. It ## can only be created from a @qcode{ClassificationSVM} model by using the ## @code{compact} object method. ## ## The available methods for a @qcode{CompactClassificationSVM} object ## are: ## @itemize ## @item ## @code{predict} ## @item ## @code{loss} ## @item ## @code{margin} ## @item ## @code{savemodel} ## @end itemize ## ## @seealso{fitcsvm, ClassificationSVM} ## @end deftypefn properties (Access = public) NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variables names ResponseName = []; # Response variable name ClassNames = []; # Names of classes in Y Prior = []; # Prior probability for each class Cost = []; # Cost of misclassification ScoreTransform = []; # Transformation for classification scores Standardize = []; # Flag to standardize predictors Sigma = []; # Predictor standard deviations Mu = []; # Predictor means ModelParameters = []; # SVM parameters Model = []; # Stores the 'libsvm' trained model Alpha = []; # Trained classifier coefficients Beta = []; # Linear predictor coefficients Bias = []; # Bias term IsSupportVector = []; # Indices of Support vectors SupportVectorLabels = []; # Support vector class labels SupportVectors = []; # Support vectors endproperties methods (Hidden) ## constructor function this = CompactClassificationSVM (Mdl = []) ## Check for appropriate class if (isempty (Mdl)) return; elseif (! strcmpi (class (Mdl), "ClassificationSVM")) error (strcat (["CompactClassificationSVM: invalid"], ... [" classification object."])); endif ## Save properties to compact model this.NumPredictors = Mdl.NumPredictors; this.PredictorNames = Mdl.PredictorNames; this.ResponseName = Mdl.ResponseName; this.ClassNames = Mdl.ClassNames; this.Prior = Mdl.Prior; this.Cost = Mdl.Cost; this.ScoreTransform = Mdl.ScoreTransform; this.Standardize = Mdl.Standardize; this.Sigma = Mdl.Sigma; this.Mu = Mdl.Mu; this.ModelParameters = Mdl.ModelParameters; this.Model = Mdl.Model; this.Alpha = Mdl.Alpha; this.Beta = Mdl.Beta; this.Bias = Mdl.Bias; this.IsSupportVector = Mdl.IsSupportVector; this.SupportVectorLabels = Mdl.SupportVectorLabels; this.SupportVectors = Mdl.SupportVectors; endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {CompactClassificationSVM} {@var{labels} =} predict (@var{obj}, @var{XC}) ## @deftypefnx {CompactClassificationSVM} {[@var{labels}, @var{scores}] =} predict (@var{obj}, @var{XC}) ## ## Classify new data points into categories using the Support Vector Machine ## classification object. ## ## @code{@var{labels} = predict (@var{obj}, @var{XC})} returns the vector of ## labels predicted for the corresponding instances in @var{XC}, using the ## trained Support Vector Machine classification compact model, @var{obj}. ## For one-class SVM model, +1 or -1 is returned. ## ## @itemize ## @item ## @var{obj} must be a @qcode{CompactClassificationSVM} class object. ## @item ## @var{XC} must be an @math{MxP} numeric matrix with the same number of ## predictors @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @end itemize ## ## @code{[@var{labels}, @var{scores}] = predict (@var{obj}, @var{XC}} also ## returns @var{scores}, which contains the desicion values for each each ## prediction. Alternatively, @var{scores} can contain the posterior ## probabilities if the ScoreTransform has been previously set using the ## @code{fitPosterior} method. ## ## @seealso{fitcsvm, ClassificationSVM.fitPosterior} ## @end deftypefn function [labels, scores] = predict (this, XC) ## Check for sufficient input arguments if (nargin < 2) error ("CompactClassificationSVM.predict: too few input arguments."); endif ## Check for valid XC if (isempty (XC)) error ("CompactClassificationSVM.predict: XC is empty."); elseif (this.NumPredictors != columns (XC)) error (strcat (["CompactClassificationSVM.predict: XC must have"], ... [" the same number of predictors as the trained"], ... [" SVM model."])); endif ## Standardize (if necessary) if (this.Standardize) XC = (XC - this.Mu) ./ this.Sigma; endif ## Predict labels and scores from new data [out, ~, scores] = svmpredict (ones (rows (XC), 1), XC, this.Model, '-q'); ## Expand scores for two classes if (numel (this.ClassNames) == 2) scores = [scores, -scores]; endif ## Translate labels to classnames if (iscellstr (this.ClassNames)) labels = cell (rows (XC), 1); labels(out==1) = this.ClassNames{1}; labels(out!=1) = this.ClassNames{2}; elseif (islogical (this.ClassNames)) labels = false (rows (XC), 1); elseif (isnumeric (this.ClassNames)) labels = zeros (rows (XC), 1); elseif (ischar (this.ClassNames)) labels = char (zeros (rows (XC), size (this.ClassNames, 2))); endif if (! iscellstr (this.ClassNames)) labels(out==1) = this.ClassNames(1); labels(out!=1) = this.ClassNames(2); endif if (nargout > 1) ## Apply ScoreTransform to return probability estimates if (! strcmp (this.ScoreTransform, "none")) f = this.ScoreTransform; if (! strcmp (class (f), "function_handle")) error (strcat (["CompactClassificationSVM.predict: 'Score"], ... ["Transform' must be a 'function_handle' object."])); endif scores = f (scores); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {CompactClassificationSVM} {@var{m} =} margin (@var{obj}, @var{X}, @var{Y}) ## ## Determine the classification margins for a Support Vector Machine ## classification object. ## ## @code{@var{m} = margin (@var{obj}, @var{X}, @var{Y})} returns the ## classification margins for the trained support vector machine (SVM) ## classifier @var{obj} using the sample data in @var{X} and the class ## labels in @var{Y}. It supports only binary classifier models. The ## classification margin is commonly defined as @var{m} = @var{y}f(@var{x}), ## where @var{f(x)} is the classification score and @var{y} is the true ## class label corresponding to @var{x}. A greater margin indicates a better ## model. ## ## @itemize ## @item ## @var{obj} must be a binary class @qcode{CompactClassificationSVM} object. ## @item ## @var{X} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @item ## @var{Y} must be @math{Mx1} numeric vector containing the class labels ## corresponding to the predictor data in @var{X}. @var{Y} must have same ## number of rows as @var{X}. ## @end itemize ## ## @seealso{fitcsvm, CompactClassificationSVM} ## @end deftypefn function m = margin (this, X, Y) ## Check for sufficient input arguments if (nargin < 3) error ("CompactClassificationSVM.margin: too few input arguments."); endif ## Check for valid X if (isempty (X)) error ("CompactClassificationSVM.margin: X is empty."); elseif (this.NumPredictors != columns (X)) error (strcat (["CompactClassificationSVM.margin: X must"], ... [" have the same number of predictors as"], ... [" the trained SVM model."])); endif ## Check for valid Y if (isempty (Y)) error ("CompactClassificationSVM.margin: Y is empty."); elseif (rows (X) != rows (Y)) error (strcat (["CompactClassificationSVM.margin: Y must have"], ... [" the same number of rows as X."])); endif [~, ~, dec_values_L] = svmpredict (Y, X, this.Model, '-q'); m = 2 * Y .* dec_values_L; endfunction ## -*- texinfo -*- ## @deftypefn {CompactClassificationSVM} {@var{L} =} loss (@var{obj}, @var{X}, @var{Y}) ## @deftypefnx {CompactClassificationSVM} {@var{L} =} loss (@dots{}, @var{name}, @var{value}) ## ## Determine the classification error for a Support Vector Machine ## classifier. ## ## @code{@var{L} = loss (@var{obj}, @var{X}, @var{Y})} returns the ## predictive accuracy of support vector machine (SVM) classification models. ## Comparing the same type of loss across multiple models allows you to ## identify which model is more accurate, with a lower loss indicating ## superior predictive performance. It supports only binary classifier ## models. ## ## @itemize ## @item ## @var{obj} must be a binary class @qcode{CompactClassificationSVM} object. ## @item ## @var{X} must be an @math{MxP} numeric matrix with the same number of ## features @math{P} as the corresponding predictors of the SVM model in ## @var{obj}. ## @item ## @var{Y} must be @math{Mx1} numeric vector containing the class labels ## corresponding to the predictor data in @var{X}. @var{Y} must have same ## number of rows as @var{X}. ## @end itemize ## ## @code{@var{L} = loss (@dots{}, @var{Name}, @var{Value})} returns the ## aforementioned results with additional properties specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"LossFun"} @tab @tab Loss function, specified as a built-in ## loss function name. It accepts the following options: (Default is ## 'classiferror') ## ## @itemize ## ## @item 'binodeviance': Binomial deviance: ## The binomial deviance loss function is used to evaluate the performance ## of a binary classifier. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \log \{1 + \exp [-2m_j]\}} ## ## @item 'classiferror': Misclassification rate in decimal ## The classification error measures the fraction of misclassified instances ## out of the total instances. It is calculated as: ## @math{L = \frac{1}{n} \sum_{j=1}^{n} \mathbb{I}(m_j \leq 0)} ## ## @item 'exponential': Exponential loss: ## The exponential loss function is used to penalize misclassified instances ## exponentially. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \exp [-m_j]} ## ## @item 'hinge': Hinge loss: ## The hinge loss function is often used for maximum-margin classification, ## particularly for support vector machines. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \max (0, 1 - m_j)} ## ## @item 'logit': Logistic loss: ## The logistic loss function, also known as log loss, measures the ## performance of a classification model where the prediction is a ## probability value. It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j \log \{1 + \exp [-m_j]\}} ## ## @item 'quadratic': Quadratic loss: ## The quadratic loss function penalizes the square of the margin. ## It is calculated as: ## @math{L = \sum_{j=1}^{n} w_j (1 - m_j)^2} ## ## @end itemize ## ## @item @qcode{"Weights"} @tab @tab Specified as a numeric vector which ## weighs each observation (row) in X. The size of Weights must be equal ## to the number of rows in X. The default value is: ones(size(X,1),1) ## ## @end multitable ## ## @seealso{fitcsvm, ClassificationSVM} ## @end deftypefn function L = loss (this, X, Y, varargin) ## Check for sufficient input arguments if (nargin < 3) error ("CompactClassificationSVM.loss: too few input arguments."); endif if (mod (nargin, 2) == 0) error (strcat (["CompactClassificationSVM.loss: Name-Value"], ... [" arguments must be in pairs."])); endif ## Check for valid X if (isempty (X)) error ("CompactClassificationSVM.loss: X is empty."); elseif (this.NumPredictors != columns (X)) error (strcat (["CompactClassificationSVM.loss: X must"], ... [" have the same number of predictors as"], ... [" the trained SVM model."])); endif ## Check for valid Y if (isempty (Y)) error ("CompactClassificationSVM.loss: Y is empty."); elseif (rows (X)!= rows (Y)) error (strcat (["CompactClassificationSVM.loss: Y must have"], ... [" the same number of rows as X."])); endif ## Set default values before parsing optional parameters LossFun = 'classiferror'; Weights = ones (size (X, 1), 1); ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "lossfun" LossFun = varargin{2}; if (! (ischar (LossFun))) error (strcat (["CompactClassificationSVM.loss: 'LossFun'"], ... [" must be a character vector."])); endif LossFun = tolower (LossFun); if (! any (strcmpi (LossFun, {"binodeviance", "classiferror", ... "exponential", "hinge", "logit", ... "quadratic"}))) error (strcat (["CompactClassificationSVM.loss:"], ... [" unsupported Loss function."])); endif case "weights" Weights = varargin{2}; ## Validate if weights is a numeric vector if(! (isnumeric (Weights) && isvector (Weights))) error (strcat (["CompactClassificationSVM.loss: 'Weights'"], ... [" must be a numeric vector."])); endif ## Check if the size of weights matches the number of rows in X if (numel (Weights) != size (X, 1)) error (strcat (["CompactClassificationSVM.loss: size of"], ... [" 'Weights' must be equal to the number"], ... [" of rows in X."])); endif otherwise error (strcat (["CompactClassificationSVM.loss: invalid"], ... [" parameter name in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Compute the classification score [~, ~, dec_values_L] = svmpredict (Y, X, this.Model, '-q'); ## Compute the margin margin = Y .* dec_values_L; ## Compute the loss based on the specified loss function switch (LossFun) case "classiferror" L = mean ((margin <= 0) .* Weights); case "hinge" L = mean (max (0, 1 - margin) .* Weights); case "logit" L = mean (log (1 + exp (-margin)) .* Weights); case "exponential" L = mean (exp (-margin) .* Weights); case "quadratic" L = mean (((1 - margin) .^2) .* Weights); case "binodeviance" L = mean (log (1 + exp (-2 * margin)) .* Weights); otherwise error ("CompactClassificationSVM.loss: unsupported Loss function."); endswitch endfunction ## -*- texinfo -*- ## @deftypefn {CompactClassificationSVM} {} savemodel (@var{obj}, @var{filename}) ## ## Save a CompactClassificationSVM object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a ## CompactClassificationSVM object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitcsvm, ClassificationSVM, CompactClassificationSVM} ## @end deftypefn function savemodel (this, fname) ## Generate variable for class name classdef_name = "CompactClassificationSVM"; ## Create variables from model properties NumPredictors = this.NumPredictors; PredictorNames = this.PredictorNames; ResponseName = this.ResponseName; ClassNames = this.ClassNames; Prior = this.Prior; Cost = this.Cost; ScoreTransform = this.ScoreTransform; Standardize = this.Standardize; Sigma = this.Sigma; Mu = this.Mu; ModelParameters = this.ModelParameters; Model = this.Model; Alpha = this.Alpha; Beta = this.Beta; Bias = this.Bias; IsSupportVector = this.IsSupportVector; SupportVectorLabels = this.SupportVectorLabels; SupportVectors = this.SupportVectors; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "NumPredictors", "PredictorNames", ... "ResponseName", "ClassNames", "Prior", "Cost", "ScoreTransform", ... "Standardize", "Sigma", "Mu", "ModelParameters", "Model", ... "Alpha", "Beta", "Bias", "IsSupportVector", ... "SupportVectorLabels", "SupportVectors"); endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a ClassificationSVM object mdl = CompactClassificationSVM (); ## Check that fieldnames in DATA match properties in ## CompactClassificationSVM names = fieldnames (data); props = fieldnames (mdl); if (! isequal (sort (names), sort (props))) msg = "CompactClassificationSVM.load_model: invalid model in '%s'."; error (msg, filename); endif ## Copy data into object for i = 1:numel (props) mdl.(props{i}) = data.(props{i}); endfor endfunction endmethods endclassdef %!demo %! ## Create a support vectors machine classifier and its compact version %! # and compare their size %! %! load fisheriris %! X = meas; %! Y = species; %! %! Mdl = fitcsvm (X, Y, 'ClassNames', unique (species)) %! CMdl = crossval (Mdl) ## Test input validation for constructor %!error ... %! CompactClassificationSVM (1) ## Test output for predict method %!shared x, y, CMdl %! load fisheriris %! inds = ! strcmp (species, 'setosa'); %! x = meas(inds, 3:4); %! y = grp2idx (species(inds)); %!test %! xc = [min(x); mean(x); max(x)]; %! Mdl = fitcsvm (x, y, 'KernelFunction', 'rbf', 'Tolerance', 1e-7); %! CMdl = compact (Mdl); %! assert (isempty (CMdl.Alpha), true) %! assert (sum (CMdl.IsSupportVector), numel (CMdl.Beta)) %! [label, score] = predict (CMdl, xc); %! assert (label, [1; 2; 2]); %! assert (score(:,1), [0.99285; -0.080296; -0.93694], 1e-5); %! assert (score(:,1), -score(:,2), eps) %!test %! Mdl = fitcsvm (x, y); %! CMdl = compact (Mdl); %! assert (isempty (CMdl.Beta), true) %! assert (sum (CMdl.IsSupportVector), numel (CMdl.Alpha)) %! assert (numel (CMdl.Alpha), 24) %! assert (CMdl.Bias, -14.415, 1e-3) %! xc = [min(x); mean(x); max(x)]; %! label = predict (CMdl, xc); %! assert (label, [1; 2; 2]); ## Test input validation for predict method %!error ... %! predict (CMdl) %!error ... %! predict (CMdl, []) %!error ... %! predict (CMdl, 1) %!test %! CMdl.ScoreTransform = "a"; %!error ... %! [labels, scores] = predict (CMdl, x); ## Test output for margin method %!test %! rand ("seed", 1); %! C = cvpartition (y, 'HoldOut', 0.15); %! Mdl = fitcsvm (x(training (C),:), y(training (C)), ... %! 'KernelFunction', 'rbf', 'Tolerance', 1e-7); %! CMdl = compact (Mdl); %! testInds = test (C); %! expected_margin = [2.0000; 0.8579; 1.6690; 3.4141; 3.4552; ... %! 2.6605; 3.5251; -4.0000; -6.3411; -6.4511; ... %! -3.0532; -7.5054; -1.6700; -5.6227; -7.3640]; %! computed_margin = margin (CMdl, x(testInds,:), y(testInds,:)); %! assert (computed_margin, expected_margin, 1e-4); ## Test input validation for margin method %!error ... %! margin (CMdl) %!error ... %! margin (CMdl, zeros (2)) %!error ... %! margin (CMdl, [], 1) %!error ... %! margin (CMdl, 1, 1) %!error ... %! margin (CMdl, [1, 2], []) %!error ... %! margin (CMdl, [1, 2], [1; 2]) ## Test output for loss method %!test %! rand ("seed", 1); %! C = cvpartition (y, 'HoldOut', 0.15); %! Mdl = fitcsvm (x(training (C),:), y(training (C)), ... %! 'KernelFunction', 'rbf', 'Tolerance', 1e-7); %! CMdl = compact (Mdl); %! testInds = test (C); %! L1 = loss (CMdl, x(testInds,:), y(testInds,:), 'LossFun', 'binodeviance'); %! L2 = loss (CMdl, x(testInds,:), y(testInds,:), 'LossFun', 'classiferror'); %! L3 = loss (CMdl, x(testInds,:), y(testInds,:), 'LossFun', 'exponential'); %! L4 = loss (CMdl, x(testInds,:), y(testInds,:), 'LossFun', 'hinge'); %! L5 = loss (CMdl, x(testInds,:), y(testInds,:), 'LossFun', 'logit'); %! L6 = loss (CMdl, x(testInds,:), y(testInds,:), 'LossFun', 'quadratic'); %! assert (L1, 2.8711, 1e-4); %! assert (L2, 0.5333, 1e-4); %! assert (L3, 10.9685, 1e-4); %! assert (L4, 1.9827, 1e-4); %! assert (L5, 1.5849, 1e-4); %! assert (L6, 7.6739, 1e-4); ## Test input validation for loss method %!error ... %! loss (CMdl) %!error ... %! loss (CMdl, zeros (2)) %!error ... %! loss (CMdl, [1, 2], 1, "LossFun") %!error ... %! loss (CMdl, [], zeros (2)) %!error ... %! loss (CMdl, 1, zeros (2)) %!error ... %! loss (CMdl, [1, 2], []) %!error ... %! loss (CMdl, [1, 2], [1; 2]) %!error ... %! loss (CMdl, [1, 2], 1, "LossFun", 1) %!error ... %! loss (CMdl, [1, 2], 1, "LossFun", "some") %!error ... %! loss (CMdl, [1, 2], 1, "Weights", ['a', 'b']) %!error ... %! loss (CMdl, [1, 2], 1, "Weights", 'a') %!error ... %! loss (CMdl, [1, 2], 1, "Weights", [1, 2]) %!error ... %! loss (CMdl, [1, 2], 1, "some", "some") statistics-release-1.7.3/inst/Classification/ConfusionMatrixChart.m000066400000000000000000000715061475240274700255570ustar00rootroot00000000000000## Copyright (C) 2020-2021 Stefano Guidoni ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ConfusionMatrixChart < handle ## -*- texinfo -*- ## @deftypefn {statistics} {@var{cmc} =} ConfusionMatrixChart () ## Create object @var{cmc}, a Confusion Matrix Chart object. ## ## @table @asis ## @item @qcode{"DiagonalColor"} ## The color of the patches on the diagonal, default is [0.0, 0.4471, 0.7412]. ## ## @item @qcode{"OffDiagonalColor"} ## The color of the patches off the diagonal, default is [0.851, 0.3255, 0.098]. ## ## @item @qcode{"GridVisible"} ## Available values: @qcode{on} (default), @qcode{off}. ## ## @item @qcode{"Normalization"} ## Available values: @qcode{absolute} (default), @qcode{column-normalized}, ## @qcode{row-normalized}, @qcode{total-normalized}. ## ## @item @qcode{"ColumnSummary"} ## Available values: @qcode{off} (default), @qcode{absolute}, ## @qcode{column-normalized},@qcode{total-normalized}. ## ## @item @qcode{"RowSummary"} ## Available values: @qcode{off} (default), @qcode{absolute}, ## @qcode{row-normalized}, @qcode{total-normalized}. ## @end table ## ## MATLAB compatibility -- the not implemented properties are: FontColor, ## PositionConstraint, InnerPosition, Layout. ## ## @seealso{confusionchart} ## @end deftypefn properties (Access = public) ## text properties XLabel = "Predicted Class"; YLabel = "True Class"; Title = ""; FontName = ""; FontSize = 0; ## chart colours DiagonalColor = [0 0.4471 0.7412]; OffDiagonalColor = [0.8510 0.3255 0.0980]; ## data visualization Normalization = "absolute"; ColumnSummary = "off"; RowSummary = "off"; GridVisible = "on"; HandleVisibility = ""; OuterPosition = []; Position = []; Units = ""; endproperties properties (GetAccess = public, SetAccess = private) ClassLabels = {}; # a string cell array of classes NormalizedValues = []; # the normalized confusion matrix Parent = 0; # a handle to the parent object endproperties properties (Access = protected) hax = 0.0; # a handle to the axes ClassN = 0; # the number of classes AbsoluteValues = []; # the original confusion matrix ColumnSummaryAbsoluteValues = []; # default values of the column summary RowSummaryAbsoluteValues = []; # default values of the row summary endproperties methods (Access = public) ## class constructor ## inputs: axis handle, a confusion matrix, a list of class labels, ## an array of optional property-value pairs. function this = ConfusionMatrixChart (hax, cm, cl, args) ## class initialization this.hax = hax; this.Parent = get (this.hax, "parent"); this.ClassLabels = cl; this.NormalizedValues = cm; this.AbsoluteValues = cm; this.ClassN = rows (cm); this.FontName = get (this.hax, "fontname"); this.FontSize = get (this.hax, "fontsize"); set (this.hax, "xlabel", this.XLabel); set (this.hax, "ylabel", this.YLabel); ## draw the chart draw (this); ## apply paired properties if (! isempty (args)) pair_idx = 1; while (pair_idx < length (args)) switch (args{pair_idx}) case "XLabel" this.XLabel = args{pair_idx + 1}; case "YLabel" this.YLabel = args{pair_idx + 1}; case "Title" this.Title = args{pair_idx + 1}; case "FontName" this.FontName = args{pair_idx + 1}; case "FontSize" this.FontSize = args{pair_idx + 1}; case "DiagonalColor" this.DiagonalColor = args{pair_idx + 1}; case "OffDiagonalColor" this.OffDiagonalColor = args{pair_idx + 1}; case "Normalization" this.Normalization = args{pair_idx + 1}; case "ColumnSummary" this.ColumnSummary = args{pair_idx + 1}; case "RowSummary" this.RowSummary = args{pair_idx + 1}; case "GridVisible" this.GridVisible = args{pair_idx + 1}; case "HandleVisibility" this.HandleVisibility = args{pair_idx + 1}; case "OuterPosition" this.OuterPosition = args{pair_idx + 1}; case "Position" this.Position = args{pair_idx + 1}; case "Units" this.Units = args{pair_idx + 1}; otherwise close (this.Parent); error ("confusionchart: invalid property %s", args{pair_idx}); endswitch pair_idx += 2; endwhile endif ## init the color map updateColorMap (this); endfunction ## set functions function set.XLabel (this, string) if (! ischar (string)) close (this.Parent); error ("confusionchart: XLabel must be a string."); endif this.XLabel = updateAxesProperties (this, "xlabel", string); endfunction function set.YLabel (this, string) if (! ischar (string)) close (this.Parent); error ("confusionchart: YLabel must be a string."); endif this.YLabel = updateAxesProperties (this, "ylabel", string); endfunction function set.Title (this, string) if (! ischar (string)) close (this.Parent); error ("confusionchart: Title must be a string."); endif this.Title = updateAxesProperties (this, "title", string); endfunction function set.FontName (this, string) if (! ischar (string)) close (this.Parent); error ("confusionchart: FontName must be a string."); endif this.FontName = updateTextProperties (this, "fontname", string); endfunction function set.FontSize (this, value) if (! isnumeric (value)) close (this.Parent); error ("confusionchart: FontSize must be numeric."); endif this.FontSize = updateTextProperties (this, "fontsize", value); endfunction function set.DiagonalColor (this, color) if (ischar (color)) color = this.convertNamedColor (color); endif if (! (isvector (color) && length (color) == 3 )) close (this.Parent); error ("confusionchart: DiagonalColor must be a color."); endif this.DiagonalColor = color; updateColorMap (this); endfunction function set.OffDiagonalColor (this, color) if (ischar (color)) color = this.convertNamedColor (color); endif if (! (isvector (color) && length (color) == 3)) close (this.Parent); error ("confusionchart: OffDiagonalColor must be a color."); endif this.OffDiagonalColor = color; updateColorMap (this); endfunction function set.Normalization (this, string) if (! any (strcmp (string, {"absolute", "column-normalized",... "row-normalized", "total-normalized"}))) close (this.Parent); error ("confusionchart: invalid value for Normalization."); endif this.Normalization = string; updateChart (this); endfunction function set.ColumnSummary (this, string) if (! any (strcmp (string, {"off", "absolute", "column-normalized",... "total-normalized"}))) close (this.Parent); error ("confusionchart: invalid value for ColumnSummary."); endif this.ColumnSummary = string; updateChart (this); endfunction function set.RowSummary (this, string) if (! any (strcmp (string, {"off", "absolute", "row-normalized",... "total-normalized"}))) close (this.Parent); error ("confusionchart: invalid value for RowSummary."); endif this.RowSummary = string; updateChart (this); endfunction function set.GridVisible (this, string) if (! any (strcmp (string, {"off", "on"}))) close (this.Parent); error ("confusionchart: invalid value for GridVisible."); endif this.GridVisible = string; setGridVisibility (this); endfunction function set.HandleVisibility (this, string) if (! any (strcmp (string, {"off", "on", "callback"}))) close (this.Parent); error ("confusionchart: invalid value for HandleVisibility"); endif set (this.hax, "handlevisibility", string); endfunction function set.OuterPosition (this, vector) if (! isvector (vector) || ! isnumeric (vector) || length (vector) != 4) close (this.Parent); error ("confusionchart: invalid value for OuterPosition"); endif set (this.hax, "outerposition", vector); endfunction function set.Position (this, vector) if (! isvector (vector) || ! isnumeric (vector) || length (vector) != 4) close (this.Parent); error ("confusionchart: invalid value for Position"); endif set (this.hax, "position", vector); endfunction function set.Units (this, string) if (! any (strcmp (string, {"centimeters", "characters", "inches", ... "normalized", "pixels", "points"}))) close (this.Parent); error ("confusionchart: invalid value for Units"); endif set (this.hax, "units", string); endfunction ## -*- texinfo -*- ## @deftypefn {ConfusionMatrixChart} {} disp (@var{cmc}, @var{order}) ## Display the properties of the @code{ConfusionMatrixChart} object ## @var{cmc}. ## ## @end deftypefn function disp (this) nv_sizes = size (this.NormalizedValues); cl_sizes = size (this.ClassLabels); printf ("%s with properties:\n\n", class (this)); printf ("\tNormalizedValues: [ %dx%d %s ]\n", nv_sizes(1), nv_sizes(2),... class (this.NormalizedValues)); printf ("\tClassLabels: { %dx%d %s }\n\n", cl_sizes(1), cl_sizes(2),... class (this.ClassLabels)); endfunction ## -*- texinfo -*- ## @deftypefn {ConfusionMatrixChart} {} sortClasses (@var{cmc}, @var{order}) ## Sort the classes of the @code{ConfusionMatrixChart} object @var{cmc} ## according to @var{order}. ## ## Valid values for @var{order} can be an array or cell array including ## the same class labels as @var{cm}, or a value like @code{"auto"}, ## @code{"ascending-diagonal"}, @code{"descending-diagonal"} and ## @code{"cluster"}. ## ## @end deftypefn ## ## @seealso{confusionchart, linkage, pdist} function sortClasses (this, order) ## check the input parameters if (nargin != 2) print_usage (); endif cl = this.ClassLabels; cm_size = this.ClassN; nv = this.NormalizedValues; av = this.AbsoluteValues; cv = this.ColumnSummaryAbsoluteValues; rv = this.RowSummaryAbsoluteValues; scl = {}; Idx = []; if (strcmp (order, "auto")) [scl, Idx] = sort (cl); elseif (strcmp (order, "ascending-diagonal")) [s, Idx] = sort (diag (nv)); scl = cl(Idx); elseif (strcmp (order, "descending-diagonal")) [s, Idx] = sort (diag (nv)); Idx = flip (Idx); scl = cl(Idx); elseif (strcmp (order, "cluster")) ## the classes are all grouped together ## this way one can visually evaluate which are the most similar classes ## according to the learning algorithm D = zeros (1, ((cm_size - 1) * cm_size / 2)); # a pdist like vector maxD = 2 * max (max (av)); k = 1; # better than computing the index at every cycle for i = 1 : (cm_size - 1) for j = (i + 1) : cm_size D(k++) = maxD - (av(i, j) + av(j, i)); # distance endfor endfor tree = linkage (D, "average"); # clustering ## we could have optimal leaf ordering with Idx = optimalleaforder (tree, D); # optimal clustering ## [sorted_v Idx] = sort (cluster (tree, )); nodes_to_visit = 2 * cm_size - 1; nodecount = 0; while (! isempty (nodes_to_visit)) current_node = nodes_to_visit(1); nodes_to_visit(1) = []; if (current_node > cm_size) node = current_node - cm_size; nodes_to_visit = [tree(node,[2 1]) nodes_to_visit]; end if (current_node <= cm_size) nodecount++; Idx(nodecount) = current_node; end end ## scl = cl(Idx); else ## must be an array or cell array of labels if (! iscellstr (order)) if (! ischar (order)) if (isrow (order)) order = vec (order); endif order = num2str (order); endif scl = cellstr (order); endif if (length (scl) != length (cl)) error ("sortClasses: wrong size for order.") endif Idx = zeros (length (scl), 1); for i = 1 : length (scl) Idx(i) = find (strcmp (cl, scl{i})); endfor endif ## rearrange the normalized values... nv = nv(Idx, :); nv = nv(:, Idx); this.NormalizedValues = nv; ## ...and the absolute values... av = av(Idx, :); av = av(:, Idx); this.AbsoluteValues = av; cv = cv([Idx ( Idx + cm_size )]); this.ColumnSummaryAbsoluteValues = cv; rv = rv([Idx ( Idx + cm_size )]); this.RowSummaryAbsoluteValues = rv; ## ...and the class labels this.ClassLabels = scl; ## update the axes set (this.hax, "xtick", (0.5 : 1 : (cm_size - 0.5)), "xticklabel", scl,... "ytick", (0.5 : 1 : (cm_size - 0.5)), "yticklabel", scl); ## get text and patch handles kids = get (this.hax, "children"); t_kids = kids(find (isprop (kids, "fontname"))); # hack to find texts m_kid = kids(find (strcmp (get (kids, "userdata"), "MainChart"))); c_kid = kids(find (strcmp (get (kids, "userdata"), "ColumnSummary"))); r_kid = kids(find (strcmp (get (kids, "userdata"), "RowSummary"))); ## re-assign colors to the main chart cdata_m = reshape (get (m_kid, "cdata"), cm_size, cm_size); cdata_m = cdata_m(Idx, :); cdata_m = cdata_m(:, Idx); cdata_v = vec (cdata_m); set (m_kid, "cdata", cdata_v); ## re-assign colors to the column summary cdata_m = reshape (transpose (get (c_kid, "cdata")), cm_size, 2); cdata_m = cdata_m(Idx, :); cdata_v = vec (cdata_m); set (c_kid, "cdata", cdata_v); ## re-assign colors to the row summary cdata_m = reshape (get (r_kid, "cdata"), cm_size, 2); cdata_m = cdata_m(Idx, :); cdata_v = vec (cdata_m); set (r_kid, "cdata", cdata_v); ## move the text labels for i = 1:length (t_kids) t_pos = get (t_kids(i), "userdata"); if (t_pos(2) > cm_size) ## row summary t_pos(1) = find (Idx == (t_pos(1) + 1)) - 1; set (t_kids(i), "userdata", t_pos); t_pos = t_pos([2 1]) + 0.5; set (t_kids(i), "position", t_pos); elseif (t_pos(1) > cm_size) ## column summary t_pos(2) = find (Idx == (t_pos(2) + 1)) - 1; set (t_kids(i), "userdata", t_pos); t_pos = t_pos([2 1]) + 0.5; set (t_kids(i), "position", t_pos); else ## main chart t_pos(1) = find (Idx == (t_pos(1) + 1)) - 1; t_pos(2) = find (Idx == (t_pos(2) + 1)) - 1; set (t_kids(i), "userdata", t_pos); t_pos = t_pos([2 1]) + 0.5; set (t_kids(i), "position", t_pos); endif endfor updateChart (this); endfunction endmethods methods (Access = private) ## convertNamedColor ## convert a named colour to a colour triplet function ret = convertNamedColor (this, color) vColorNames = ["ymcrgbwk"]'; vColorTriplets = [1 1 0; 1 0 1; 0 1 1; 1 0 0; 0 1 0; 0 0 1; 1 1 1; 0 0 0]; if (strcmp (color, "black")) color = 'k'; endif index = find (vColorNames == color(1)); if (! isempty (index)) ret = vColorTriplets(index, :); else ret = []; # trigger an error message endif endfunction ## updateAxesProperties ## update the properties of the axes function ret = updateAxesProperties (this, prop, value) set (this.hax, prop, value); ret = value; endfunction ## updateTextProperties ## set the properties of the texts function ret = updateTextProperties (this, prop, value) hax_kids = get (this.hax, "children"); text_kids = hax_kids(isprop (hax_kids , "fontname")); # hack to find texts text_kids(end + 1) = get (this.hax, "xlabel"); text_kids(end + 1) = get (this.hax, "ylabel"); text_kids(end + 1) = get (this.hax, "title"); updateAxesProperties (this, prop, value); set (text_kids, prop, value); ret = value; endfunction ## setGridVisibility ## toggle the visibility of the grid function setGridVisibility (this) kids = get (this.hax, "children"); kids = kids(find (isprop (kids, "linestyle"))); if (strcmp (this.GridVisible, "on")) set (kids, "linestyle", "-"); else set (kids, "linestyle", "none"); endif endfunction ## updateColorMap ## change the colormap and, accordingly, the text colors function updateColorMap (this) cm_size = this.ClassN; d_color = this.DiagonalColor; o_color = this.OffDiagonalColor; ## quick hack d_color(find (d_color == 1.0)) = 0.999; o_color(find (o_color == 1.0)) = 0.999; ## 64 shades for each color cm_colormap(1:64,:) = [1.0 : (-(1.0 - o_color(1)) / 63) : o_color(1);... 1.0 : (-(1.0 - o_color(2)) / 63) : o_color(2);... 1.0 : (-(1.0 - o_color(3)) / 63) : o_color(3)]'; cm_colormap(65:128,:) = [1.0 : (-(1.0 - d_color(1)) / 63) : d_color(1);... 1.0 : (-(1.0 - d_color(2)) / 63) : d_color(2);... 1.0 : (-(1.0 - d_color(3)) / 63) : d_color(3)]'; colormap (this.hax, cm_colormap); ## update text colors kids = get (this.hax, "children"); t_kids = kids(find (isprop (kids, "fontname"))); # hack to find texts m_patch = kids(find (strcmp (get (kids, "userdata"), "MainChart"))); c_patch = kids(find (strcmp (get (kids, "userdata"), "ColumnSummary"))); r_patch = kids(find (strcmp (get (kids, "userdata"), "RowSummary"))); m_colors = get (m_patch, "cdata"); c_colors = get (c_patch, "cdata"); r_colors = get (r_patch, "cdata"); ## when a patch is dark, let's use a pale color for the text for i = 1 : length (t_kids) t_pos = get (t_kids(i), "userdata"); color_idx = 1; if (t_pos(2) > cm_size) ## row summary idx = (t_pos(2) - cm_size - 1) * cm_size + t_pos(1) + 1; color_idx = r_colors(idx) + 1; elseif (t_pos(1) > cm_size) ## column summary idx = (t_pos(1) - cm_size - 1) * cm_size + t_pos(2) + 1; color_idx = c_colors(idx) + 1; else ## main chart idx = t_pos(2) * cm_size + t_pos(1) + 1; color_idx = m_colors(idx) + 1; endif if (sum (cm_colormap(color_idx, :)) < 1.8) set (t_kids(i), "color", [.97 .97 1.0]); else set (t_kids(i), "color", [.15 .15 .15]); endif endfor endfunction ## updateChart ## update the text labels and the NormalizedValues property function updateChart (this) cm_size = this.ClassN; cm = this.AbsoluteValues; l_cs = this.ColumnSummaryAbsoluteValues; l_rs = this.RowSummaryAbsoluteValues; kids = get (this.hax, "children"); t_kids = kids(find (isprop (kids, "fontname"))); # hack to find texts normalization = this.Normalization; column_summary = this.ColumnSummary; row_summary = this.RowSummary; ## normalization for labelling row_totals = sum (cm, 2); col_totals = sum (cm, 1); mat_total = sum (col_totals); cm_labels = cm; add_percent = true; if (strcmp (normalization, "column-normalized")) for i = 1 : cm_size cm_labels(:,i) = cm_labels(:,i) ./ col_totals(i); endfor elseif (strcmp (normalization, "row-normalized")) for i = 1 : cm_size cm_labels(i,:) = cm_labels(i,:) ./ row_totals(i); endfor elseif (strcmp (normalization, "total-normalized")) cm_labels = cm_labels ./ mat_total; else add_percent = false; endif ## update NormalizedValues this.NormalizedValues = cm_labels; ## update axes last_row = cm_size; last_col = cm_size; userdata = cell2mat (get (t_kids, "userdata")); cs_kids = t_kids(find (userdata(:,1) > cm_size)); cs_kids(end + 1) = kids(find (strcmp (get (kids, "userdata"),... "ColumnSummary"))); if (! strcmp ("off", column_summary)) set (cs_kids, "visible", "on"); last_row += 3; else set (cs_kids, "visible", "off"); endif rs_kids = t_kids(find (userdata(:,2) > cm_size)); rs_kids(end + 1) = kids(find (strcmp (get (kids, "userdata"),... "RowSummary"))); if (! strcmp ("off", row_summary)) set (rs_kids, "visible", "on"); last_col += 3; else set (rs_kids, "visible", "off"); endif axis (this.hax, [0 last_col 0 last_row]); ## update column summary data cs_add_percent = true; if (! strcmp (column_summary, "off")) if (strcmp (column_summary, "column-normalized")) for i = 1 : cm_size if (col_totals(i) == 0) ## avoid division by zero l_cs([i (cm_size + i)]) = 0; else l_cs([i, cm_size + i]) = l_cs([i, cm_size + i]) ./ col_totals(i); endif endfor elseif strcmp (column_summary, "total-normalized") l_cs = l_cs ./ mat_total; else cs_add_percent = false; endif endif ## update row summary data rs_add_percent = true; if (! strcmp (row_summary, "off")) if (strcmp (row_summary, "row-normalized")) for i = 1 : cm_size if (row_totals(i) == 0) ## avoid division by zero l_rs([i (cm_size + i)]) = 0; else l_rs([i, cm_size + i]) = l_rs([i, cm_size + i]) ./ row_totals(i); endif endfor elseif (strcmp (row_summary, "total-normalized")) l_rs = l_rs ./ mat_total; else rs_add_percent = false; endif endif ## update text label_list = vec (cm_labels); for i = 1 : length (t_kids) t_pos = get (t_kids(i), "userdata"); new_string = ""; if (t_pos(2) > cm_size) ## this is the row summary idx = (t_pos(2) - cm_size - 1) * cm_size + t_pos(1) + 1; if (rs_add_percent) new_string = num2str (100.0 * l_rs(idx), "%3.1f"); new_string = [new_string "%"]; else new_string = num2str (l_rs(idx)); endif elseif (t_pos(1) > cm_size) ## this is the column summary idx = (t_pos(1) - cm_size - 1) * cm_size + t_pos(2) + 1; if (cs_add_percent) new_string = num2str (100.0 * l_cs(idx), "%3.1f"); new_string = [new_string "%"]; else new_string = num2str (l_cs(idx)); endif else ## this is the main chart idx = t_pos(2) * cm_size + t_pos(1) + 1; if (add_percent) new_string = num2str (100.0 * label_list(idx), "%3.1f"); new_string = [new_string "%"]; else new_string = num2str (label_list(idx)); endif endif set (t_kids(i), "string", new_string); endfor endfunction ## draw ## draw the chart function draw (this) cm = this.AbsoluteValues; cl = this.ClassLabels; cm_size = this.ClassN; ## set up the axes set (this.hax, "xtick", (0.5 : 1 : (cm_size - 0.5)), "xticklabel", cl,... "ytick", (0.5 : 1 : (cm_size - 0.5)), "yticklabel", cl ); axis ("ij"); axis (this.hax, [0 cm_size 0 cm_size]); ## prepare the patches indices_b = 0 : (cm_size -1); indices_v = repmat (indices_b, cm_size, 1); indices_vx = transpose (vec (indices_v)); indices_vy = vec (indices_v', 2); indices_ex = vec ((cm_size + 1) * [1; 2] .* ones (2, cm_size), 2); ## normalization for colorization ## it is used a colormap of 128 shades of two colors, 64 shades for each ## color normal = max (max (cm)); cm_norm = round (63 * cm ./ normal); cm_norm = cm_norm + 64 * eye (cm_size); ## default normalization: absolute cm_labels = vec (cm); ## the patches of the main chart x_patch = [indices_vx; ( indices_vx + 1 ); ( indices_vx + 1 ); indices_vx]; y_patch = [indices_vy; indices_vy; ( indices_vy + 1 ); ( indices_vy + 1 )]; c_patch = vec (cm_norm(1 : cm_size, 1 : cm_size)); ## display the patches ph = patch (this.hax, x_patch, y_patch, c_patch); set (ph, "userdata", "MainChart"); ## display the labels userdata = [indices_vy; indices_vx]'; nonzero_idx = find (cm_labels != 0); th = text ((x_patch(1, nonzero_idx) + 0.5), (y_patch(1, nonzero_idx) +... 0.5), num2str (cm_labels(nonzero_idx)), "parent", this.hax ); set (th, "horizontalalignment", "center"); for i = 1 : length (nonzero_idx) set (th(i), "userdata", userdata(nonzero_idx(i), :)); endfor ## patches for the summaries main_values = diag (cm); ct_values = sum (cm)'; rt_values = sum (cm, 2); cd_values = ct_values - main_values; rd_values = rt_values - main_values; ## column summary x_cs = [[indices_b indices_b]; ( [indices_b indices_b] + 1 ); ( [indices_b indices_b] + 1 ); [indices_b indices_b]]; y_cs = [(repmat ([1 1 2 2]', 1, cm_size)) (repmat ([2 2 3 3]', 1, cm_size))] +... cm_size; c_cs = [(round (63 * (main_values ./ ct_values)) + 64); (round (63 * (cd_values ./ ct_values)))]; c_cs(isnan (c_cs)) = 0; l_cs = [main_values; cd_values]; ph = patch (this.hax, x_cs, y_cs, c_cs); set (ph, "userdata", "ColumnSummary"); set (ph, "visible", "off" ); userdata = [y_cs(1,:); x_cs(1,:)]'; nonzero_idx = find (l_cs != 0); th = text ((x_cs(1,nonzero_idx) + 0.5), (y_cs(1,nonzero_idx) + 0.5),... num2str (l_cs(nonzero_idx)), "parent", this.hax); set (th, "horizontalalignment", "center"); for i = 1 : length (nonzero_idx) set (th(i), "userdata", userdata(nonzero_idx(i), :)); endfor set (th, "visible", "off"); ## row summary x_rs = y_cs; y_rs = x_cs; c_rs = [(round (63 * (main_values ./ rt_values)) + 64); (round (63 * (rd_values ./ rt_values)))]; c_rs(isnan (c_rs)) = 0; l_rs = [main_values; rd_values]; ph = patch (this.hax, x_rs, y_rs, c_rs); set (ph, "userdata", "RowSummary"); set (ph, "visible", "off"); userdata = [y_rs(1,:); x_rs(1,:)]'; nonzero_idx = find (l_rs != 0); th = text ((x_rs(1,nonzero_idx) + 0.5), (y_rs(1,nonzero_idx) + 0.5),... num2str (l_rs(nonzero_idx)), "parent", this.hax); set (th, "horizontalalignment", "center"); for i = 1 : length (nonzero_idx) set (th(i), "userdata", userdata(nonzero_idx(i), :)); endfor set (th, "visible", "off"); this.ColumnSummaryAbsoluteValues = l_cs; this.RowSummaryAbsoluteValues = l_rs; endfunction endmethods endclassdef %!demo %! ## Create a simple ConfusionMatrixChart Object %! %! cm = ConfusionMatrixChart (gca, [1 2; 1 2], {"A","B"}, {"XLabel","LABEL A"}) %! NormalizedValues = cm.NormalizedValues %! ClassLabels = cm.ClassLabels ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! cm = ConfusionMatrixChart (gca, [1 2; 1 2], {"A","B"}, {"XLabel","LABEL A"}); %! assert (isa (cm, "ConfusionMatrixChart"), true); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect statistics-release-1.7.3/inst/Classification/private/000077500000000000000000000000001475240274700227305ustar00rootroot00000000000000statistics-release-1.7.3/inst/Classification/private/parseScoreTransform.m000066400000000000000000000057361475240274700271230ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Private Function} parseScoreTransform (@var{pd}, @var{classname}) ## ## Parse ScoreTransform for Classification objects. ## ## @end deftypefn function f = parseScoreTransform (ScoreTransform, classname) stList = {"doublelogit", "invlogit", "ismax", "logit", "none", ... "identity", "sign", "symmetric", "symmetricismax", ... "symmetriclogit"}; if (! (ischar (ScoreTransform) || strcmp (class (ScoreTransform), "function_handle"))) error (strcat (["%s: 'ScoreTransform' must be a character"], ... [" vector or a function handle."]), classname); endif if (! ismember (ScoreTransform, stList)) error ("%s: unrecognized 'ScoreTransform' function.", classname); endif ## Handle ScoreTransform here if (is_function_handle (ScoreTransform)) m = eye (5); if (! isequal (size (m), size (ScoreTransform (m)))) error (strcat (["%s: function handle for 'ScoreTransform' must"], ... [" return the same size as its input."]), classname); endif f = ScoreTransform; else if (strcmpi ("doublelogit", ScoreTransform)) f = @(x) 1 ./ (1 + exp .^ (-2 * x)); elseif (strcmpi ("invlogit", ScoreTransform)) f = @(x) log (x ./ (1 - x)); elseif (strcmpi ("ismax", ScoreTransform)) f = eval (sprintf ("@(x) ismax (x)")); elseif (strcmpi ("logit", ScoreTransform)) f = @(x) 1 ./ (1 + exp .^ (-x)); elseif (any (strcmpi ({"identity", "none"}, ScoreTransform))) f = 'none'; elseif (strcmpi ("sign", ScoreTransform)) f = @(x) sign (x); elseif (strcmpi ("symmetric", ScoreTransform)) f = @(x) 2 * x - 1; elseif (strcmpi ("symmetricismax", ScoreTransform)) f = eval (sprintf ("@(x) symmetricismax (x)")); elseif (strcmpi ("symmetriclogit", ScoreTransform)) f = @(x) 2 ./ (1 + exp .^ (-x)) - 1; endif endif endfunction ## Helper functions for ScoreTransform function out = ismax (score) out = score; out(score == max (score)) = 1; out(score != max (score)) = 0; endfunction function out = symmetricismax (score) out = score; out(score == max (score)) = 1; out(score != max (score)) = -1; endfunction statistics-release-1.7.3/inst/Clustering/000077500000000000000000000000001475240274700204425ustar00rootroot00000000000000statistics-release-1.7.3/inst/Clustering/CalinskiHarabaszEvaluation.m000066400000000000000000000232301475240274700260610ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef CalinskiHarabaszEvaluation < ClusterCriterion ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} evalclusters (@var{x}, @var{clust}, @qcode{CalinskiHarabasz}) ## @deftypefnx {statistics} {@var{obj} =} evalclusters (@dots{}, @qcode{Name}, @qcode{Value}) ## ## A Calinski-Harabasz object to evaluate clustering solutions. ## ## A @code{CalinskiHarabaszEvaluation} object is a @code{ClusterCriterion} ## object used to evaluate clustering solutions using the Calinski-Harabasz ## criterion. ## ## The Calinski-Harabasz index is based on the ratio between SSb and SSw. ## SSb is the overall variance between clusters, that is the variance of the ## distances between the centroids. ## SSw is the overall variance within clusters, that is the sum of the ## variances of the distances between each datapoint and its centroid. ## ## The best solution according to the Calinski-Harabasz criterion is the one ## that scores the highest value. ## ## @seealso{evalclusters, ClusterCriterion, DaviesBouldinEvaluation, ## GapEvaluation, SilhouetteEvaluation} ## @end deftypefn properties (GetAccess = public, SetAccess = private) endproperties properties (Access = protected) Centroids = {}; # a list of the centroids for every solution endproperties methods (Access = public) ## constructor function this = CalinskiHarabaszEvaluation (x, clust, KList) this@ClusterCriterion(x, clust, KList); this.CriterionName = "CalinskiHarabasz"; this.evaluate(this.InspectedK); # evaluate the list of cluster numbers endfunction ## -*- texinfo -*- ## @deftypefn {CalinskiHarabaszEvaluation} {@var{obj} =} addK (@var{obj}, @var{K}) ## ## Add a new cluster array to inspect the CalinskiHarabaszEvaluation object. ## ## @end deftypefn function this = addK (this, K) addK@ClusterCriterion(this, K); ## if we have new data, we need a new evaluation if (this.OptimalK == 0) Centroids_tmp = {}; pS = 0; # position shift of the elements of Centroids for iter = 1 : length (this.InspectedK) ## reorganize Centroids according to the new list of cluster numbers if (any (this.InspectedK(iter) == K)) pS += 1; else Centroids_tmp{iter} = this.Centroids{iter - pS}; endif endfor this.Centroids = Centroids_tmp; this.evaluate(K); # evaluate just the new cluster numbers endif endfunction ## -*- texinfo -*- ## @deftypefn {CalinskiHarabaszEvaluation} {} plot (@var{obj}) ## @deftypefnx {CalinskiHarabaszEvaluation} {@var{h} =} plot (@var{obj}) ## ## Plot the evaluation results. ## ## Plot the CriterionValues against InspectedK from the ## CalinskiHarabaszEvaluation, @var{obj}, to the current plot. It can also ## return a handle to the current plot. ## ## @end deftypefn function h = plot (this) yLabel = sprintf ("%s value", this.CriterionName); h = gca (); hold on; plot (this.InspectedK, this.CriterionValues, "bo-"); plot (this.OptimalK, this.CriterionValues(this.OptimalIndex), "b*"); xlabel ("number of clusters"); ylabel (yLabel); hold off; endfunction ## -*- texinfo -*- ## @deftypefn {CalinskiHarabaszEvaluation} {@var{obj} =} compact (@var{obj}) ## ## Return a compact CalinskiHarabaszEvaluation object (not implemented yet). ## ## @end deftypefn function this = compact (this) warning (["CalinskiHarabaszEvaluation.compact: this"... " method is not yet implemented."]); endfunction endmethods methods (Access = protected) ## evaluate ## do the evaluation function this = evaluate (this, K) ## use complete observations only UsableX = this.X(find (this.Missing == false), :); if (! isempty (this.ClusteringFunction)) ## build the clusters for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) if (isa (this.ClusteringFunction, "function_handle")) ## custom function ClusteringSolution = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); if (ismatrix (ClusteringSolution) && ... rows (ClusteringSolution) == this.NumObservations && ... columns (ClusteringSolution) == this.P) ## the custom function returned a matrix: ## we take the index of the maximum value for every row [~, this.ClusteringSolutions(:, iter)] = ... max (ClusteringSolution, [], 2); elseif (iscolumn (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution; elseif (isrow (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution'; else error (["CalinskiHarabaszEvaluation: invalid return value "... "from custom clustering function"]); endif this.ClusteringSolutions(:, iter) = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); else switch (this.ClusteringFunction) case "kmeans" [this.ClusteringSolutions(:, iter), this.Centroids{iter}] =... kmeans (UsableX, this.InspectedK(iter), ... "Distance", "sqeuclidean", "EmptyAction", "singleton", ... "Replicates", 5); case "linkage" ## use clusterdata this.ClusteringSolutions(:, iter) = clusterdata (UsableX, ... "MaxClust", this.InspectedK(iter), ... "Distance", "euclidean", "Linkage", "ward"); this.Centroids{iter} = this.computeCentroids (UsableX, iter); case "gmdistribution" gmm = fitgmdist (UsableX, this.InspectedK(iter), ... "SharedCov", true, "Replicates", 5); this.ClusteringSolutions(:, iter) = cluster (gmm, UsableX); this.Centroids{iter} = gmm.mu; otherwise error (["CalinskiHarabaszEvaluation: unexpected error, " ... "report this bug"]); endswitch endif endif endfor endif ## get the criterion values for every clustering solution for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) ## not defined for one cluster if (this.InspectedK(iter) == 1) this.CriterionValues(iter) = NaN; continue; endif ## CaliÅ„ski-Harabasz index ## reference: calinhara function from the fpc package of R, ## by Christian Hennig ## https://CRAN.R-project.org/package=fpc W = zeros (columns (UsableX)); # between clusters covariance for i = 1 : this.InspectedK(iter) vIndicesI = find (this.ClusteringSolutions(:, iter) == i); ni = length (vIndicesI); # size of cluster i if (ni == 1) ## if the cluster has just one member the covariance is zero continue; endif ## weighted update of the covariance matrix W += cov (UsableX(vIndicesI, :)) * (ni - 1); endfor S = (this.NumObservations - 1) * cov (UsableX); # within clusters cov. B = S - W; # between clusters means ## tr(B) / tr(W) * (N-k) / (k-1) this.CriterionValues(iter) = (this.NumObservations - ... this.InspectedK(iter)) * trace (B) / ... ((this.InspectedK(iter) - 1) * trace (W)); endif endfor [~, this.OptimalIndex] = max (this.CriterionValues); this.OptimalK = this.InspectedK(this.OptimalIndex(1)); this.OptimalY = this.ClusteringSolutions(:, this.OptimalIndex(1)); endfunction endmethods methods (Access = private) ## computeCentroids ## compute the centroids if they are not available by other means function C = computeCentroids (this, X, index) C = zeros (this.InspectedK(index), columns (X)); for iter = 1 : this.InspectedK(index) vIndicesI = find (this.ClusteringSolutions(:, index) == iter); C(iter, :) = mean (X(vIndicesI, :)); endfor endfunction endmethods endclassdef %!test %! load fisheriris %! eva = evalclusters (meas, "kmeans", "calinskiharabasz", "KList", [1:6]); %! assert (class (eva), "CalinskiHarabaszEvaluation"); statistics-release-1.7.3/inst/Clustering/ClusterCriterion.m000066400000000000000000000211631475240274700241230ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ClusterCriterion < handle ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} ClusterCriterion (@var{x}, @var{clust}, @var{criterion}) ## ## A clustering evaluation object as created by @code{evalclusters}. ## ## @code{ClusterCriterion} is a superclass for clustering evaluation objects ## as created by @code{evalclusters}. ## ## List of public properties: ## @table @code ## @item @qcode{ClusteringFunction} ## a valid clustering funtion name or function handle. It can be empty if ## the clustering solutions are passed as an input matric. ## ## @item @qcode{CriterionName} ## a valid criterion name to evaluate the clustering solutions. ## ## @item @qcode{CriterionValues} ## a vector of values as generated by the evaluation criterion for each ## clustering solution. ## ## @item @qcode{InspectedK} ## the list of proposed cluster numbers. ## ## @item @qcode{Missing} ## a logical vector of missing observations. When there are @code{NaN} ## values in the data matrix, the corresponding observation is excluded. ## ## @item @qcode{NumObservations} ## the number of non-missing observations in the data matrix. ## ## @item @qcode{OptimalK} ## the optimal number of clusters. ## ## @item @qcode{OptimalY} ## the clustering solution corresponding to @code{OptimalK}. ## ## @item @qcode{X} ## the data matrix. ## ## @end table ## ## List of public methods: ## @table @code ## @item @qcode{addK} ## add a list of numbers of clusters to evaluate. ## ## @item @qcode{compact} ## return a compact clustering evaluation object. Not implemented ## ## @item @qcode{plot} ## plot the clustering evaluation values against the corresponding number of ## clusters. ## ## @end table ## ## @seealso{evalclusters, CalinskiHarabaszEvaluation, DaviesBouldinEvaluation, ## GapEvaluation, SilhouetteEvaluation} ## @end deftypefn properties (Access = public) ## public properties endproperties properties (GetAccess = public, SetAccess = protected) ClusteringFunction = ""; CriterionName = ""; CriterionValues = []; InspectedK = []; Missing = []; NumObservations = 0; OptimalK = 0; OptimalY = []; X = []; endproperties properties (Access = protected) N = 0; # number of observations P = 0; # number of variables ClusteringSolutions = []; # OptimalIndex = 0; # index of the optimal K endproperties methods (Access = public) ## constructor function this = ClusterCriterion (x, clust, KList) ## parsing input data if ((! ismatrix (x)) || (! isnumeric (x))) error ("ClusterCriterion: 'x' must be a numeric matrix"); endif this.X = x; this.N = rows (this.X); this.P = columns (this.X); ## look for missing values for iter = 1 : this.N if (any (find (x(iter, :) == NaN))) this.Missing(iter) = true; else this.Missing(iter) = false; endif endfor ## number of usable observations this.NumObservations = sum (this.Missing == false); ## parsing the clustering algorithm if (ischar (clust)) if (any (strcmpi (clust, {"kmeans", "linkage", "gmdistribution"}))) this.ClusteringFunction = lower (clust); else error ("ClusterCriterion: unknown clustering algorithm '%s'", clust); endif elseif (isa (clust, "function_handle")) this.ClusteringFunction = clust; elseif (ismatrix (clust)) if (isnumeric (clust) && (length (size (clust)) == 2) && ... (rows (clust) == this.N)) this.ClusteringFunction = ""; this.ClusteringSolutions = clust(find (this.Missing == false), :); else error ("ClusterCriterion: invalid matrix of clustering solutions"); endif else error ("ClusterCriterion: invalid argument"); endif ## parsing the list of cluster sizes to inspect this.InspectedK = parseKList (this, KList); endfunction ## -*- texinfo -*- ## @deftypefn {ClusterCriterion} {@var{obj} =} addK (@var{obj}, @var{K}) ## ## Add a new cluster array to inspect the ClusterCriterion object. ## ## @end deftypefn function this = addK (this, k) ## if there is not a clustering function, then we are using a predefined ## set of clustering solutions, hence we cannot redefine the number of ## solutions if (isempty (this.ClusteringFunction)) warning (["ClusterCriterion.addK: cannot redefine the list of cluster"... "numbers to evaluate when there is not a clustering function"]); return; endif ## otherwise go on newList = this.parseKList ([this.InspectedK k]); ## check if the list has changed if (length (newList) == length (this.InspectedK)) warning ("ClusterCriterion.addK: the list has not changed"); else ## update ClusteringSolutions and CriterionValues ClusteringSolutions_tmp = zeros (this.NumObservations, ... length (newList)); CriterionValues_tmp = zeros (length (newList), 1); for iter = 1 : length (this.InspectedK) idx = find (newList == this.InspectedK(iter)); if (! isempty (idx)) ClusteringSolutions_tmp(:, idx) = this.ClusteringSolutions(:, iter); CriterionValues_tmp(idx) = this.CriterionValues(iter); endif endfor this.ClusteringSolutions = ClusteringSolutions_tmp; this.CriterionValues = CriterionValues_tmp; ## reset the old results this.OptimalK = 0; this.OptimalY = []; this.OptimalIndex = 0; ## update the list of cluster numbers to evaluate this.InspectedK = newList; endif endfunction ## -*- texinfo -*- ## @deftypefn {ClusterCriterion} {} plot (@var{obj}) ## @deftypefnx {ClusterCriterion} {@var{h} =} plot (@var{obj}) ## ## Plot the evaluation results. ## ## Plot the CriterionValues against InspectedK from the ClusterCriterion, ## @var{obj}, to the current plot. It can also return a handle to the ## current plot. ## ## @end deftypefn function h = plot (this) yLabel = sprintf ("%s value", this.CriterionName); h = gca (); hold on; plot (this.InspectedK, this.CriterionValues, "bo-"); plot (this.OptimalK, this.CriterionValues(this.OptimalIndex), "b*"); xlabel ("number of clusters"); ylabel (yLabel); hold off; endfunction ## -*- texinfo -*- ## @deftypefn {ClusterCriterion} {@var{obj} =} compact (@var{obj}) ## ## Return a compact ClusterCriterion object (not implemented yet). ## ## @end deftypefn function this = compact (this) warning ("ClusterCriterion.compact: this method is not yet implemented."); endfunction endmethods methods (Access = private) ## check if a list of cluster sizes is correct function retList = parseKList (this, KList) if (isnumeric (KList) && isvector (KList) && all (find (KList > 0)) && ... all (floor (KList) == KList)) retList = unique (KList); else error (["ClusterCriterion: the list of cluster sizes must be an " ... "array of positive integer numbers"]); endif endfunction endmethods endclassdef ## Test input validation %!error ... %! ClusterCriterion ("1", "kmeans", [1:6]) %!error ... %! ClusterCriterion ([1, 2, 1, 3, 2, 4, 3], "k", [1:6]) %!error ... %! ClusterCriterion ([1, 2, 1; 3, 2, 4], 1, [1:6]) %!error ... %! ClusterCriterion ([1, 2, 1; 3, 2, 4], ones (2, 2, 2), [1:6]) statistics-release-1.7.3/inst/Clustering/DaviesBouldinEvaluation.m000066400000000000000000000233021475240274700254000ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef DaviesBouldinEvaluation < ClusterCriterion ## -*- texinfo -*- ## @deftypefn {Function File} {@var{obj} =} evalclusters (@var{x}, @var{clust}, @qcode{DaviesBouldin}) ## @deftypefnx {Function File} {@var{obj} =} evalclusters (@dots{}, @qcode{Name}, @qcode{Value}) ## ## A Davies-Bouldin object to evaluate clustering solutions. ## ## A @code{DaviesBouldinEvaluation} object is a @code{ClusterCriterion} ## object used to evaluate clustering solutions using the Davies-Bouldin ## criterion. ## ## The Davies-Bouldin criterion is based on the ratio between the distances ## between clusters and within clusters, that is the distances between the ## centroids and the distances between each datapoint and its centroid. ## ## The best solution according to the Davies-Bouldin criterion is the one ## that scores the lowest value. ## ## @seealso{evalclusters, ClusterCriterion, CalinskiHarabaszEvaluation, ## GapEvaluation, SilhouetteEvaluation} ## @end deftypefn properties (GetAccess = public, SetAccess = private) endproperties properties (Access = protected) Centroids = {}; # a list of the centroids for every solution endproperties methods (Access = public) ## constructor function this = DaviesBouldinEvaluation (x, clust, KList) this@ClusterCriterion(x, clust, KList); this.CriterionName = "DaviesBouldin"; this.evaluate(this.InspectedK); # evaluate the list of cluster numbers endfunction ## -*- texinfo -*- ## @deftypefn {DaviesBouldinEvaluation} {@var{obj} =} addK (@var{obj}, @var{K}) ## ## Add a new cluster array to inspect the DaviesBouldinEvaluation object. ## ## @end deftypefn function this = addK (this, K) addK@ClusterCriterion(this, K); ## if we have new data, we need a new evaluation if (this.OptimalK == 0) Centroids_tmp = {}; pS = 0; # position shift of the elements of Centroids for iter = 1 : length (this.InspectedK) ## reorganize Centroids according to the new list of cluster numbers if (any (this.InspectedK(iter) == K)) pS += 1; else Centroids_tmp{iter} = this.Centroids{iter - pS}; endif endfor this.Centroids = Centroids_tmp; this.evaluate(K); # evaluate just the new cluster numbers endif endfunction ## -*- texinfo -*- ## @deftypefn {DaviesBouldinEvaluation} {} plot (@var{obj}) ## @deftypefnx {DaviesBouldinEvaluation} {@var{h} =} plot (@var{obj}) ## ## Plot the evaluation results. ## ## Plot the CriterionValues against InspectedK from the ## DaviesBouldinEvaluation ClusterCriterion, @var{obj}, to the current plot. ## It can also return a handle to the current plot. ## ## @end deftypefn function h = plot (this) yLabel = sprintf ("%s value", this.CriterionName); h = gca (); hold on; plot (this.InspectedK, this.CriterionValues, "bo-"); plot (this.OptimalK, this.CriterionValues(this.OptimalIndex), "b*"); xlabel ("number of clusters"); ylabel (yLabel); hold off; endfunction ## -*- texinfo -*- ## @deftypefn {DaviesBouldinEvaluation} {@var{obj} =} compact (@var{obj}) ## ## Return a compact DaviesBouldinEvaluation object (not implemented yet). ## ## @end deftypefn function this = compact (this) warning (["DaviesBouldinEvaluation.compact: this"... " method is not yet implemented."]); endfunction endmethods methods (Access = protected) ## evaluate ## do the evaluation function this = evaluate (this, K) ## use complete observations only UsableX = this.X(find (this.Missing == false), :); if (! isempty (this.ClusteringFunction)) ## build the clusters for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) if (isa (this.ClusteringFunction, "function_handle")) ## custom function ClusteringSolution = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); if (ismatrix (ClusteringSolution) && ... rows (ClusteringSolution) == this.NumObservations && ... columns (ClusteringSolution) == this.P) ## the custom function returned a matrix: ## we take the index of the maximum value for every row [~, this.ClusteringSolutions(:, iter)] = ... max (ClusteringSolution, [], 2); elseif (iscolumn (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution; elseif (isrow (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution'; else error (["DaviesBouldinEvaluation: invalid return value "... "from custom clustering function"]); endif this.ClusteringSolutions(:, iter) = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); else switch (this.ClusteringFunction) case "kmeans" [this.ClusteringSolutions(:, iter), this.Centroids{iter}] =... kmeans (UsableX, this.InspectedK(iter), ... "Distance", "sqeuclidean", "EmptyAction", "singleton", ... "Replicates", 5); case "linkage" ## use clusterdata this.ClusteringSolutions(:, iter) = clusterdata (UsableX, ... "MaxClust", this.InspectedK(iter), ... "Distance", "euclidean", "Linkage", "ward"); this.Centroids{iter} = this.computeCentroids (UsableX, iter); case "gmdistribution" gmm = fitgmdist (UsableX, this.InspectedK(iter), ... "SharedCov", true, "Replicates", 5); this.ClusteringSolutions(:, iter) = cluster (gmm, UsableX); this.Centroids{iter} = gmm.mu; otherwise error (["DaviesBouldinEvaluation: unexpected error, " ... "report this bug"]); endswitch endif endif endfor endif ## get the criterion values for every clustering solution for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) ## not defined for one cluster if (this.InspectedK(iter) == 1) this.CriterionValues(iter) = NaN; continue; endif ## Davies-Bouldin value ## an evaluation of the ratio between within-cluster and ## between-cluster distances ## mean distances between cluster members and their centroid vD = zeros (this.InspectedK(iter), 1); for i = 1 : this.InspectedK(iter) vIndicesI = find (this.ClusteringSolutions(:, iter) == i); vD(i) = mean (vecnorm (UsableX(vIndicesI, :) - ... this.Centroids{iter}(i, :), 2, 2)); endfor ## within-to-between cluster distance ratio Dij = zeros (this.InspectedK(iter)); for i = 1 : (this.InspectedK(iter) - 1) for j = (i + 1) : this.InspectedK(iter) ## centroid to centroid distance dij = vecnorm (this.Centroids{iter}(i, :) - ... this.Centroids{iter}(j, :)); ## within-to-between cluster distance ratio for clusters i and j Dij(i, j) = (vD(i) + vD(j)) / dij; endfor endfor ## ( max_j D1j + max_j D2j + ... + max_j Dkj) / k this.CriterionValues(iter) = sum (max (Dij(i, :), [], 2)) / ... this.InspectedK(iter); endif endfor [~, this.OptimalIndex] = min (this.CriterionValues); this.OptimalK = this.InspectedK(this.OptimalIndex(1)); this.OptimalY = this.ClusteringSolutions(:, this.OptimalIndex(1)); endfunction endmethods methods (Access = private) ## computeCentroids ## compute the centroids if they are not available by other means function C = computeCentroids (this, X, index) C = zeros (this.InspectedK(index), columns (X)); for iter = 1 : this.InspectedK(index) vIndicesI = find (this.ClusteringSolutions(:, index) == iter); C(iter, :) = mean (X(vIndicesI, :)); endfor endfunction endmethods endclassdef %!test %! load fisheriris %! eva = evalclusters (meas, "kmeans", "DaviesBouldin", "KList", [1:6]); %! assert (class (eva), "DaviesBouldinEvaluation"); statistics-release-1.7.3/inst/Clustering/GapEvaluation.m000066400000000000000000000370311475240274700233630ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef GapEvaluation < ClusterCriterion ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} evalclusters (@var{x}, @var{clust}, @qcode{gap}) ## @deftypefnx {statistics} {@var{obj} =} evalclusters (@dots{}, @qcode{Name}, @qcode{Value}) ## ## A gap object to evaluate clustering solutions. ## ## A @code{GapEvaluation} object is a @code{ClusterCriterion} ## object used to evaluate clustering solutions using the gap criterion, ## which is a mathematical formalization of the elbow method. ## ## List of public properties specific to @code{SilhouetteEvaluation}: ## @table @code ## @item @qcode{B} ## the number of reference datasets to generate. ## ## @item @qcode{Distance} ## a valid distance metric name, or a function handle as accepted by the ## @code{pdist} function. ## ## @item @qcode{ExpectedLogW} ## a vector of the expected values for the logarithm of the within clusters ## dispersion. ## ## @item @qcode{LogW} ## a vector of the values of the logarithm of the within clusters dispersion. ## ## @item @qcode{ReferenceDistribution} ## a valid name for the reference distribution, namely: @code{PCA} (default) ## or @code{uniform}. ## ## @item @qcode{SE} ## a vector of the standard error of the expected values for the logarithm ## of the within clusters dispersion. ## ## @item @qcode{SearchMethod} ## a valid name for the search method to use: @code{globalMaxSE} (default) or ## @code{firstMaxSE}. ## ## @item @qcode{StdLogW} ## a vector of the standard deviation of the expected values for the logarithm ## of the within clusters dispersion. ## @end table ## ## The best solution according to the gap criterion depends on the chosen ## search method. When the search method is @code{globalMaxSE}, the chosen ## gap value is the smaller one which is inside a standard error from the ## max gap value; when the search method is @code{firstMaxSE}, the chosen ## gap value is the first one which is inside a standard error from the next ## gap value. ## ## @seealso{evalclusters, ClusterCriterion, CalinskiHarabaszEvaluation, ## DaviesBouldinEvaluation, SilhouetteEvaluation} ## @end deftypefn properties (GetAccess = public, SetAccess = private) B = 0; # number of reference datasets Distance = ""; # pdist parameter ReferenceDistribution = ""; # distribution to use as reference SearchMethod = ""; # the method do identify the optimal number of clusters ExpectedLogW = []; # expected value for the natural logarithm of W LogW = []; # natural logarithm of W SE = []; # standard error for the natural logarithm of W StdLogW = []; # standard deviation of the natural logarithm of W endproperties properties (Access = protected) DistanceVector = []; # vector of pdist distances mExpectedLogW = []; # the result of the Monte-Carlo simulations endproperties methods (Access = public) ## constructor function this = GapEvaluation (x, clust, KList, b = 100, ... distanceMetric = "sqeuclidean", ... referenceDistribution = "pca", searchMethod = "globalmaxse") this@ClusterCriterion(x, clust, KList); ## parsing the distance criterion if (ischar (distanceMetric)) if (any (strcmpi (distanceMetric, {"sqeuclidean", "euclidean", ... "cityblock", "cosine", "correlation", "hamming", "jaccard"}))) this.Distance = lower (distanceMetric); ## kmeans can use only a subset if (strcmpi (clust, "kmeans") && any (strcmpi (this.Distance, ... {"euclidean", "jaccard"}))) error (["GapEvaluation: invalid distance criterion '%s' "... "for 'kmeans'"], distanceMetric); endif else error ("GapEvaluation: unknown distance criterion '%s'", ... distanceMetric); endif elseif (isa (distanceMetric, "function_handle")) this.Distance = distanceMetric; ## kmeans cannot use a function handle if (strcmpi (clust, "kmeans")) error ("GapEvaluation: invalid distance criterion for 'kmeans'"); endif elseif (isvector (distanceMetric) && isnumeric (distanceMetric)) this.Distance = ""; this.DistanceVector = distanceMetric; # the validity check is delegated ## kmeans cannot use a distance vector if (strcmpi (clust, "kmeans")) error (["GapEvaluation: invalid distance criterion for "... "'kmeans'"]); endif else error ("GapEvaluation: invalid distance metric"); endif ## B: number of Monte-Carlo iterations if (! isnumeric (b) || ! isscalar (b) || b != floor (b) || b < 1) error ("GapEvaluation: b must a be positive integer number"); endif this.B = b; ## reference distribution if (! ischar (referenceDistribution) || ! any (strcmpi ... (referenceDistribution, {"pca", "uniform"}))) error (["GapEvaluation: the reference distribution must be either" ... "'PCA' or 'uniform'"]); elseif (strcmpi (referenceDistribution, "pca")) warning (["GapEvaluation: 'PCA' distribution not implemented, " ... "using 'uniform'"]); endif this.ReferenceDistribution = lower (referenceDistribution); if (! ischar (searchMethod) || ! any (strcmpi (searchMethod, ... {"globalmaxse", "firstmaxse"}))) error (["evalclusters: the search method must be either" ... "'globalMaxSE' or 'firstMaxSE'"]); endif this.SearchMethod = lower (searchMethod); ## a matrix to store the results from the Monte-Carlo runs this.mExpectedLogW = zeros (this.B, length (this.InspectedK)); this.CriterionName = "gap"; this.evaluate(this.InspectedK); # evaluate the list of cluster numbers endfunction ## -*- texinfo -*- ## @deftypefn {GapEvaluation} {@var{obj} =} addK (@var{obj}, @var{K}) ## ## Add a new cluster array to inspect the GapEvaluation object. ## ## @end deftypefn function this = addK (this, K) addK@ClusterCriterion(this, K); ## if we have new data, we need a new evaluation if (this.OptimalK == 0) mExpectedLogW_tmp = zeros (this.B, length (this.InspectedK)); pS = 0; # position shift for iter = 1 : length (this.InspectedK) ## reorganize all the arrays according to the new list ## of cluster numbers if (any (this.InspectedK(iter) == K)) pS += 1; else mExpectedLogW_tmp(:, iter) = this.mExpectedLogW(:, iter - pS); endif endfor this.mExpectedLogW = mExpectedLogW_tmp; this.evaluate(K); # evaluate just the new cluster numbers endif endfunction ## -*- texinfo -*- ## @deftypefn {ClusterCriterion} {} plot (@var{obj}) ## @deftypefnx {ClusterCriterion} {@var{h} =} plot (@var{obj}) ## ## Plot the evaluation results. ## ## Plot the CriterionValues against InspectedK from the GapEvaluation ## ClusterCriterion, @var{obj}, and show the standard deviation to the ## current plot. It can also return a handle to the current plot. ## ## @end deftypefn function h = plot (this) yLabel = sprintf ("%s value", this.CriterionName); h = gca (); hold on; errorbar (this.InspectedK, this.CriterionValues, this.StdLogW); plot (this.InspectedK, this.CriterionValues, "bo"); plot (this.OptimalK, this.CriterionValues(this.OptimalIndex), "b*"); xlabel ("number of clusters"); ylabel (yLabel); hold off; endfunction ## -*- texinfo -*- ## @deftypefn {GapEvaluation} {@var{obj} =} compact (@var{obj}) ## ## Return a compact GapEvaluation object (not implemented yet). ## ## @end deftypefn function this = compact (this) warning ("GapEvaluation.compact: this method is not yet implemented."); endfunction endmethods methods (Access = protected) ## evaluate ## do the evaluation function this = evaluate (this, K) ## Monte-Carlo runs for mcrun = 1 : (this.B + 1) ## use complete observations only UsableX = this.X(find (this.Missing == false), :); ## the last run use tha actual data, ## the others are Monte-Carlo runs with reconstructed data if (mcrun <= this.B) ## uniform distribution colMins = min (UsableX); colMaxs = max (UsableX); for col = 1 : columns (UsableX) UsableX(:, col) = colMins(col) + rand (this.NumObservations, 1) *... (colMaxs(col) - colMins(col)); endfor endif if (! isempty (this.ClusteringFunction)) ## build the clusters for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) if (isa (this.ClusteringFunction, "function_handle")) ## custom function ClusteringSolution = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); if (ismatrix (ClusteringSolution) && ... rows (ClusteringSolution) == this.NumObservations && ... columns (ClusteringSolution) == this.P) ## the custom function returned a matrix: ## we take the index of the maximum value for every row [~, this.ClusteringSolutions(:, iter)] = ... max (ClusteringSolution, [], 2); elseif (iscolumn (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution; elseif (isrow (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution'; else error (["GapEvaluation: invalid return value from " ... "custom clustering function"]); endif this.ClusteringSolutions(:, iter) = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); else switch (this.ClusteringFunction) case "kmeans" this.ClusteringSolutions(:, iter) = kmeans (UsableX, ... this.InspectedK(iter), "Distance", this.Distance, ... "EmptyAction", "singleton", "Replicates", 5); case "linkage" if (! isempty (this.Distance)) ## use clusterdata Distance_tmp = this.Distance; LinkageMethod = "average"; # for non euclidean methods if (strcmpi (this.Distance, "sqeuclidean")) ## pdist uses different names for its algorithms Distance_tmp = "squaredeuclidean"; LinkageMethod = "ward"; elseif (strcmpi (this.Distance, "euclidean")) LinkageMethod = "ward"; endif this.ClusteringSolutions(:, iter) = clusterdata ... (UsableX, "MaxClust", this.InspectedK(iter), ... "Distance", Distance_tmp, "Linkage", LinkageMethod); else ## use linkage Z = linkage (this.DistanceVector, "average"); this.ClusteringSolutions(:, iter) = ... cluster (Z, "MaxClust", this.InspectedK(iter)); endif case "gmdistribution" gmm = fitgmdist (UsableX, this.InspectedK(iter), ... "SharedCov", true, "Replicates", 5); this.ClusteringSolutions(:, iter) = cluster (gmm, UsableX); otherwise ## this should not happen error (["GapEvaluation: unexpected error, " ... "report this bug"]); endswitch endif endif endfor endif ## get the gap values for every clustering distance_pdist = this.Distance; if (strcmpi (distance_pdist, "sqeuclidean")) distance_pdist = "squaredeuclidean"; endif ## compute LogW for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) wk = 0; for r = 1 : this.InspectedK(iter) vIndicesR = find (this.ClusteringSolutions(:, iter) == r); nr = length (vIndicesR); Dr = pdist (UsableX(vIndicesR, :), distance_pdist); wk += sum (Dr) / (2 * nr); endfor if (mcrun <= this.B) this.mExpectedLogW(mcrun, iter) = log (wk); else this.LogW(iter) = log (wk); endif endif endfor endfor this.ExpectedLogW = mean (this.mExpectedLogW); this.SE = sqrt ((1 + 1 / this.B) * sumsq (this.mExpectedLogW - ... this.ExpectedLogW) / this.B); this.StdLogW = std (this.mExpectedLogW); this.CriterionValues = this.ExpectedLogW - this.LogW; this.OptimalIndex = this.gapSearch (); this.OptimalK = this.InspectedK(this.OptimalIndex(1)); this.OptimalY = this.ClusteringSolutions(:, this.OptimalIndex(1)); endfunction ## gapSearch ## find the best solution according to the gap method function ind = gapSearch (this) if (strcmpi (this.SearchMethod, "globalmaxse")) [gapmax, indgp] = max (this.CriterionValues); for iter = 1 : length (this.InspectedK) ind = iter; if (this.CriterionValues(iter) > (gapmax - this.SE(indgp))) return endif endfor elseif (strcmpi (this.SearchMethod, "firstmaxse")) for iter = 1 : (length (this.InspectedK) - 1) ind = iter; if (this.CriterionValues(iter) > (this.CriterionValues(iter + 1) - ... this.SE(iter + 1))) return endif endfor else ## this should not happen error (["GapEvaluation: unexpected error, please report this bug"]); endif endfunction endmethods endclassdef %!test %! load fisheriris %! eva = evalclusters (meas([1:50],:), "kmeans", "gap", "KList", [1:3], ... %! "referencedistribution", "uniform"); %! assert (class (eva), "GapEvaluation"); statistics-release-1.7.3/inst/Clustering/SilhouetteEvaluation.m000066400000000000000000000276321475240274700250070ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef SilhouetteEvaluation < ClusterCriterion ## -*- texinfo -*- ## @deftypefn {Function File} {@var{obj} =} evalclusters (@var{x}, @var{clust}, @qcode{silhouette}) ## @deftypefnx {Function File} {@var{obj} =} evalclusters (@dots{}, @qcode{Name}, @qcode{Value}) ## ## A silhouette object to evaluate clustering solutions. ## ## A @code{SilhouetteEvaluation} object is a @code{ClusterCriterion} ## object used to evaluate clustering solutions using the silhouette ## criterion. ## ## List of public properties specific to @code{SilhouetteEvaluation}: ## @table @code ## @item @qcode{Distance} ## a valid distance metric name, or a function handle or a numeric array as ## generated by the @code{pdist} function. ## ## @item @qcode{ClusterPriors} ## a valid name for the evaluation of silhouette values: @code{empirical} ## (default) or @code{equal}. ## ## @item @qcode{ClusterSilhouettes} ## a cell array with the silhouette values of each data point for each cluster ## number. ## ## @end table ## ## The best solution according to the silhouette criterion is the one ## that scores the highest average silhouette value. ## ## @seealso{evalclusters, ClusterCriterion, CalinskiHarabaszEvaluation, ## DaviesBouldinEvaluation, GapEvaluation} ## @end deftypefn properties (GetAccess = public, SetAccess = private) Distance = ""; # pdist parameter ClusterPriors = ""; # evaluation of silhouette values: equal or empirical ClusterSilhouettes = {}; # results of the silhoutte function for each K endproperties properties (Access = protected) DistanceVector = []; # vector of pdist distances endproperties methods (Access = public) ## constructor function this = SilhouetteEvaluation (x, clust, KList, ... distanceMetric = "sqeuclidean", clusterPriors = "empirical") this@ClusterCriterion(x, clust, KList); ## parsing the distance criterion if (ischar (distanceMetric)) if (any (strcmpi (distanceMetric, {"sqeuclidean", ... "euclidean", "cityblock", "cosine", "correlation", ... "hamming", "jaccard"}))) this.Distance = lower (distanceMetric); ## kmeans can use only a subset if (strcmpi (clust, "kmeans") && any (strcmpi (this.Distance, ... {"euclidean", "jaccard"}))) error (["SilhouetteEvaluation: invalid distance criterion '%s' "... "for 'kmeans'"], distanceMetric); endif else error ("SilhouetteEvaluation: unknown distance criterion '%s'", ... distanceMetric); endif elseif (isa (distanceMetric, "function_handle")) this.Distance = distanceMetric; ## kmeans cannot use a function handle if (strcmpi (clust, "kmeans")) error (["SilhouetteEvaluation: invalid distance criterion for "... "'kmeans'"]); endif elseif (isvector (distanceMetric) && isnumeric (distanceMetric)) this.Distance = ""; this.DistanceVector = distanceMetric; # the validity check is delegated ## kmeans cannot use a distance vector if (strcmpi (clust, "kmeans")) error (["SilhouetteEvaluation: invalid distance criterion for "... "'kmeans'"]); endif else error ("SilhouetteEvaluation: invalid distance metric"); endif ## parsing the prior probabilities of each cluster if (ischar (distanceMetric)) if (any (strcmpi (clusterPriors, {"empirical", "equal"}))) this.ClusterPriors = lower (clusterPriors); else error (["SilhouetteEvaluation: unknown prior probability criterion"... " '%s'"], clusterPriors); endif else error ("SilhouetteEvaluation: invalid prior probabilities"); endif this.CriterionName = "silhouette"; this.evaluate(this.InspectedK); # evaluate the list of cluster numbers endfunction ## -*- texinfo -*- ## @deftypefn {SilhouetteEvaluation} {@var{obj} =} addK (@var{obj}, @var{K}) ## ## Add a new cluster array to inspect the SilhouetteEvaluation object. ## ## @end deftypefn function this = addK (this, K) addK@ClusterCriterion(this, K); ## if we have new data, we need a new evaluation if (this.OptimalK == 0) ClusterSilhouettes_tmp = {}; pS = 0; # position shift of the elements of ClusterSilhouettes for iter = 1 : length (this.InspectedK) ## reorganize ClusterSilhouettes according to the new list ## of cluster numbers if (any (this.InspectedK(iter) == K)) pS += 1; else ClusterSilhouettes_tmp{iter} = this.ClusterSilhouettes{iter - pS}; endif endfor this.ClusterSilhouettes = ClusterSilhouettes_tmp; this.evaluate(K); # evaluate just the new cluster numbers endif endfunction ## -*- texinfo -*- ## @deftypefn {SilhouetteEvaluation} {} plot (@var{obj}) ## @deftypefnx {SilhouetteEvaluation} {@var{h} =} plot (@var{obj}) ## ## Plot the evaluation results. ## ## Plot the CriterionValues against InspectedK from the ## SilhouetteEvaluation ClusterCriterion, @var{obj}, to the current plot. ## It can also return a handle to the current plot. ## ## @end deftypefn function h = plot (this) yLabel = sprintf ("%s value", this.CriterionName); h = gca (); hold on; plot (this.InspectedK, this.CriterionValues, "bo-"); plot (this.OptimalK, this.CriterionValues(this.OptimalIndex), "b*"); xlabel ("number of clusters"); ylabel (yLabel); hold off; endfunction ## -*- texinfo -*- ## @deftypefn {SilhouetteEvaluation} {@var{obj} =} compact (@var{obj}) ## ## Return a compact SilhouetteEvaluation object (not implemented yet). ## ## @end deftypefn function this = compact (this) warning (["SilhouetteEvaluation.compact: this"... " method is not yet implemented."]); endfunction endmethods methods (Access = protected) ## evaluate ## do the evaluation function this = evaluate (this, K) ## use complete observations only UsableX = this.X(find (this.Missing == false), :); if (! isempty (this.ClusteringFunction)) ## build the clusters for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) if (isa (this.ClusteringFunction, "function_handle")) ## custom function ClusteringSolution = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); if (ismatrix (ClusteringSolution) && ... rows (ClusteringSolution) == this.NumObservations && ... columns (ClusteringSolution) == this.P) ## the custom function returned a matrix: ## we take the index of the maximum value for every row [~, this.ClusteringSolutions(:, iter)] = ... max (ClusteringSolution, [], 2); elseif (iscolumn (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution; elseif (isrow (ClusteringSolution) && length (ClusteringSolution) == this.NumObservations) this.ClusteringSolutions(:, iter) = ClusteringSolution'; else error (["SilhouetteEvaluation: invalid return value from " ... "custom clustering function"]); endif this.ClusteringSolutions(:, iter) = ... this.ClusteringFunction(UsableX, this.InspectedK(iter)); else switch (this.ClusteringFunction) case "kmeans" this.ClusteringSolutions(:, iter) = kmeans (UsableX, ... this.InspectedK(iter), "Distance", this.Distance, ... "EmptyAction", "singleton", "Replicates", 5); case "linkage" if (! isempty (this.Distance)) ## use clusterdata Distance_tmp = this.Distance; LinkageMethod = "average"; # for non euclidean methods if (strcmpi (this.Distance, "sqeuclidean")) ## pdist uses different names for its algorithms Distance_tmp = "squaredeuclidean"; LinkageMethod = "ward"; elseif (strcmpi (this.Distance, "euclidean")) LinkageMethod = "ward"; endif this.ClusteringSolutions(:, iter) = clusterdata (UsableX,... "MaxClust", this.InspectedK(iter), ... "Distance", Distance_tmp, "Linkage", LinkageMethod); else ## use linkage Z = linkage (this.DistanceVector, "average"); this.ClusteringSolutions(:, iter) = ... cluster (Z, "MaxClust", this.InspectedK(iter)); endif case "gmdistribution" gmm = fitgmdist (UsableX, this.InspectedK(iter), ... "SharedCov", true, "Replicates", 5); this.ClusteringSolutions(:, iter) = cluster (gmm, UsableX); otherwise error (["SilhouetteEvaluation: unexpected error, " ... "report this bug"]); endswitch endif endif endfor endif ## get the silhouette values for every clustering for iter = 1 : length (this.InspectedK) ## do it only for the specified K values if (any (this.InspectedK(iter) == K)) ## Custom call to silhouette to avoid plotting any figures this.ClusterSilhouettes{iter} = silhouette (UsableX, ... this.ClusteringSolutions(:, iter), ... "sqeuclidean", "DoNotPlot"); if (strcmpi (this.ClusterPriors, "empirical")) this.CriterionValues(iter) = mean (this.ClusterSilhouettes{iter}); else ## equal this.CriterionValues(iter) = 0; si = this.ClusterSilhouettes{iter}; for k = 1 : this.InspectedK(iter) this.CriterionValues(iter) += mean (si(find ... (this.ClusteringSolutions(:, iter) == k))); endfor this.CriterionValues(iter) /= this.InspectedK(iter); endif endif endfor [~, this.OptimalIndex] = max (this.CriterionValues); this.OptimalK = this.InspectedK(this.OptimalIndex(1)); this.OptimalY = this.ClusteringSolutions(:, this.OptimalIndex(1)); endfunction endmethods endclassdef %!test %! load fisheriris %! eva = evalclusters (meas, "kmeans", "silhouette", "KList", [1:6]); %! assert (class (eva), "SilhouetteEvaluation"); statistics-release-1.7.3/inst/PKG_ADD000066400000000000000000000015771475240274700173510ustar00rootroot00000000000000if (compare_versions (version (), "9", "<")) a1_e324kporit985_itogj3_dirlist = ... {"Classification", "Clustering", "datasets", "dist_fit", "dist_fun", ... "dist_obj", "dist_stat", "dist_wrap", "Regression", "shadow9"}; else a1_e324kporit985_itogj3_dirlist = ... {"Classification", "Clustering", "datasets", "dist_fit", "dist_fun", ... "dist_obj", "dist_stat", "dist_wrap", "Regression"}; endif d_2seRTE546_oyi_795jg09_dirname = fileparts (canonicalize_file_name ... (mfilename ("fullpath"))); for iiII123DRT_idx = 1:length (a1_e324kporit985_itogj3_dirlist) addpath (fullfile (d_2seRTE546_oyi_795jg09_dirname, ... a1_e324kporit985_itogj3_dirlist{iiII123DRT_idx})); endfor clear a1_e324kporit985_itogj3_dirlist clear d_2seRTE546_oyi_795jg09_dirname iiII123DRT_idx warning ("off", "Octave:data-file-in-path") statistics-release-1.7.3/inst/PKG_DEL000066400000000000000000000016221475240274700173540ustar00rootroot00000000000000clear -f editDistance libsvmread libsvmwrite svmpredict svmtrain if (compare_versions (version (), "9", "<")) a1_e324kporit985_itogj3_dirlist = ... {"Classification", "Clustering", "datasets", "dist_fit", "dist_fun", ... "dist_obj", "dist_stat", "dist_wrap", "Regression", "shadow9"}; else a1_e324kporit985_itogj3_dirlist = ... {"Classification", "Clustering", "datasets", "dist_fit", "dist_fun", ... "dist_obj", "dist_stat", "dist_wrap", "Regression"}; endif d_2seRTE546_oyi_795jg09_dirname = fileparts (canonicalize_file_name ... (mfilename ("fullpath"))); for iiII123DRT_idx = 1:length (a1_e324kporit985_itogj3_dirlist) rmpath (fullfile (d_2seRTE546_oyi_795jg09_dirname, ... a1_e324kporit985_itogj3_dirlist{iiII123DRT_idx})); endfor clear a1_e324kporit985_itogj3_dirlist clear d_2seRTE546_oyi_795jg09_dirname iiII123DRT_idx statistics-release-1.7.3/inst/Regression/000077500000000000000000000000001475240274700204435ustar00rootroot00000000000000statistics-release-1.7.3/inst/Regression/RegressionGAM.m000066400000000000000000001136751475240274700233030ustar00rootroot00000000000000## Copyright (C) 2023 Mohammed Azmat Khan ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef RegressionGAM ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} RegressionGAM (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} RegressionGAM (@dots{}, @var{name}, @var{value}) ## ## Create a @qcode{RegressionGAM} class object containing a Generalised Additive ## Model (GAM) for regression. ## ## A @qcode{RegressionGAM} class object can store the predictors and response ## data along with various parameters for the GAM model. It is recommended to ## use the @code{fitrgam} function to create a @qcode{RegressionGAM} object. ## ## @code{@var{obj} = RegressionGAM (@var{X}, @var{Y})} returns an object of ## class RegressionGAM, with matrix @var{X} containing the predictor data and ## vector @var{Y} containing the continuous response data. ## ## @itemize ## @item ## @var{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or variables. ## @var{X} will be used to train the GAM model. ## @item ## @var{Y} must be @math{Nx1} numeric vector containing the response data ## corresponding to the predictor data in @var{X}. @var{Y} must have same ## number of rows as @var{X}. ## @end itemize ## ## @code{@var{obj} = RegressionGAM (@dots{}, @var{name}, @var{value})} returns ## an object of class RegressionGAM with additional properties specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @tab @qcode{"predictors"} @tab Predictor Variable names, specified as ## a row vector cell of strings with the same length as the columns in @var{X}. ## If omitted, the program will generate default variable names ## @qcode{(x1, x2, ..., xn)} for each column in @var{X}. ## ## @item @tab @qcode{"responsename"} @tab Response Variable Name, specified as ## a string. If omitted, the default value is @qcode{"Y"}. ## ## @item @tab @qcode{"formula"} @tab a model specification given as a string in ## the form @qcode{"Y ~ terms"} where @qcode{Y} represents the reponse variable ## and @qcode{terms} the predictor variables. The formula can be used to ## specify a subset of variables for training model. For example: ## @qcode{"Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3"} specifies four linear terms ## for the first four columns of for predictor data, and @qcode{x1:x2} and ## @qcode{x2:x3} specify the two interaction terms for 1st-2nd and 3rd-4th ## columns respectively. Only these terms will be used for training the model, ## but @var{X} must have at least as many columns as referenced in the formula. ## If Predictor Variable names have been defined, then the terms in the formula ## must reference to those. When @qcode{"formula"} is specified, all terms used ## for training the model are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object as a matrix containing the column indexes for each ## term including both the predictors and the interactions used. ## ## @item @tab @qcode{"interactions"} @tab a logical matrix, a positive integer ## scalar, or the string @qcode{"all"} for defining the interactions between ## predictor variables. When given a logical matrix, it must have the same ## number of columns as @var{X} and each row corresponds to a different ## interaction term combining the predictors indexed as @qcode{true}. Each ## interaction term is appended as a column vector after the available predictor ## column in @var{X}. When @qcode{"all"} is defined, then all possible ## combinations of interactions are appended in @var{X} before training. At the ## moment, parsing a positive integer has the same effect as the @qcode{"all"} ## option. When @qcode{"interactions"} is specified, only the interaction terms ## appended to @var{X} are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object. ## ## @item @tab @qcode{"knots"} @tab a scalar or a row vector with the same ## columns as @var{X}. It defines the knots for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 5, hence expanded to @qcode{ones (1, columns (X)) * 5}. You can ## parse a row vector with different number of knots for each predictor ## variable to be fitted with, although not recommended. ## ## @item @tab @qcode{"order"} @tab a scalar or a row vector with the same ## columns as @var{X}. It defines the order of the polynomial when training the ## GAM. As a scalar, it is expanded to a row vector. The default values is 3, ## hence expanded to @qcode{ones (1, columns (X)) * 3}. You can parse a row ## vector with different number of polynomial order for each predictor variable ## to be fitted with, although not recommended. ## ## @item @tab @qcode{"dof"} @tab a scalar or a row vector with the same columns ## as @var{X}. It defines the degrees of freedom for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 8, hence expanded to @qcode{ones (1, columns (X)) * 8}. You can ## parse a row vector with different degrees of freedom for each predictor ## variable to be fitted with, although not recommended. ## ## @item @tab @qcode{"tol"} @tab a positive scalar to set the tolerance for ## covergence during training. By defaul, it is set to @qcode{1e-3}. ## @end multitable ## ## You can parse either a @qcode{"formula"} or an @qcode{"interactions"} ## optional parameter. Parsing both parameters will result an error. ## Accordingly, you can only pass up to two parameters among @qcode{"knots"}, ## @qcode{"order"}, and @qcode{"dof"} to define the required polynomial for ## training the GAM model. ## ## @seealso{fitrgam, regress, regress_gp} ## @end deftypefn properties (Access = public) X = []; # Predictor data Y = []; # Response data BaseModel = []; # Base model parameters (no interactions) ModelwInt = []; # Model parameters with interactions IntMatrix = []; # Interactions matrix applied to predictor data NumObservations = []; # Number of observations in training dataset RowsUsed = []; # Rows used in fitting NumPredictors = []; # Number of predictors PredictorNames = []; # Predictor variable names ResponseName = []; # Response variable name Formula = []; # Formula for GAM model Interactions = []; # Number or matrix of interaction terms Knots = []; # Knots of spline fitting Order = []; # Order of spline fitting DoF = []; # Degrees of freedom for fitting spline Tol = []; # Tolerence for convergence endproperties methods (Access = public) ## Class object constructor function this = RegressionGAM (X, Y, varargin) ## Check for sufficient number of input arguments if (nargin < 2) error ("RegressionGAM: too few input arguments."); endif ## Get training sample size and number of variables in training data nsample = rows (X); ndims_X = columns (X); ## Check correspodence between predictors and response if (nsample != rows (Y)) error ("RegressionGAM: number of rows in X and Y must be equal."); endif ## Set default values before parsing optional parameters PredictorNames = {}; # Predictor variable names ResponseName = []; # Response variable name Formula = []; # Formula for GAM model Interactions = []; # Interaction terms DoF = ones (1, ndims_X) * 8; # Degrees of freedom Order = ones (1, ndims_X) * 3; # Order of spline Knots = ones (1, ndims_X) * 5; # Knots Tol = 1e-3; # Tolerence for convergence ## Number of parameters for Knots, DoF, Order (maximum 2 allowed) KOD = 0; ## Number of parameters for Formula, Ineractions (maximum 1 allowed) F_I = 0; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "predictors" PredictorNames = varargin{2}; if (! isempty (PredictorNames)) if (! iscellstr (PredictorNames)) error (strcat (["RegressionGAM: PredictorNames must"], ... [" be a cellstring array."])); elseif (columns (PredictorNames) != columns (X)) error (strcat (["RegressionGAM: PredictorNames must"], ... [" have same number of columns as X."])); endif endif case "responsename" ResponseName = varargin{2}; if (! ischar (ResponseName)) error ("RegressionGAM: ResponseName must be a char string."); endif case "formula" if (F_I < 1) Formula = varargin{2}; if (! ischar (Formula) && ! islogical (Formula)) error ("RegressionGAM: Formula must be a string."); endif F_I += 1; else error ("RegressionGAM: Interactions have been already defined."); endif case "interactions" if (F_I < 1) tmp = varargin{2}; if (isnumeric (tmp) && isscalar (tmp) && tmp == fix (tmp) && tmp >= 0) Interactions = tmp; elseif (islogical (tmp)) Interactions = tmp; elseif (ischar (tmp) && strcmpi (tmp, "all")) Interactions = tmp; else error ("RegressionGAM: invalid Interactions parameter."); endif F_I += 1; else error ("RegressionGAM: Formula has been already defined."); endif case "knots" if (KOD < 2) Knots = varargin{2}; if (! isnumeric (Knots) || ! (isscalar (Knots) || isequal (size (Knots), [1, ndims_X]))) error ("RegressionGAM: invalid value for Knots."); endif DoF = Knots + Order; Order = DoF - Knots; KOD += 1; else error ("RegressionGAM: DoF and Order have been set already."); endif case "order" if (KOD < 2) Order = varargin{2}; if (! isnumeric (Order) || ! (isscalar (Order) || isequal (size (Order), [1, ndims_X]))) error ("RegressionGAM: invalid value for Order."); endif DoF = Knots + Order; Knots = DoF - Order; KOD += 1; else error ("RegressionGAM: DoF and Knots have been set already."); endif case "dof" if (KOD < 2) DoF = varargin{2}; if (! isnumeric (DoF) || ! (isscalar (DoF) || isequal (size (DoF), [1, ndims_X]))) error ("RegressionGAM: invalid value for DoF."); endif Knots = DoF - Order; Order = DoF - Knots; KOD += 1; else error ("RegressionGAM: Knots and Order have been set already."); endif case "tol" Tol = varargin{2}; if (! (isnumeric (Tol) && isscalar (Tol) && (Tol > 0))) error ("RegressionGAM: Tolerance must be a Positive scalar."); endif otherwise error (strcat (["RegressionGAM: invalid parameter name"],... [" in optional pair arguments."])); endswitch varargin (1:2) = []; endwhile ## Assign original X and Y data to the RegressionGAM object this.X = X; this.Y = Y; ## Remove nans from X and Y RowsUsed = ! logical (sum (isnan ([Y, X]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Check X and Y contain valid data if (! isnumeric (X) || ! isfinite (X)) error ("RegressionGAM: invalid values in X."); endif if (! isnumeric (Y) || ! isfinite (Y)) error ("RegressionGAM: invalid values in Y."); endif ## Assign the number of observations and their correspoding indices ## on the original data, which will be used for training the model, ## to the RegressionGAM object this.NumObservations = rows (X); this.RowsUsed = cast (RowsUsed, "double"); ## Assign the number of original predictors to the RegressionGAM object this.NumPredictors = ndims_X; ## Generate default predictors and response variabe names (if necessary) if (isempty (PredictorNames)) for i = 1:ndims_X PredictorNames {i} = strcat ("x", num2str (i)); endfor endif if (isempty (ResponseName)) ResponseName = "Y"; endif ## Assign predictors and response variable names this.PredictorNames = PredictorNames; this.ResponseName = ResponseName; ## Assign remaining optional parameters this.Formula = Formula; this.Interactions = Interactions; this.Knots = Knots; this.Order = Order; this.DoF = DoF; this.Tol = Tol; ## Fit the basic model Inter = mean (Y); [iter, param, res, RSS] = this.fitGAM (X, Y, Inter, Knots, Order); this.BaseModel.Intercept = Inter; this.BaseModel.Parameters = param; this.BaseModel.Iterations = iter; this.BaseModel.Residuals = res; this.BaseModel.RSS = RSS; ## Handle interaction terms (if given) if (F_I > 0) if (isempty (this.Formula)) ## Analyze Interactions optional parameter this.IntMatrix = this.parseInteractions (); ## Append interaction terms to the predictor matrix for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = X(:,tindex); Xinter = ones (this.NumObservations, 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append interaction terms X = [X, Xinter]; endfor else ## Analyze Formula optional parameter this.IntMatrix = this.parseFormula (); ## Add selected predictors and interaction terms XN = []; for i = 1:rows (this.IntMatrix) tindex = logical (this.IntMatrix(i,:)); Xterms = X(:,tindex); Xinter = ones (this.NumObservations, 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append selected predictors and interaction terms XN = [XN, Xinter]; endfor X = XN; endif ## Update length of Knots, Order, and DoF vectors to match ## the columns of X with the interaction terms Knots = ones (1, columns (X)) * Knots(1); # Knots Order = ones (1, columns (X)) * Order(1); # Order of spline DoF = ones (1, columns (X)) * DoF(1); # Degrees of freedom ## Fit the model with interactions [iter, param, res, RSS] = this.fitGAM (X, Y, Inter, Knots, Order); this.ModelwInt.Intercept = Inter; this.ModelwInt.Parameters = param; this.ModelwInt.Iterations = iter; this.ModelwInt.Residuals = res; this.ModelwInt.RSS = RSS; endif endfunction ## -*- texinfo -*- ## @deftypefn {RegressionGAM} {@var{yFit} =} predict (@var{obj}, @var{Xfit}) ## @deftypefnx {RegressionGAM} {@var{yFit} =} predict (@dots{}, @var{Name}, @var{Value}) ## @deftypefnx {RegressionGAM} {[@var{yFit}, @var{ySD}, @var{yInt}] =} predict (@dots{}) ## ## Predict new data points using generalized additive model regression object. ## ## @code{@var{yFit} = predict (@var{obj}, @var{Xfit}} returns a vector of ## predicted responses, @var{yFit}, for the predictor data in matrix @var{Xfit} ## based on the Generalized Additive Model in @var{obj}. @var{Xfit} must have ## the same number of features/variables as the training data in @var{obj}. ## ## @itemize ## @item ## @var{obj} must be a @qcode{RegressionGAM} class object. ## @end itemize ## ## @code{[@var{yFit}, @var{ySD}, @var{yInt}] = predict (@var{obj}, @var{Xfit}} ## also returns the standard deviations, @var{ySD}, and prediction intervals, ## @var{yInt}, of the response variable @var{yFit}, evaluated at each ## observation in the predictor data @var{Xfit}. ## ## @code{@var{yFit} = predict (@dots{}, @var{Name}, @var{Value})} returns the ## aforementioned results with additional properties specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.28 0.02 0.7 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"alpha"} @tab @tab significance level of the prediction ## intervals @var{yInt}, specified as scalar in range @qcode{[0,1]}. The default ## value is 0.05, which corresponds to 95% prediction intervals. ## ## @item @qcode{"includeinteractions"} @tab @tab a boolean flag to include ## interactions to predict new values based on @var{Xfit}. By default, ## @qcode{"includeinteractions"} is @qcode{true} when the GAM model in @var{obj} ## contains a @qcode{obj.Formula} or @qcode{obj.Interactions} fields. Otherwise, ## is set to @qcode{false}. If set to @qcode{true} when no interactions are ## present in the trained model, it will result to an error. If set to ## @qcode{false} when using a model that includes interactions, the predictions ## will be made on the basic model without any interaction terms. This way you ## can make predictions from the same GAM model without having to retrain it. ## @end multitable ## ## @seealso{fitrgam, RegressionGAM} ## @end deftypefn function [yFit, ySD, yInt] = predict (this, Xfit, varargin) ## Check for sufficient input arguments if (nargin < 2) error ("RegressionGAM.predict: too few arguments."); endif ## Check for valid XC if (isempty (Xfit)) error ("RegressionGAM.predict: Xfit is empty."); elseif (columns (this.X) != columns (Xfit)) error (strcat (["@RegressionGAM/predict: Xfit must have the same"], ... [" number of features (columns) as in the GAM model."])); endif ## Clean Xfit data notnansf = ! logical (sum (isnan (Xfit), 2)); Xfit = Xfit (notnansf, :); ## Default values for Name-Value Pairs alpha = 0.05; if (isempty (this.IntMatrix)) incInt = false; else incInt = true; endif ## Parse optional arguments while (numel (varargin) > 0) switch (tolower (varargin {1})) case "includeinteractions" tmpInt = varargin{2}; if (! islogical (tmpInt) || (tmpInt != 0 && tmpInt != 1)) error (strcat (["RegressionGAM.predict: includeinteractions"], ... [" must be a logical value."])); endif ## Check model for interactions if (tmpInt && isempty (this.IntMat)) error (strcat (["RegressionGAM.predict: trained model"], ... [" does not include any interactions."])); endif incInt = tmpInt; case "alpha" alpha = varargin{2}; if (! (isnumeric (alpha) && isscalar (alpha) && alpha > 0 && alpha < 1)) error (strcat (["RegressionGAM.predict: alpha must be a"], ... [" scalar value between 0 and 1."])); endif otherwise error (strcat(["RegressionGAM.predict: invalid NAME in"], ... [" optional pairs of arguments."])); endswitch varargin (1:2) = []; endwhile ## Choose whether interactions must be included if (incInt) if (! isempty (this.Interactions)) ## Append interaction terms to the predictor matrix for i = 1:rows (this.IntMat) tindex = logical (this.IntMat(i,:)); Xterms = Xfit(:,tindex); Xinter = ones (rows (Xfit), 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append interaction terms Xfit = [Xfit, Xinter]; endfor else ## Add selected predictors and interaction terms XN = []; for i = 1:rows (this.IntMat) tindex = logical (this.IntMat(i,:)); Xterms = Xfit(:,tindex); Xinter = ones (rows (Xfit), 1); for c = 1:sum (tindex) Xinter = Xinter .* Xterms(:,c); endfor ## Append selected predictors and interaction terms XN = [XN, Xinter]; endfor Xfit = XN; endif ## Get parameters and intercept vectors from model with interactions params = this.ModelwInt.Parameters; Interc = this.ModelwInt.Intercept; ## Update length of DoF vector DoF = ones (1, columns (Xfit)) * this.DoF(1); else ## Get parameters and intercept vectors from base model params = this.BaseModel.Parameters; Interc = this.BaseModel.Intercept; ## Get DoF from model DoF = this.DoF; endif ## Predict values from testing data yFit = predict_val (params, Xfit, Interc); ## Predict Standard Deviation and Intervals of estimated data if requested if (nargout > 0) ## Ensure that RowsUsed in the model are selected Y = this.Y(logical (this.RowsUsed)); X = this.X(logical (this.RowsUsed), :); ## Predict response from training predictor data with the trained model yrs = predict_val (params, X , Interc); ## Get the residuals between predicted and actual response data rs = Y - yrs; var_rs = var (rs); var_pr = var (yFit); # var is calculated here instead take sqrt(SD) t_mul = tinv (1 - alpha / 2, this.DoF); ydev = (yFit - mean (yFit)) .^ 2; ySD = sqrt (ydev / (rows (yFit) - 1)); varargout{1} = ySD; if (nargout > 1) moe = t_mul (1) * ySD; lower = (yFit - moe); upper = (yFit + moe); yInt = [lower, upper]; varargout{2} = yInt; endif endif endfunction ## -*- texinfo -*- ## @deftypefn {RegressionGAM} {} savemodel (@var{obj}, @var{filename}) ## ## Save a RegressionGAM object. ## ## @code{savemodel (@var{obj}, @var{filename})} saves a RegressionGAM ## object into a file defined by @var{filename}. ## ## @seealso{loadmodel, fitrgam, RegressionGAM} ## @end deftypefn function savemodel (obj, fname) ## Generate variable for class name classdef_name = "RegressionGAM"; ## Create variables from model properties X = obj.X; Y = obj.Y; NumObservations = obj.NumObservations; RowsUsed = obj.RowsUsed; NumPredictors = obj.NumPredictors; PredictorNames = obj.PredictorNames; ResponseName = obj.ResponseName; Formula = obj.Formula; Interactions = obj.Interactions; Knots = obj.Knots; Order = obj.Order; DoF = obj.DoF; Tol = obj.Tol; BaseModel = obj.BaseModel; ModelwInt = obj.ModelwInt; IntMatrix = obj.IntMatrix; ## Save classdef name and all model properties as individual variables save (fname, "classdef_name", "X", "Y", "NumObservations", "RowsUsed", ... "NumPredictors", "PredictorNames", "ResponseName", "Formula", ... "Interactions", "Knots", "Order", "DoF", "Tol", "BaseModel", ... "ModelwInt", "IntMatrix"); endfunction endmethods ## Helper functions methods (Access = private) ## Determine interactions from Interactions optional parameter function intMat = parseInteractions (this) if (islogical (this.Interactions)) ## Check that interaction matrix corresponds to predictors if (numel (this.PredictorNames) != columns (this.Interactions)) error (strcat (["RegressionGAM: columns in Interactions logical"], ... [" matrix must equal to the number of predictors."])); endif intMat = this.Interactions elseif (isnumeric (this.Interactions)) ## Need to measure the effect of all interactions to keep the best ## performing. Just check that the given number is not higher than ## p*(p-1)/2, where p is the number of predictors. p = this.NumPredictors; if (this.Interactions > p * (p - 1) / 2) error (strcat (["RegressionGAM: number of interaction terms"], ... [" requested is larger than all possible"], ... [" combinations of predictors in X."])); endif ## Get all combinations except all zeros allMat = flip (fullfact(p)([2:end],:), 2); ## Only keep interaction terms iterms = find (sum (allMat, 2) != 1); intMat = allMat(iterms); elseif (strcmpi (this.Interactions, "all")) ## Calculate all p*(p-1)/2 interaction terms allMat = flip (fullfact(p)([2:end],:), 2); ## Only keep interaction terms iterms = find (sum (allMat, 2) != 1); intMat = allMat(iterms); endif endfunction ## Determine interactions from formula function intMat = parseFormula (this) intMat = []; ## Check formula for syntax if (isempty (strfind (this.Formula, '~'))) error ("RegressionGAM: invalid syntax in Formula."); endif ## Split formula and keep predictor terms formulaParts = strsplit (this.Formula, '~'); ## Check there is some string after '~' if (numel (formulaParts) < 2) error ("RegressionGAM: no predictor terms in Formula."); endif predictorString = strtrim (formulaParts{2}); if (isempty (predictorString)) error ("RegressionGAM: no predictor terms in Formula."); endif ## Spit additive terms (between + sign) aterms = strtrim (strsplit (predictorString, '+')); ## Process all terms for i = 1:numel (aterms) ## Find individual terms (string missing ':') if (isempty (strfind (aterms(i), ':'){:})) ## Search PredictorNames to associate with column in X sterms = strcmp (this.PredictorNames, aterms(i)); ## Append to interactions matrix intMat = [intMat; sterms]; else ## Split interaction terms (string contains ':') mterms = strsplit (aterms{i}, ':'); ## Add each individual predictor to interaction term vector iterms = logical (zeros (1, this.NumPredictors)); for t = 1:numel (mterms) iterms = iterms | strcmp (this.PredictorNames, mterms(t)); endfor ## Check that all predictors have been identified if (sum (iterms) != t) error ("RegressionGAM: some predictors have not been identified."); endif ## Append to interactions matrix intMat = [intMat; iterms]; endif endfor ## Check that all terms have been identified if (! all (sum (intMat, 2) > 0)) error ("RegressionGAM: some terms have not been identified."); endif endfunction ## Fit the model function [iter, param, res, RSS] = fitGAM (this, X, Y, Inter, Knots, Order) ## Initialize variables converged = false; iter = 0; RSS = zeros (1, columns (X)); res = Y - Inter; ns = rows (X); Tol = this.Tol; ## Start training while (! (converged || iter > 1000)) iter += 1; ## Single cycle of backfit for j = 1:columns (X) ## Calculate residuals to fit spline if (iter > 1) res = res + ppval (param(j), X(:, j)); endif ## Fit an spline to the data gk = splinefit (X(:,j), res, Knots(j), "order", Order(j)); ## This might be wrong! We need to check this out RSSk(j) = abs (sum (abs (Y - ppval (gk, X(:,j)) - Inter)) .^ 2) / ns; param(j) = gk; res = res - ppval (param(j), X(:,j)); endfor ## Check if RSS is less than the tolerence if (all (abs (RSS - RSSk) <= Tol)) converged = true; endif ## Update RSS RSS = RSSk; endwhile endfunction endmethods methods (Static, Hidden) function mdl = load_model (filename, data) ## Create a RegressionGAM object mdl = RegressionGAM (1, 1); ## Get fieldnames from DATA (including private properties) names = fieldnames (data); ## Copy data into object for i = 1:numel (names) ## Check that fieldnames in DATA match properties in RegressionGAM try mdl.(names{i}) = data.(names{i}); catch error ("RegressionGAM.load_model: invalid model in '%s'.", ... filename) end_try_catch endfor endfunction endmethods endclassdef ## Helper function for making prediction of new data based on GAM model function ypred = predict_val (params, X, intercept) [nsample, ndims_X] = size (X); ypred = ones (nsample, 1) * intercept; ## Add the remaining terms for j = 1:ndims_X ypred = ypred + ppval (params(j), X (:,j)); endfor endfunction %!demo %! ## Train a RegressionGAM Model for synthetic values %! f1 = @(x) cos (3 * x); %! f2 = @(x) x .^ 3; %! x1 = 2 * rand (50, 1) - 1; %! x2 = 2 * rand (50, 1) - 1; %! y = f1(x1) + f2(x2); %! y = y + y .* 0.2 .* rand (50,1); %! X = [x1, x2]; %! a = fitrgam (X, y, "tol", 1e-3) %!demo %! ## Declare two different functions %! f1 = @(x) cos (3 * x); %! f2 = @(x) x .^ 3; %! %! ## Generate 80 samples for f1 and f2 %! x = [-4*pi:0.1*pi:4*pi-0.1*pi]'; %! X1 = f1 (x); %! X2 = f2 (x); %! %! ## Create a synthetic response by adding noise %! rand ("seed", 3); %! Ytrue = X1 + X2; %! Y = Ytrue + Ytrue .* 0.2 .* rand (80,1); %! %! ## Assemble predictor data %! X = [X1, X2]; %! %! ## Train the GAM and test on the same data %! a = fitrgam (X, Y, "order", [5, 5]); %! [ypred, ySDsd, yInt] = predict (a, X); %! %! ## Plot the results %! figure %! [sortedY, indY] = sort (Ytrue); %! plot (sortedY, "r-"); %! xlim ([0, 80]); %! hold on %! plot (ypred(indY), "g+") %! plot (yInt(indY,1), "k:") %! plot (yInt(indY,2), "k:") %! xlabel ("Predictor samples"); %! ylabel ("Response"); %! title ("actual vs predicted values for function f1(x) = cos (3x) "); %! legend ({"Theoretical Response", "Predicted Response", "Prediction Intervals"}); %! %! ## Use 30% Holdout partitioning for training and testing data %! C = cvpartition (80, "HoldOut", 0.3); %! [ypred, ySDsd, yInt] = predict (a, X(test(C),:)); %! %! ## Plot the results %! figure %! [sortedY, indY] = sort (Ytrue(test(C))); %! plot (sortedY, 'r-'); %! xlim ([0, sum(test(C))]); %! hold on %! plot (ypred(indY), "g+") %! plot (yInt(indY,1),'k:') %! plot (yInt(indY,2),'k:') %! xlabel ("Predictor samples"); %! ylabel ("Response"); %! title ("actual vs predicted values for function f1(x) = cos (3x) "); %! legend ({"Theoretical Response", "Predicted Response", "Prediction Intervals"}); ## Test constructor %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [1; 2; 3; 4]; %! a = RegressionGAM (x, y); %! assert ({a.X, a.Y}, {x, y}) %! assert ({a.BaseModel.Intercept}, {2.5000}) %! assert ({a.Knots, a.Order, a.DoF}, {[5, 5, 5], [3, 3, 3], [8, 8, 8]}) %! assert ({a.NumObservations, a.NumPredictors}, {4, 3}) %! assert ({a.ResponseName, a.PredictorNames}, {"Y", {"x1", "x2", "x3"}}) %! assert ({a.Formula}, {[]}) %!test %! x = [1, 2, 3, 4; 4, 5, 6, 7; 7, 8, 9, 1; 3, 2, 1, 2]; %! y = [1; 2; 3; 4]; %! pnames = {"A", "B", "C", "D"}; %! formula = "Y ~ A + B + C + D + A:C"; %! intMat = logical ([1,0,0,0;0,1,0,0;0,0,1,0;0,0,0,1;1,0,1,0]); %! a = RegressionGAM (x, y, "predictors", pnames, "formula", formula); %! assert ({a.IntMatrix}, {intMat}) %! assert ({a.ResponseName, a.PredictorNames}, {"Y", pnames}) %! assert ({a.Formula}, {formula}) ## Test input validation for constructor %!error RegressionGAM () %!error RegressionGAM (ones(10,2)) %!error ... %! RegressionGAM (ones(10,2), ones (5,1)) %!error ... %! RegressionGAM ([1;2;3;"a";4], ones (5,1)) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "some", "some") %!error %! RegressionGAM (ones(10,2), ones (10,1), "formula", {"y~x1+x2"}) %!error %! RegressionGAM (ones(10,2), ones (10,1), "formula", [0, 1, 0]) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "formula", "something") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "formula", "something~") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "formula", "something~") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "formula", "something~x1:") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "interactions", "some") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "interactions", -1) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "interactions", [1 2 3 4]) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "interactions", 3) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "formula", "y ~ x1 + x2", "interactions", 1) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "interactions", 1, "formula", "y ~ x1 + x2") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "knots", "a") %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "order", 3, "dof", 2, "knots", 5) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "dof", 'a') %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "knots", 5, "order", 3, "dof", 2) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "order", 'a') %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "knots", 5, "dof", 2, "order", 2) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "tol", -1) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "responsename", -1) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "predictors", -1) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "predictors", ['a','b','c']) %!error ... %! RegressionGAM (ones(10,2), ones (10,1), "predictors", {'a','b','c'}) ## Test input validation for predict method %!error ... %! predict (RegressionGAM (ones(10,1), ones(10,1))) %!error ... %! predict (RegressionGAM (ones(10,1), ones(10,1)), []) %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), 2) %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), ones (10,2), "some", "some") %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), ones (10,2), "includeinteractions", "some") %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), ones (10,2), "includeinteractions", 5) %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), ones (10,2), "alpha", 5) %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), ones (10,2), "alpha", -1) %!error ... %! predict (RegressionGAM(ones(10,2), ones(10,1)), ones (10,2), "alpha", 'a') statistics-release-1.7.3/inst/adtest.m000066400000000000000000001007271475240274700177740ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} adtest (@var{x}) ## @deftypefnx {statistics} {@var{h} =} adtest (@var{x}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} adtest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{adstat}, @var{cv}] =} adtest (@dots{}) ## ## Anderson-Darling goodness-of-fit hypothesis test. ## ## @code{@var{h} = adtest (@var{x})} returns a test decision for the null ## hypothesis that the data in vector @var{x} is from a population with a normal ## distribution, using the Anderson-Darling test. The alternative hypothesis is ## that x is not from a population with a normal distribution. The result ## @var{h} is 1 if the test rejects the null hypothesis at the 5% significance ## level, or 0 otherwise. ## ## @code{@var{h} = adtest (@var{x}, @var{Name}, @var{Value})} returns a test ## decision for the Anderson-Darling test with additional options specified by ## one or more Name-Value pair arguments. For example, you can specify a null ## distribution other than normal, or select an alternative method for ## calculating the p-value, such as a Monte Carlo simulation. ## ## The following parameters can be parsed as Name-Value pair arguments. ## ## @multitable @columnfractions 0.25 0.75 ## @headitem Name @tab Description ## @item "Distribution" @tab The distribution being tested for. It tests ## whether @var{x} could have come from the specified distribution. There are ## two choise available for parsing distribution parameters: ## @end multitable ## ## @itemize ## @item ## One of the following char strings: "norm", "exp", "ev", "logn", "weibull", ## for defining either the 'normal', 'exponential', 'extreme value', lognormal, ## or 'Weibull' distribution family, accordingly. In this case, @var{x} is ## tested against a composite hypothesis for the specified distribution family ## and the required distribution parameters are estimated from the data in ## @var{x}. The default is "norm". ## ## @item ## A cell array defining a distribution in which the first cell contains a char ## string with the distribution name, as mentioned above, and the consecutive ## cells containing all specified parameters of the null distribution. In this ## case, @var{x} is tested against a simple hypothesis. ## @end itemize ## ## @multitable @columnfractions 0.25 0.75 ## @item "Alpha" @tab Significance level alpha for the test. Any scalar ## numeric value between 0 and 1. The default is 0.05 corresponding to the 5% ## significance level. ## ## @item "MCTol" @tab Monte-Carlo standard error for the p-value, ## @var{pval}, value. which must be a positive scalar value. In this case, an ## approximation for the p-value is computed directly, using Monte-Carlo ## simulations. ## ## @item "Asymptotic" @tab Method for calculating the p-value of the ## Anderson-Darling test, which can be either true or false logical value. If ## you specify 'true', adtest estimates the p-value using the limiting ## distribution of the Anderson-Darling test statistic. If you specify 'false', ## adtest calculates the p-value based on an analytical formula. For sample ## sizes greater than 120, the limiting distribution estimate is likely to be ## more accurate than the small sample size approximation method. ## @end multitable ## ## @itemize ## @item ## If you specify a distribution family with unknown parameters for the ## distribution Name-Value pair (i.e. composite distribution hypothesis test), ## the "Asymptotic" option must be false. ## @item ## ## If you use MCTol to calculate the p-value using a Monte Carlo simulation, ## the "Asymptotic" option must be false. ## @end itemize ## ## @code{[@var{h}, @var{pval}] = adtest (@dots{})} also returns the p-value, ## @var{pval}, of the Anderson-Darling test, using any of the input arguments ## from the previous syntaxes. ## ## @code{[@var{h}, @var{pval}, @var{adstat}, @var{cv}] = adtest (@dots{})} also ## returns the test statistic, @var{adstat}, and the critical value, @var{cv}, ## for the Anderson-Darling test. ## ## The Anderson-Darling test statistic belongs to the family of Quadratic ## Empirical Distribution Function statistics, which are based on the weighted ## sum of the difference @math{[Fn(x)-F(x)]^2} over the ordered sample values ## @math{X1 < X2 < ... < Xn}, where @math{F} is the hypothesized continuous ## distribution and @math{Fn} is the empirical CDF based on the data sample with ## @math{n} sample points. ## ## @seealso{kstest} ## @end deftypefn function [H, pVal, ADStat, CV] = adtest (x, varargin) ## Check for valid input data if (nargin < 1) print_usage; endif ## Ensure the sample data is a real vector. if (! isvector (x) || ! isreal(x)) error ("adtest: X must be a vector of real numbers."); endif ## Add defaults distribution = "norm"; isCompositeTest = true; alpha = 0.05; MCTol = []; asymptotic = false; ## Parse arguments and check if parameter/value pairs are valid i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "distribution" i = i + 1; distribution = varargin{i}; ## Check for char string or cell array ## If distribution is a char string, then X is tested against a ## composite hypothesis for the specified distribution family and ## distribution parameters are estimated from the sample data. valid_dists = {"norm", "exp", "ev", "logn", "weibull"}; if (ischar (distribution)) if (! any (strcmpi (distribution, valid_dists))) error ("adtest: invalid distribution family in char string."); endif ## If distribution is a cell array, then X is tested against a ## simple hypothesis for the specified distribution and ## distribution parameters given in the cell array. elseif (iscell (distribution)) if (! any (strcmpi (distribution(1), valid_dists))) error ("adtest: invalid distribution family in cell array."); endif ## Check for valid distribution parameter in cell array err_msg = "adtest: invalid distribution parameters in cell array."; if (strcmpi (distribution(1), "norm")) if (numel (distribution) != 3) error (err_msg); endif z = normcdf (x, distribution{2}, distribution{3}); elseif (strcmpi (distribution(1), "exp")) if (numel (distribution) != 2) error (err_msg); endif z = expcdf (x, distribution{2}); elseif (strcmpi (distribution(1), "ev")) if (numel (distribution) != 3) error (err_msg); endif z = evcdf (x, distribution{2}, distribution{3}); elseif (strcmpi (distribution(1), "logn")) if (numel (distribution) != 3) error (err_msg); endif z = logncdf (x, distribution{2}, distribution{3}); elseif (strcmpi (distribution(1), "weibull")) if (numel (distribution) != 3) error (err_msg); endif z = wblcdf (x, distribution{2}, distribution{3}); endif isCompositeTest = false; else error ("adtest: invalid distribution option."); endif case "alpha" i = i + 1; alpha = varargin{i}; ## Check for valid alpha if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("adtest: invalid value for alpha."); endif case "mctol" i = i + 1; MCTol = varargin{i}; ## Check Monte Carlo tolerance is a numeric scalar greater than 0 if (! isempty (MCTol) && (! isscalar (MCTol) || MCTol <= 0)) error ("adtest: invalid Monte Carlo Tolerance."); endif case "asymptotic" i = i + 1; asymptotic = varargin{i}; ## Check that it is either true or false if (! isbool (asymptotic)) error ("adtest: asymptotic option must be boolean."); endif otherwise error ("adtest: invalid Name argument."); endswitch i = i + 1; endwhile ## Check conflicts with asymptotic option if (asymptotic && isCompositeTest) error (strcat (["adtest: asymptotic option is not valid"], ... [" for the composite distribution test."])); elseif (asymptotic && ! isempty (MCTol)) error (strcat (["adtest: asymptotic option is not valid"], ... [" for the Monte Carlo simulation test."])); endif ## Remove missing values. x = x(! isnan (x)); ## Compute sample size n. n = length (x); ## For composite tests if (isCompositeTest) ## Abort for sample size less than 4 if (n < 4) error ("adtest: not enough data for composite testing."); endif ## If data follow a lognormal distribution, log(x) is normally distributed if (strcmpi (distribution, "logn")) x = log (x); distribution = "norm"; ## If data follow a Weibull distribution, log(x) has a type I extreme-value ## distribution elseif (strcmpi (distribution, "weibull")) x = log (x); distribution = "ev"; endif ## Compute ADStat switch distribution case "norm" ## Check for complex numbers due to log (x) if (any (! isreal (x))) ## Data is not compatible with logn distribution test warning ("adtest: bad data for lognormal distribution."); ADStat = NaN; else z = normcdf (x, mean (x), std (x)); ADStat = ComputeADStat (z, n); endif case "exp" z = expcdf (x, mean (x)); ADStat = ComputeADStat (z, n); case "ev" ## Check for complex numbers due to log (x) if (any (! isreal (x))) ## Data is not compatible with Weibull distribution test warning ("adtest: bad data for Weibull distribution."); ADStat = NaN; else params = evfit (x); z = evcdf (x, params (1), params (2)); ADStat = ComputeADStat (z, n); endif endswitch ## Compute p-value and critical values without Monte Carlo simulation if (isempty (MCTol)) alphas = [0.0005, 0.0010, 0.0015, 0.0020, 0.0050, 0.0100, 0.0250, ... 0.0500, 0.1000, 0.1500, 0.2000, 0.2500, 0.3000, 0.3500, ... 0.4000, 0.4500, 0.5000, 0.5500, 0.6000, 0.6500, 0.7000, ... 0.7500, 0.8000, 0.8500, 0.9000, 0.9500, 0.9900]; switch distribution case "norm" CVs = computeCriticalValues_norm (n); case "exp" CVs = computeCriticalValues_exp (n); case "ev" CVs = computeCriticalValues_ev (n); endswitch ## 1-D interpolation into the tabulated results pp = pchip (log (alphas), CVs); CV = ppval (pp, log (alpha)); ## If alpha is not within the lookup table, throw a warning ## Hypothesis result is computed by comparing the p-value with ## alpha, rather than CV with ADStat if alpha < alphas(1) CV = CVs(1); warning ("adtest: alpha not within the lookup table."); elseif alpha > alphas(end) CV = CVs(end); warning ("adtest: alpha not within the lookup table."); endif if (ADStat > CVs(1)) ## P value is smaller than smallest tabulated value warning (strcat (["adtest: out of range min p-value:"], ... sprintf (" %g", alphas(1)))); pVal = alphas(1); elseif (ADStat < CVs(end)) ## P value is larger than largest tabulated value warning (strcat (["adtest: out of range max p-value:"], ... sprintf (" %g", alphas(end)))); pVal = alphas(end); elseif (isnan (ADStat)) ## Handle certain cases of negative data (ADStat == NaN) pVal = 0; else ## Find p-value by inverse interpolation i = find (ADStat > CVs, 1, "first"); logPVal = fzero (@(x)ppval(pp,x) - ADStat, log(alphas([i-1,i]))); pVal = exp (logPVal); endif ## Compute p-value and critical values without Monte Carlo simulation else [CV, pVal] = adtestMC (ADStat, n, alpha, distribution, mctol); endif ## Calculate H if (isnan (ADStat)) H = true; else if (isempty (MCTol)) if (alpha < alphas(1) || alpha > alphas(end)) H = (pVal < alpha); else H = (ADStat > CV); endif else H = (ADStat > CV); endif endif ## For simple tests else ## Compute the Anderson-Darling statistic ADStat = ComputeADStat (z, n); ## Compute p-value and critical values without Monte Carlo simulation if (isempty (MCTol)) alphas = [0.0005, 0.0010, 0.0015, 0.0020, 0.0050, 0.0100, 0.0250, ... 0.0500, 0.1000, 0.1500, 0.2000, 0.2500, 0.3000, 0.3500, ... 0.4000, 0.4500, 0.5000, 0.5500, 0.6000, 0.6500, 0.7000, ... 0.7500, 0.8000, 0.8500, 0.9000, 0.9500, 0.9900]; if (asymptotic) if (n <= 120) warning ("adtest: asymptotic distribution with small sample size."); endif pVal = 1 - ADInf (ADStat); ## For extra output arguments if (nargout > 3) ## Make sure alpha is within the lookup table validateAlpha (alpha, alphas); ## Find critical values critVals = findAsymptoticDistributionCriticalValues; i = find (alphas > alpha, 1, "first"); startVal = critVals(i-1); CV = fzero(@(ad)1-ADInf(ad)-alpha, startVal); endif else if (n == 1) pVal = 1 - sqrt (1 - 4 * exp (-1 - ADStat)); else if (n < 4) warning ("adtest: small sample size."); endif pVal = 1 - ADn (n, ADStat); endif ## For extra output arguments if (nargout > 3) ## Make sure alpha is within the lookup table validateAlpha (alpha, alphas); ## Find critical values [CVs, sampleSizes] = findCriticalValues; [OneOverSampleSizes, LogAlphas] = meshgrid (1 ./ sampleSizes, ... log (alphas)); CV = interp2 (OneOverSampleSizes, LogAlphas, CVs', 1./n, log (alpha)); endif endif ## Compute p-value and critical values with Monte Carlo simulation else [CV, pVal] = adtestMC (ADStat, n, alpha, "unif", mctol); endif ## Calculate H H = (pVal < alpha); endif endfunction ## Compute Anderson-Darling Statistic function ADStat = ComputeADStat (z, n) ## Sort the data and compute the statistic z = reshape (z, n, 1); z = sort (z); w = 2 * (1:n) - 1; ADStat = - w * (log (z)+ log (1-z(end:-1:1))) / n - n; endfunction ## Anderson-Darling distribution Pr(An= 2); x(ad >= 2) = exp (-exp (1.0776 - (2.30695 - (0.43424 - (0.082433 - ... (0.008056 - 0.0003146 .* adh) .* adh) .* adh) .* adh) .* adh)); ## Compute error function defined between 0 and 1 if (any (x < 0 | x > 1)) error ("adtest: invalid values for error function."); endif e = zeros (size (x)); c = 0.01265 + 0.1757/n; ## Define function by intervals using 3 fixed functions g1, g2, g3. xc1 = x(x < c) / c; g1 = sqrt (xc1) .* (1 - xc1) .* (49 * xc1 - 102); e(x < c) = (0.0037 / n ^ 3 + 0.00078 / n ^ 2 + 0.00006 / n) * g1; xc2 = (x(x >= c & x < 0.8) - c) ./ (0.8 - c); g2 = -0.00022633 + (6.54034 - (14.6538 - (14.458 - (8.259 -... 1.91864 .* xc2) .* xc2) .* xc2) .* xc2) .* xc2; e(x >= c & x < 0.8) = (0.04213 / n + 0.01365 / n ^ 2) * g2; xc3 = x(x >= 0.8); e(x >= 0.8) = 1 / n * (-130.2137 + (745.2337 - (1705.091 - (1950.646 -... (1116.360 - 255.7844 .* ... xc3) .* xc3) .* xc3) .* xc3) .* xc3) .* xc3; p = x + e; endfunction ## Evaluate the Anderson-Darling limit distribution function ad = ADInf (z) ## Distribution is invalid for negative values if (z < 0) error ("adtest: invalid X for asymptotic distribution."); endif ## Due to floating point precision: ## Return 0 below a certain threshold if (z < 0.02) ad = 0; return; ## Return 1 above the following threshold elseif (z >= 32.4) ad = 1; return; endif n = 1:500; K = 1/z*[1, ((4*n + 1).*cumprod((1/2 - n)./n))]; ADTerms = arrayfun(@(j)ADf(z,j),0:500); ad = ADTerms*K'; endfunction ## Series expansion for f(z,j) called by ADInf function f = ADf(z,j) ## Compute t=tj=(4j+1)^2*pi^2/(8z) t = (4 * j + 1) ^ 2 * 1.233700550136170 / z; ## First 2 terms in recursive series ## c0=pi*exp(-t)/(sqrt(2t)) ## c1=pi*sqrt(pi/2)*erfc(sqrt(t)) c0 = 2.221441469079183 * exp (-t) / sqrt (t); c1 = 3.937402486430604 * erfc (sqrt (t)); r = z / 8; f = c0 + c1 * r; ## Evaluate the recursion for n = 2:500 c = 1 / (n - 1) * ((n - 3 / 2 - t) * c1 + t * c0); r = r * (z / 8) * (1 / n); fn = f + c * r; c0 = c1; c1 = c; if (f == fn) return; endif f = fn; endfor endfunction ## An improved version of the Petitt method for the composite normal case. function CVs = computeCriticalValues_norm (n) CVs = [1.5649, 1.4407, 1.3699, 1.3187, 1.1556, 1.0339, 0.8733, ... 0.7519, 0.6308, 0.5598, 0.5092, 0.4694, 0.4366, 0.4084, ... 0.3835, 0.3611, 0.3405, 0.3212, 0.3029, 0.2852, 0.2679, ... 0.2506, 0.2330, 0.2144, 0.1935, 0.1673, 0.1296] + ... [-0.9362, -0.9029, -0.8906, -0.8865, -0.8375, -0.7835, -0.6746, ... -0.5835, -0.4775, -0.4094, -0.3679, -0.3327, -0.3099, -0.2969, ... -0.2795, -0.2623, -0.2464, -0.2325, -0.2164, -0.1994, -0.1784, ... -0.1569, -0.1377, -0.1201, -0.0989, -0.0800, -0.0598] ./ n + ... [-8.3249, -6.6022, -5.6461, -4.9685, -3.2208, -2.1647, -1.2460, ... -0.7803, -0.4627, -0.3672, -0.2833, -0.2349, -0.1442, -0.0229, ... 0.0377, 0.0817, 0.1150, 0.1583, 0.1801, 0.1887, 0.1695, ... 0.1513, 0.1533, 0.1724, 0.2027, 0.3158, 0.6431] ./ n ^ 2; endfunction ## An improved version of the Petitt method for the composite exponential case. function CVs = computeCriticalValues_exp (n) CVs = [3.2371, 2.9303, 2.7541, 2.6307, 2.2454, 1.9621, 1.5928, ... 1.3223, 1.0621, 0.9153, 0.8134, 0.7355, 0.6725, 0.6194, ... 0.5734, 0.5326, 0.4957, 0.4617, 0.4301, 0.4001, 0.3712, ... 0.3428, 0.3144, 0.2849, 0.2527, 0.2131, 0.1581] + ... [1.6146, 0.8716, 0.4715, 0.2066, -0.4682, -0.7691, -0.7388, ... -0.5758, -0.4036, -0.3142, -0.2564, -0.2152, -0.1845, -0.1607, ... -0.1409, -0.1239, -0.1084, -0.0942, -0.0807, -0.0674, -0.0537, ... -0.0401, -0.0261, -0.0116, 0.0047, 0.0275, 0.0780] ./ n; endfunction ## An improved version of the Petitt method for the composite extreme value case function CVs = computeCriticalValues_ev(n) CVs = [1.6473, 1.5095, 1.4301, 1.3742, 1.1974, 1.0667, 0.8961, ... 0.7683, 0.6416, 0.5680, 0.5156, 0.4744, 0.4405, 0.4115, ... 0.3858, 0.3626, 0.3415, 0.3217, 0.3029, 0.2848, 0.2672, ... 0.2496, 0.2315, 0.2124, 0.1909, 0.1633, 0.1223] + ... [-0.7097, -0.5934, -0.5328, -0.4930, -0.3708, -0.2973, -0.2075, ... -0.1449, -0.0892, -0.0619, -0.0442, -0.0302, -0.0196, -0.0112, ... -0.0039, 0.0024, 0.0074, 0.0122, 0.0167, 0.0207, 0.0245, ... 0.0282, 0.0323, 0.0371, 0.0436, 0.0549, 0.0813] ./ n .^ (1 / 2); endfunction ## Find rows of critical values at relevant significance levels function [CVs, sampleSizes] = findCriticalValues CVs = [7.2943, 6.6014, 6.1962, 5.9088, 4.9940, 4.3033, 3.3946, 2.7142, ... 2.0470, 1.6682, 1.4079, 1.2130, 1.0596, 0.9353, 0.8326, 0.7465, ... 0.6740, 0.6126, 0.5606, 0.5170, 0.4806, 0.4508, 0.4271, 0.4091, ... NaN, NaN, NaN; ... %n=1 7.6624, 6.4955, 5.9916, 5.6682, 4.7338, 4.0740, 3.2247, 2.5920, ... 1.9774, 1.6368, 1.4078, 1.2329, 1.0974, 0.9873, 0.8947, 0.8150, ... 0.7448, 0.6820, 0.6251, 0.5727, 0.5240, 0.4779, 0.4337, 0.3903, ... 0.3462, 0.3030, 0.2558; ... %n=2 7.2278, 6.3094, 5.8569, 5.5557, 4.6578, 4.0111, 3.1763, 2.5581, ... 1.9620, 1.6314, 1.4079, 1.2390, 1.1065, 0.9979, 0.9060, 0.8264, ... 0.7560, 0.6928, 0.6350, 0.5816, 0.5314, 0.4835, 0.4371, 0.3907, ... 0.3424, 0.2885, 0.2255; ... %n=3 7.0518, 6.2208, 5.7904, 5.4993, 4.6187, 3.9788, 3.1518, 2.5414, ... 1.9545, 1.6288, 1.4080, 1.2416, 1.1104, 1.0025, 0.9110, 0.8315, ... 0.7611, 0.6977, 0.6397, 0.5859, 0.5352, 0.4868, 0.4395, 0.3920, ... 0.3421, 0.2845, 0.2146; ... %n=4 6.9550, 6.1688, 5.7507, 5.4653, 4.5949, 3.9591, 3.1370, 2.5314, ... 1.9501, 1.6272, 1.4080, 1.2430, 1.1126, 1.0051, 0.9138, 0.8344, ... 0.7640, 0.7005, 0.6424, 0.5884, 0.5375, 0.4888, 0.4411, 0.3930, ... 0.3424, 0.2833, 0.2097; ... %n=5 6.8935, 6.1345, 5.7242, 5.4426, 4.5789, 3.9459, 3.1271, 2.5248, ... 1.9472, 1.6262, 1.4081, 1.2439, 1.1140, 1.0067, 0.9156, 0.8362, ... 0.7658, 0.7023, 0.6441, 0.5901, 0.5391, 0.4901, 0.4422, 0.3938, ... 0.3427, 0.2828, 0.2071; ... %n=6 6.8509, 6.1102, 5.7053, 5.4264, 4.5674, 3.9364, 3.1201, 2.5201, ... 1.9451, 1.6255, 1.4081, 1.2445, 1.1149, 1.0079, 0.9168, 0.8375, ... 0.7671, 0.7036, 0.6454, 0.5912, 0.5401, 0.4911, 0.4430, 0.3944, ... 0.3430, 0.2826, 0.2056; ... %n=7 6.8196, 6.0920, 5.6912, 5.4142, 4.5588, 3.9293, 3.1148, 2.5166, ... 1.9436, 1.6249, 1.4081, 1.2450, 1.1156, 1.0087, 0.9177, 0.8384, ... 0.7681, 0.7045, 0.6463, 0.5921, 0.5409, 0.4918, 0.4436, 0.3949, ... 0.3433, 0.2825, 0.2046; ... %n=8 6.7486, 6.0500, 5.6582, 5.3856, 4.5384, 3.9124, 3.1024, 2.5084, ... 1.9400, 1.6237, 1.4081, 1.2460, 1.1171, 1.0106, 0.9197, 0.8406, ... 0.7702, 0.7066, 0.6483, 0.5941, 0.5428, 0.4935, 0.4451, 0.3961, ... 0.3441, 0.2826, 0.2029; ... %n=12 6.7140, 6.0292, 5.6417, 5.3713, 4.5281, 3.9040, 3.0962, 2.5044, ... 1.9382, 1.6230, 1.4081, 1.2465, 1.1179, 1.0115, 0.9207, 0.8416, ... 0.7712, 0.7077, 0.6493, 0.5950, 0.5437, 0.4944, 0.4459, 0.3968, ... 0.3445, 0.2827, 0.2023; ... %n=16 6.6801, 6.0084, 5.6252, 5.3569, 4.5178, 3.8955, 3.0900, 2.5003, ... 1.9365, 1.6224, 1.4081, 1.2470, 1.1186, 1.0123, 0.9217, 0.8426, ... 0.7723, 0.7087, 0.6503, 0.5960, 0.5446, 0.4952, 0.4466, 0.3974, ... 0.3450, 0.2829, 0.2019; ... %n=24 6.6468, 5.9877, 5.6087, 5.3425, 4.5075, 3.8869, 3.0837, 2.4963, ... 1.9347, 1.6218, 1.4082, 1.2474, 1.1193, 1.0132, 0.9226, 0.8436, ... 0.7732, 0.7097, 0.6513, 0.5969, 0.5455, 0.4960, 0.4474, 0.3980, ... 0.3455, 0.2832, 0.2016; ... %n=48 6.6634, 5.9980, 5.6169, 5.3497, 4.5127, 3.8912, 3.0868, 2.4983, ... 1.9356, 1.6221, 1.4081, 1.2472, 1.1190, 1.0128, 0.9222, 0.8431, ... 0.7728, 0.7092, 0.6508, 0.5965, 0.5451, 0.4956, 0.4470, 0.3977, ... 0.3453, 0.2830, 0.2017; ... %n=32 6.6385, 5.9825, 5.6046, 5.3389, 4.5049, 3.8848, 3.0822, 2.4953, ... 1.9343, 1.6217, 1.4082, 1.2475, 1.1195, 1.0134, 0.9228, 0.8438, ... 0.7735, 0.7099, 0.6516, 0.5972, 0.5458, 0.4962, 0.4476, 0.3982, ... 0.3456, 0.2833, 0.2016; ... %n=64 6.6318, 5.9783, 5.6012, 5.3360, 4.5028, 3.8830, 3.0809, 2.4944, ... 1.9339, 1.6215, 1.4082, 1.2476, 1.1197, 1.0136, 0.9230, 0.8440, ... 0.7737, 0.7101, 0.6517, 0.5974, 0.5459, 0.4964, 0.4477, 0.3984, ... 0.3457, 0.2833, 0.2015; ... %n=88 6.6297, 5.9770, 5.6001, 5.3350, 4.5021, 3.8825, 3.0805, 2.4942, ... 1.9338, 1.6215, 1.4082, 1.2476, 1.1197, 1.0136, 0.9231, 0.8441, ... 0.7738, 0.7102, 0.6518, 0.5974, 0.5460, 0.4965, 0.4478, 0.3984, ... 0.3458, 0.2834, 0.2015; ... %n=100 6.6262, 5.9748, 5.5984, 5.3335, 4.5010, 3.8816, 3.0798, 2.4937, ... 1.9336, 1.6214, 1.4082, 1.2477, 1.1198, 1.0137, 0.9232, 0.8442, ... 0.7739, 0.7103, 0.6519, 0.5975, 0.5461, 0.4966, 0.4479, 0.3985, ... 0.3458, 0.2834, 0.2015; ... %n=128 6.6201, 5.9709, 5.5953, 5.3308, 4.4990, 3.8800, 3.0787, 2.4930, ... 1.9333, 1.6213, 1.4082, 1.2478, 1.1199, 1.0139, 0.9234, 0.8443, ... 0.7740, 0.7105, 0.6521, 0.5977, 0.5463, 0.4967, 0.4480, 0.3986, ... 0.3459, 0.2834, 0.2015; ... %n=256 6.6127, 5.9694, 5.5955, 5.3314, 4.4982, 3.8781, 3.0775, 2.4924, ... 1.9330, 1.6212, 1.4082, 1.2479, 1.1201, 1.0140, 0.9235, 0.8445, ... 0.7742, 0.7106, 0.6523, 0.5979, 0.5464, 0.4969, 0.4481, 0.3987, ... 0.3460, 0.2835, 0.2015]; %n=Inf sampleSizes = [1 2 3 4 5 6 7 8 12 16 24 32 48 64 88 100 128 256 Inf]; endfunction % ------------------------------------------ function critVals = findAsymptoticDistributionCriticalValues critVals = [6.6127034546551, 5.9694013422151, 5.5954643397078, ... 5.3313658857909, 4.4981996466091, 3.8781250216054, ... 3.0774641787107, 2.4923671600494, 1.9329578327416, ... 1.6212385363175, 1.4081977005506, 1.2478596347253, ... 1.1200136586965, 1.0140004020016, 0.9235137094902, ... 0.8445069178452, 0.7742142410993, 0.7106405935247, ... 0.6522701010084, 0.5978828157471, 0.5464229310982, ... 0.4968804113119, 0.4481425895777, 0.3987228486242, ... 0.3460480234939, 0.2835161264344, 0.2014922164166]; %n=Inf endfunction ## Make sure alpha is within the lookup table function validateAlpha (alpha, alphas) if (alpha < alphas(1) || alpha > alphas(end)) error (strcat (["adtest: out of range invalid alpha -"], ... sprintf (" lower limit: %g", alphas(1)),... sprintf (" upper limit: %g", alphas(end)))); endif endfunction ## Simulated critical values and p-values for Anderson-Darling test function [crit, p] = adtestMC (ADStat, n, alpha, distribution, MCTol) ## Initial values vartol = mctol^2; crit = 0; p = 0; mcRepsTot = 0; mcRepsMin = 1000; ## Monte Carlo loop while true mcRepsOld = mcRepsTot; mcReps = ceil(mcRepsMin - mcRepsOld); ADstatMC = zeros(mcReps,1); ## Switch to selected distribution switch distribution case "norm" mu0 = 0; sigma0 = 1; for rep = 1:length (ADstatMC) x = normrnd (mu0, sigma0, n, 1); xCDF = sort (x); nullCDF = normcdf (xCDF, mean (x), std (x)); w = 2 * (1:n) - 1 ; ADstatMC(rep) = - w * (log (nullCDF) + ... log (1 - nullCDF(end:-1:1))) / n - n; endfor case "exp" beta0 = 1; for rep = 1:length (ADstatMC) x = exprnd (beta0, n, 1); xCDF = sort (x); nullCDF = expcdf (xCDF, mean (x)); w = 2 * (1:n) - 1 ; ADstatMC(rep) = - w * (log (nullCDF) + ... log (1 - nullCDF(end:-1:1))) / n - n; endfor case "ev" mu0 = 0; sigma0 = 1; for rep = 1:length (ADstatMC) x = evrnd (mu0, sigma0, n, 1); pHat = evfit (x); xCDF = sort (x); nullCDF = evcdf (xCDF, pHat(1), pHat(2)); w = 2 * (1:n) - 1 ; ADstatMC(rep) = - w * (log (nullCDF) + ... log (1 - nullCDF(end:-1:1))) / n - n; endfor case "unif" for rep = 1:length(ADstatMC) z = sort (rand (n, 1)); w = 2 * (1:n) - 1 ; ADstatMC(rep) = - w * (log (z) + ... log (1 - z(end:-1:1))) / n - n; endfor endswitch critMC = prctile (ADstatMC, 100 * (1 - alpha)); pMC = sum (ADstatMC > ADStat) ./ mcReps; mcRepsTot = mcRepsOld + mcReps; crit = (mcRepsOld * crit + mcReps * critMC) / mcRepsTot; p = (mcRepsOld * p + mcReps * pMC) / mcRepsTot; ## Compute a std err for p, with lower bound (1/N)*(1-1/N)/N when p==0. sepsq = max (p * (1 - p) / mcRepsTot, 1 / mcRepsTot ^ 2); if (sepsq < MCTol ^ 2) break endif ## Based on the current estimate, find the number of trials needed to ## make the MC std err less than the specified tolerance. mcRepsMin = 1.2 * (mcRepsTot * sepsq) / (MCTol ^ 2); endwhile endfunction ## Test input validation %!error adtest (); %!error adtest (ones (20,2)); %!error adtest ([1+i,0-3i]); %!error ... %! adtest (ones (20,1), "Distribution", "normal"); %!error ... %! adtest (rand (20,1), "Distribution", {"normal", 5, 3}); %!error ... %! adtest (rand (20,1), "Distribution", {"norm", 5}); %!error ... %! adtest (rand (20,1), "Distribution", {"exp", 5, 4}); %!error ... %! adtest (rand (20,1), "Distribution", {"ev", 5}); %!error ... %! adtest (rand (20,1), "Distribution", {"logn", 5, 3, 2}); %!error ... %! adtest (rand (20,1), "Distribution", {"Weibull", 5}); %!error ... %! adtest (rand (20,1), "Distribution", 35); %!error ... %! adtest (rand (20,1), "Name", "norm"); %!error ... %! adtest (rand (20,1), "Name", {"norm", 75, 10}); %!error ... %! adtest (rand (20,1), "Distribution", "norm", "Asymptotic", true); %!error ... %! adtest (rand (20,1), "MCTol", 0.001, "Asymptotic", true); %!error ... %! adtest (rand (20,1), "Distribution", {"norm", 5, 3}, "MCTol", 0.001, ... %! "Asymptotic", true); %!error ... %! [h, pval, ADstat, CV] = adtest (ones (20,1), "Distribution", {"norm",5,3},... %! "Alpha", 0.000000001); %!error ... %! [h, pval, ADstat, CV] = adtest (ones (20,1), "Distribution", {"norm",5,3},... %! "Alpha", 0.999999999); %!error ... %! adtest (10); ## Test warnings %!warning ... %! randn ("seed", 34); %! adtest (ones (20,1), "Alpha", 0.000001); %!warning ... %! randn ("seed", 34); %! adtest (normrnd(0,1,100,1), "Alpha", 0.99999); %!warning ... %! randn ("seed", 34); %! adtest (normrnd(0,1,100,1), "Alpha", 0.00001); ## Test results %!test %! load examgrades %! x = grades(:,1); %! [h, pval, adstat, cv] = adtest (x); %! assert (h, false); %! assert (pval, 0.1854, 1e-4); %! assert (adstat, 0.5194, 1e-4); %! assert (cv, 0.7470, 1e-4); %!test %! load examgrades %! x = grades(:,1); %! [h, pval, adstat, cv] = adtest (x, "Distribution", "ev"); %! assert (h, false); %! assert (pval, 0.071363, 1e-6); %!test %! load examgrades %! x = grades(:,1); %! [h, pval, adstat, cv] = adtest (x, "Distribution", {"norm", 75, 10}); %! assert (h, false); %! assert (pval, 0.4687, 1e-4); statistics-release-1.7.3/inst/anova1.m000066400000000000000000000316151475240274700176740ustar00rootroot00000000000000## Copyright (C) 2021-2022 Andreas Bertsatos ## Copyright (C) 2022 Andrew Penn ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} anova1 (@var{x}) ## @deftypefnx {statistics} {@var{p} =} anova1 (@var{x}, @var{group}) ## @deftypefnx {statistics} {@var{p} =} anova1 (@var{x}, @var{group}, @var{displayopt}) ## @deftypefnx {statistics} {@var{p} =} anova1 (@var{x}, @var{group}, @var{displayopt}, @var{vartype}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}] =} anova1 (@var{x}, @dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}, @var{stats}] =} anova1 (@var{x}, @dots{}) ## ## Perform a one-way analysis of variance (ANOVA) for comparing the means of two ## or more groups of data under the null hypothesis that the groups are drawn ## from distributions with the same mean. For planned contrasts and/or ## diagnostic plots, use @qcode{anovan} instead. ## ## anova1 can take up to three input arguments: ## ## @itemize ## @item ## @var{x} contains the data and it can either be a vector or matrix. ## If @var{x} is a matrix, then each column is treated as a separate group. ## If @var{x} is a vector, then the @var{group} argument is mandatory. ## ## @item ## @var{group} contains the names for each group. If @var{x} is a matrix, then ## @var{group} can either be a cell array of strings of a character array, with ## one row per column of @var{x}. If you want to omit this argument, enter an ## empty array ([]). If @var{x} is a vector, then @var{group} must be a vector ## of the same length, or a string array or cell array of strings with one row ## for each element of @var{x}. @var{x} values corresponding to the same value ## of @var{group} are placed in the same group. ## ## @item ## @var{displayopt} is an optional parameter for displaying the groups contained ## in the data in a boxplot. If omitted, it is 'on' by default. If group names ## are defined in @var{group}, these are used to identify the groups in the ## boxplot. Use 'off' to omit displaying this figure. ## ## @item ## @var{vartype} is an optional parameter to used to indicate whether the ## groups can be assumed to come from populations with equal variance. When ## @qcode{vartype} is @qcode{"equal"} the variances are assumed to be equal ## (this is the default). When @qcode{vartype} is @qcode{"unequal"} the ## population variances are not assumed to be equal and Welch's ANOVA test is ## used instead. ## @end itemize ## ## anova1 can return up to three output arguments: ## ## @itemize ## @item ## @var{p} is the p-value of the null hypothesis that all group means are equal. ## ## @item ## @var{atab} is a cell array containing the results in a standard ANOVA table. ## ## @item ## @var{stats} is a structure containing statistics useful for performing ## a multiple comparison of means with the MULTCOMPARE function. ## @end itemize ## ## If anova1 is called without any output arguments, then it prints the results ## in a one-way ANOVA table to the standard output. It is also printed when ## @var{displayopt} is 'on'. ## ## ## Examples: ## ## @example ## x = meshgrid (1:6); ## x = x + normrnd (0, 1, 6, 6); ## anova1 (x, [], 'off'); ## [p, atab] = anova1(x); ## @end example ## ## ## @example ## x = ones (50, 4) .* [-2, 0, 1, 5]; ## x = x + normrnd (0, 2, 50, 4); ## groups = @{"A", "B", "C", "D"@}; ## anova1 (x, groups); ## @end example ## ## @seealso{anova2, anovan, multcompare} ## @end deftypefn function [p, anovatab, stats] = anova1 (x, group, displayopt, vartype) ## Check for valid number of input arguments if (nargin < 1 || nargin > 4) error ("anova1: invalid number of input arguments."); endif ## Add defaults if (nargin < 2) group = []; endif if (nargin < 3) displayopt = "on"; endif if (nargin < 4) vartype = "equal"; endif plotdata = ! (strcmp (displayopt, 'off')); ## Convert group to cell array from character array, make it a column if (! isempty (group) && ischar (group)) group = cellstr (group); endif if (size (group, 1) == 1) group = group'; endif ## If x is a matrix, convert it to column vector and create a ## corresponging column vector for groups if (length (x) < prod (size (x))) [n, m] = size (x); x = x(:); gi = reshape (repmat ((1:m), n, 1), n*m, 1); if (length (group) == 0) ## no group names are provided group = gi; elseif (size (group, 1) == m) ## group names exist and match columns group = group(gi,:); else error ("anova1: columns in X and GROUP length do not match."); endif endif ## Check that x and group are the same size if (! all (numel (x) == numel (group))) error ("anova1: GROUP must be a vector with the same number of rows as x."); endif ## Identify NaN values (if any) and remove them from X along with ## their corresponding values from group vector nonan = ! isnan (x); x = x(nonan); group = group(nonan, :); ## Convert group to indices and separate names [group_id, group_names] = grp2idx (group); group_id = group_id(:); named = 1; ## Center data to improve accuracy and keep uncentered data for ploting xorig = x; mu = mean(x); x = x - mu; xr = x; ## Get group size and mean for each group groups = size (group_names, 1); xs = zeros (1, groups); xm = xs; xv = xs; for j = 1:groups group_size = find (group_id == j); xs(j) = length (group_size); xm(j) = mean (xr(group_size)); xv(j) = var (xr(group_size), 0); endfor ## Calculate statistics lx = length (xr); ## Number of samples in groups gm = mean (xr); ## Grand mean of groups dfm = length (xm) - 1; ## degrees of freedom for model dfe = lx - dfm - 1; ## degrees of freedom for error SSM = xs .* (xm - gm) * (xm - gm)'; ## Sum of Squares for Model SST = (xr(:) - gm)' * (xr(:) - gm); ## Sum of Squares Total SSE = SST - SSM; ## Sum of Squares Error if (dfm > 0) MSM = SSM / dfm; ## Mean Square for Model else MSM = NaN; endif if (dfe > 0) MSE = SSE / dfe; ## Mean Square for Error else MSE = NaN; endif ## Calculate F statistic if (SSE != 0) ## Regular Matrix case. switch (lower (vartype)) case "equal" ## Assume equal variances (Fisher's One-way ANOVA) F = (SSM / dfm) / MSE; case "unequal" ## Accomodate for unequal variances (Welch's One-way ANOVA) ## Calculate the sampling variance for each group (i.e. the square of the SEM) sv = xv ./ xs; ## Calculate weights as the reciprocal of the sampling variance w = 1 ./ sv; ## Calculate the origin ori = sum (w .* xm) ./ sum (w); ## Calculate Welch's F statistic F = (groups - 1)^-1 * sum (w .* (xm - ori).^2) /... (1 + ((2 * (groups - 2)/(groups^2 - 1)) * ... sum ((1 - w / sum (w)).^2 .* (xs - 1).^-1))); ## Welch's test does not use a pooled error term MSE = NaN; ## Correct the error degrees of freedom dfe = (3 /(groups^2 - 1) * sum ((1 - w / sum (w)).^2 .* (xs-1).^-1))^-1; otherwise error ("anova1: invalid fourth (vartype) argument to anova1."); endswitch p = 1 - fcdf (F, dfm, dfe); ## Probability of F given equal means. elseif (SSM == 0) ## Constant Matrix case. F = 0; p = 1; else ## Perfect fit case. F = Inf; p = 0; end ## Create results table (if requested) if (nargout > 1) switch (lower (vartype)) case "equal" anovatab = {"Source", "SS", "df", "MS", "F", "Prob>F"; ... "Groups", SSM, dfm, MSM, F, p; ... "Error", SSE, dfe, MSE, "", ""; ... "Total", SST, dfm + dfe, "", "", ""}; case "unequal" anovatab = {"Source", "F", "df", "dfe", "F", "Prob>F"; ... "Groups", SSM, dfm, dfe, F, p}; endswitch endif ## Create stats structure (if requested) for MULTCOMPARE if (nargout > 2) if (length (group_names) > 0) stats.gnames = group_names; else stats.gnames = strjust (num2str ((1:length (xm))'), 'left'); end stats.n = xs; stats.source = 'anova1'; stats.vartype = vartype; stats.means = xm + mu; stats.vars = xv; stats.df = dfe; stats.s = sqrt (MSE); endif ## Print results table on screen if no output argument was requested if (nargout == 0 || plotdata) switch (lower (vartype)) case "equal" printf("\n ANOVA Table\n\n"); printf("Source SS df MS F Prob>F\n"); printf("------------------------------------------------------\n"); printf("Groups %10.4f %5.0f %10.4f %8.2f %9.4f\n", SSM, dfm, MSM, F, p); printf("Error %10.4f %5.0f %10.4f\n", SSE, dfe, MSE); printf("Total %10.4f %5.0f\n\n", SST, dfm + dfe); case "unequal" printf("\n Welch's ANOVA Table\n\n"); printf("Source F df dfe Prob>F\n"); printf("-----------------------------------------\n"); printf("Groups %8.2f %5.0f %7.2f %10.4f\n\n", F, dfm, dfe, p); endswitch endif ## Plot data using BOXPLOT (unless opted out) if (plotdata) boxplot (x, group_id, "Notch", "on", "Labels", group_names); endif endfunction %!demo %! x = meshgrid (1:6); %! randn ("seed", 15); # for reproducibility %! x = x + normrnd (0, 1, 6, 6); %! anova1 (x, [], 'off'); %!demo %! x = meshgrid (1:6); %! randn ("seed", 15); # for reproducibility %! x = x + normrnd (0, 1, 6, 6); %! [p, atab] = anova1(x); %!demo %! x = ones (50, 4) .* [-2, 0, 1, 5]; %! randn ("seed", 13); # for reproducibility %! x = x + normrnd (0, 2, 50, 4); %! groups = {"A", "B", "C", "D"}; %! anova1 (x, groups); %!demo %! y = [54 87 45; 23 98 39; 45 64 51; 54 77 49; 45 89 50; 47 NaN 55]; %! g = [1 2 3 ; 1 2 3 ; 1 2 3 ; 1 2 3 ; 1 2 3 ; 1 2 3 ]; %! anova1 (y(:), g(:), "on", "unequal"); ## testing against GEAR.DAT data file and results for one-factor ANOVA from ## https://www.itl.nist.gov/div898/handbook/eda/section3/eda354.htm %!test %! data = [1.006, 0.996, 0.998, 1.000, 0.992, 0.993, 1.002, 0.999, 0.994, 1.000, ... %! 0.998, 1.006, 1.000, 1.002, 0.997, 0.998, 0.996, 1.000, 1.006, 0.988, ... %! 0.991, 0.987, 0.997, 0.999, 0.995, 0.994, 1.000, 0.999, 0.996, 0.996, ... %! 1.005, 1.002, 0.994, 1.000, 0.995, 0.994, 0.998, 0.996, 1.002, 0.996, ... %! 0.998, 0.998, 0.982, 0.990, 1.002, 0.984, 0.996, 0.993, 0.980, 0.996, ... %! 1.009, 1.013, 1.009, 0.997, 0.988, 1.002, 0.995, 0.998, 0.981, 0.996, ... %! 0.990, 1.004, 0.996, 1.001, 0.998, 1.000, 1.018, 1.010, 0.996, 1.002, ... %! 0.998, 1.000, 1.006, 1.000, 1.002, 0.996, 0.998, 0.996, 1.002, 1.006, ... %! 1.002, 0.998, 0.996, 0.995, 0.996, 1.004, 1.004, 0.998, 0.999, 0.991, ... %! 0.991, 0.995, 0.984, 0.994, 0.997, 0.997, 0.991, 0.998, 1.004, 0.997]; %! group = [1:10] .* ones (10,10); %! group = group(:); %! [p, tbl] = anova1 (data, group, "off"); %! assert (p, 0.022661, 1e-6); %! assert (tbl{2,5}, 2.2969, 1e-4); %! assert (tbl{2,3}, 9, 0); %! assert (tbl{4,2}, 0.003903, 1e-6); %! data = reshape (data, 10, 10); %! [p, tbl, stats] = anova1 (data, [], "off"); %! assert (p, 0.022661, 1e-6); %! assert (tbl{2,5}, 2.2969, 1e-4); %! assert (tbl{2,3}, 9, 0); %! assert (tbl{4,2}, 0.003903, 1e-6); %! means = [0.998, 0.9991, 0.9954, 0.9982, 0.9919, 0.9988, 1.0015, 1.0004, 0.9983, 0.9948]; %! N = 10 * ones (1, 10); %! assert (stats.means, means, 1e-6); %! assert (length (stats.gnames), 10, 0); %! assert (stats.n, N, 0); ## testing against one-way ANOVA example dataset from GraphPad Prism 8 %!test %! y = [54 87 45; 23 98 39; 45 64 51; 54 77 49; 45 89 50; 47 NaN 55]; %! g = [1 2 3 ; 1 2 3 ; 1 2 3 ; 1 2 3 ; 1 2 3 ; 1 2 3 ]; %! [p, tbl] = anova1 (y(:), g(:), "off", "equal"); %! assert (p, 0.00004163, 1e-6); %! assert (tbl{2,5}, 22.573418, 1e-6); %! assert (tbl{2,3}, 2, 0); %! assert (tbl{3,3}, 14, 0); %! [p, tbl] = anova1 (y(:), g(:), "off", "unequal"); %! assert (p, 0.00208877, 1e-8); %! assert (tbl{2,5}, 15.523192, 1e-6); %! assert (tbl{2,3}, 2, 0); %! assert (tbl{2,4}, 7.5786897, 1e-6); statistics-release-1.7.3/inst/anova2.m000066400000000000000000000370561475240274700177020ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## Copyright (C) 2022 Andrew Penn ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} anova2 (@var{x}, @var{reps}) ## @deftypefnx {statistics} {@var{p} =} anova2 (@var{x}, @var{reps}, @var{displayopt}) ## @deftypefnx {statistics} {@var{p} =} anova2 (@var{x}, @var{reps}, @var{displayopt}, @var{model}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}] =} anova2 (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}, @var{stats}] =} anova2 (@dots{}) ## ## Performs two-way factorial (crossed) or a nested analysis of variance ## (ANOVA) for balanced designs. For unbalanced factorial designs, diagnostic ## plots and/or planned contrasts, use @qcode{anovan} instead. ## ## @qcode{anova2} requires two input arguments with an optional third and fourth: ## ## @itemize ## @item ## @var{x} contains the data and it must be a matrix of at least two columns and ## two rows. ## ## @item ## @var{reps} is the number of replicates for each combination of factor groups. ## ## @item ## @var{displayopt} is an optional parameter for displaying the ANOVA table, ## when it is 'on' (default) and suppressing the display when it is 'off'. ## ## @item ## @var{model} is an optional parameter to specify the model type as either: ## ## @itemize ## @item ## "interaction" or "full" (default): compute both main effects and their ## interaction ## ## @item ## "linear": compute both main effects without an interaction. When @var{reps} ## > 1 the test is suitable for a balanced randomized block design. When ## @var{reps} == 1, the test becomes a One-way Repeated Measures (RM)-ANOVA ## with Greenhouse-Geisser correction to the column factor degrees of freedom ## to make the test robust to violations of sphericity ## ## @item ## "nested": treat the row factor as nested within columns. Note that the row ## factor is considered a random factor in the calculation of the statistics. ## ## @end itemize ## @end itemize ## ## @qcode{anova2} returns up to three output arguments: ## ## @itemize ## @item ## @var{p} is the p-value of the null hypothesis that all group means are equal. ## ## @item ## @var{atab} is a cell array containing the results in a standard ANOVA table. ## ## @item ## @var{stats} is a structure containing statistics useful for performing ## a multiple comparison of means with the MULTCOMPARE function. ## @end itemize ## ## If anova2 is called without any output arguments, then it prints the results ## in a one-way ANOVA table to the standard output as if @var{displayopt} is ## 'on'. ## ## Examples: ## ## @example ## load popcorn; ## anova2 (popcorn, 3); ## @end example ## ## ## @example ## [p, anovatab, stats] = anova2 (popcorn, 3, "off"); ## disp (p); ## @end example ## ## @seealso{anova1, anovan, multcompare} ## @end deftypefn function [p, anovatab, stats] = anova2 (x, reps, displayopt, model) ## Check for valid number of input arguments if (nargin < 1 || nargin >4) error ("anova2: invalid number of input arguments."); endif ## Check for NaN values in X if (any (isnan( x(:)))) error ("anova2: NaN values in input are not allowed. Use anovan instead."); endif ## Add defaults if (nargin == 1) reps = 1; endif if (nargin < 3) displayopt = "on"; endif if (nargin < 4) model = "interaction"; endif epsilonhat = []; plotdata = ! (strcmp (displayopt, "off")); ## Calculate group numbers FFGn = size (x, 1) / reps; ## Number of groups in Row Factor SFGn = size (x, 2); ## Number of groups in Column Factor ## Check for valid repetitions if (! (int16 (FFGn) == FFGn)) error ("anova2: the number of rows in X must be a multiple of REPS."); else idx_s = 1; idx_e = reps; for i = 1:FFGn RIdx(i,:) = [idx_s:idx_e]; idx_s += reps; idx_e += reps; endfor endif ## Calculate group sample sizes GTsz = length (x(:)); ## Number of total samples FFGs = prod (size (x(RIdx(1,:),:))); ## Number of group samples of Row Factor SFGs = size (x, 1); ## Number of group samples of Column Factor ## Calculate group means GTmu = sum (x(:)) / GTsz; ## Grand mean of groups for i = 1:FFGn ## Group means of Row Factor FFGm(i) = mean (x(RIdx(i,:),:), "all"); endfor for i = 1:SFGn ## Group means of Column Factor SFGm(i) = mean (x(:,i)); endfor ## Calculate Sum of Squares for Row and Column Factors SSR = sum (FFGs * ((FFGm - GTmu) .^ 2)); ## Rows Sum of Squares SSC = sum (SFGs * ((SFGm - GTmu) .^ 2)); ## Columns Sum of Squares ## Calculate Total Sum of Squares SST = (x(:) - GTmu)' * (x(:) - GTmu); ## Calculate Sum of Squares Error (Within) if (reps > 1) SSE = 0; for i = 1:FFGn for j = 1:SFGn SSE += sum ((x(RIdx(i,:),j) - mean (x(RIdx(i,:),j))) .^ 2); endfor endfor else SSE = SST - SSC - SSR; endif ## Calculate degrees of freedom and Sum of Squares Interaction (if applicable) df_SSR = FFGn - 1; ## Row Factor df_SSC = SFGn - 1; ## Column Factor if (reps > 1) df_SSE = GTsz - (FFGn * SFGn); ## Error with replication df_SSI = df_SSR * df_SSC; ## Interaction: Degrees of Freedom SSI = SST - SSR - SSC - SSE; ## Interaction: Sum of Squares else df_SSE = df_SSR * df_SSC; ## No replication, assuming additive model df_SSI = 0; SSI = 0; endif df_tot = GTsz - 1; ## Total ## Model-specific calculations of sums-of-squares, mean squares and degrees of ## freedom. The calculations are based on equalities for the partitioning of ## variance in fully balanced designs. switch (lower (model)) case {"interaction", "full"} ## TWO-WAY ANOVA WITH INTERACTION (full factorial model) ## Sums--of-squares are already partitioned into main effects and ## interaction. Just calculate mean-squares and degrees of fredom model = "interaction"; MSE = SSE / df_SSE; ## Mean Square for Error (Within) MSR = SSR / df_SSR; ## Mean Square for Row Factor MS_DENOM = MSE; df_DENOM = df_SSE; case "linear" ## TWO-WAY ANOVA WITHOUT INTERACTION (additive, linear model) ## Pool Error and Interaction term model = "linear"; SSE += SSI; df_SSE += df_SSI; SSI = 0; df_SSI = 0; if (reps == 1) ## Assume one-way repeated measures ANOVA. Perform calculations for a ## correction factor (epsilonhat) to make tests of the Column factor ## robust to violations of sphericity vcov = cov (x); N = SFGn^2 * (mean (diag (vcov)) - mean (mean (vcov)))^2; D = (SFGn - 1) * ... (sum (sumsq (vcov)) - 2 * SFGn * sum ((mean (vcov, 2).^2)) + ... SFGn^2 * mean (mean (vcov))^2); epsilonhat = N / D; dfN_GG = epsilonhat * (SFGn - 1); dfD_GG = epsilonhat * (FFGn - 1) * (SFGn - 1); endif reps = 1; ## Set reps to 1 to avoid printing interaction MSE = SSE / df_SSE; ## Mean Square for Error (Within) MSR = SSR / df_SSR; ## Mean Square for Row Factor MS_DENOM = MSE; df_DENOM = df_SSE; case "nested" ## NESTED ANOVA ## Row Factor is nested within Column Factor. Treat Row factor as random. ## Pool Row Factor and Interaction term model = "nested"; SSR += SSI; df_SSR += df_SSI; SSI = 0; df_SSI = 0; reps = 1; ## Set reps to 1 to avoid printing interaction MSE = SSE / df_SSE; ## Mean Square for Error (Within) MSR = SSR / df_SSR; ## Mean Square for Row Factor MS_DENOM = MSR; ## Row factor is random so MSR is denominator df_DENOM = df_SSR; ## Row factor is random so df_SSR is denominator otherwise error ("anova2: model type not recognised"); endswitch ## Calculate F statistics and p values F_MSR = MSR / MSE; ## F statistic for Row Factor p_MSR = 1 - fcdf (F_MSR, df_SSR, df_SSE); MSC = SSC / df_SSC; ## Mean Square for Column Factor F_MSC = MSC / MS_DENOM; ## F statistic for Column Factor if (isempty(epsilonhat)) p_MSC = 1 - fcdf (F_MSC, df_SSC, df_DENOM); else ## Apply correction for sphericity to the p-value of the column factor p_MSC = 1 - fcdf (F_MSC, dfN_GG, dfD_GG); endif ## With replication if (reps > 1) MSI = SSI / df_SSI; ## Mean Square for Interaction F_MSI = MSI / MSE; ## F statistic for Interaction p_MSI = 1 - fcdf (F_MSI, df_SSI, df_SSE); else MSI = 0; F_MSI = 0; p_MSI = NaN; endif ## Create p output (if requested) if (nargout > 0) if (reps > 1) p = [p_MSC, p_MSR, p_MSI]; else p = [p_MSC, p_MSR]; endif endif ## Create results table (if requested) if (nargout > 1 && reps > 1) anovatab = {"Source", "SS", "df", "MS", "F", "Prob>F"; ... "Columns", SSC, df_SSC, MSC, F_MSC, p_MSC; ... "Rows", SSR, df_SSR, MSR, F_MSR, p_MSR; ... "Interaction", SSI, df_SSI, MSI, F_MSI, p_MSI; ... "Error", SSE, df_SSE, MSE, "", ""; ... "Total", SST, df_tot, "", "", ""}; elseif (nargout > 1 && reps == 1) anovatab = {"Source", "SS", "df", "MS", "F", "Prob>F"; ... "Columns", SSC, df_SSC, MSC, F_MSC, p_MSC; ... "Rows", SSR, df_SSR, MSR, F_MSR, p_MSR; ... "Error", SSE, df_SSE, MSE, "", ""; ... "Total", SST, df_tot, "", "", ""}; endif ## Create stats structure (if requested) for MULTCOMPARE if (nargout > 2) stats.source = "anova2"; stats.sigmasq = MS_DENOM; ## MS used to calculate F relating to stats.pval stats.colmeans = SFGm(:)'; stats.coln = SFGs; stats.rowmeans = FFGm(:)'; stats.rown = FFGs; stats.inter = (reps > 1); if stats.inter stats.pval = p_MSI; ## Interaction p-value if stats.inter is true else stats.pval = p_MSC; ## Column Factor p-value if stats.inter is false end stats.df = df_DENOM; ## Degrees of freedom used to calculate stats.pval stats.model = model; endif ## Print results table on screen if no output argument was requested if (nargout == 0 || plotdata) printf("\n ANOVA Table\n\n"); printf("Source SS df MS F Prob>F\n"); printf("-----------------------------------------------------------\n"); printf("Columns %10.4f %5.0f %10.4f %8.2f %9.4f\n", ... SSC, df_SSC, MSC, F_MSC, p_MSC); printf("Rows %10.4f %5.0f %10.4f %8.2f %9.4f\n", ... SSR, df_SSR, MSR, F_MSR, p_MSR); if (reps > 1) printf("Interaction %10.4f %5.0f %10.4f %8.2f %9.4f\n", ... SSI, df_SSI, MSI, F_MSI, p_MSI); endif printf("Error %10.4f %5.0f %10.4f\n", SSE, df_SSE, MSE); printf("Total %10.4f %5.0f\n\n", SST, df_tot); if (! isempty (epsilonhat)) printf (strcat (["Note: Greenhouse-Geisser's correction was applied to the\n"], ... ["degrees of freedom for the Column factor: F(%.2f,%.2f)\n\n"]),... dfN_GG, dfD_GG); endif if (strcmpi (model, "nested")) printf (strcat (["Note: Rows are a random factor nested within the columns.\n"], ... ["The Column F statistic uses the Row MS instead of the MSE.\n\n"])); endif endif endfunction %!demo %! %! # Factorial (Crossed) Two-way ANOVA with Interaction %! %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! %! [p, atab, stats] = anova2(popcorn, 3, "on"); %!demo %! %! # One-way Repeated Measures ANOVA (Rows are a crossed random factor) %! %! data = [54, 43, 78, 111; %! 23, 34, 37, 41; %! 45, 65, 99, 78; %! 31, 33, 36, 35; %! 15, 25, 30, 26]; %! %! [p, atab, stats] = anova2 (data, 1, "on", "linear"); %!demo %! %! # Balanced Nested One-way ANOVA (Rows are a nested random factor) %! %! data = [4.5924 7.3809 21.322; -0.5488 9.2085 25.0426; ... %! 6.1605 13.1147 22.66; 2.3374 15.2654 24.1283; ... %! 5.1873 12.4188 16.5927; 3.3579 14.3951 10.2129; ... %! 6.3092 8.5986 9.8934; 3.2831 3.4945 10.0203]; %! %! [p, atab, stats] = anova2 (data, 4, "on", "nested"); ## testing against popcorn data and results from Matlab %!test %! ## Test for anova2 ("interaction") %! ## comparison with results from Matlab for column effect %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! [p, atab, stats] = anova2 (popcorn, 3, "off"); %! assert (p(1), 7.678957383294716e-07, 1e-14); %! assert (p(2), 0.0001003738963050171, 1e-14); %! assert (p(3), 0.7462153966366274, 1e-14); %! assert (atab{2,5}, 56.700, 1e-14); %! assert (atab{2,3}, 2, 0); %! assert (atab{4,2}, 0.08333333333333348, 1e-14); %! assert (atab{5,4}, 0.1388888888888889, 1e-14); %! assert (atab{5,2}, 1.666666666666667, 1e-14); %! assert (atab{6,2}, 22); %! assert (stats.source, "anova2"); %! assert (stats.colmeans, [6.25, 4.75, 4]); %! assert (stats.inter, 1, 0); %! assert (stats.pval, 0.7462153966366274, 1e-14); %! assert (stats.df, 12); %!test %! ## Test for anova2 ("linear") - comparison with results from GraphPad Prism 8 %! data = [54, 43, 78, 111; %! 23, 34, 37, 41; %! 45, 65, 99, 78; %! 31, 33, 36, 35; %! 15, 25, 30, 26]; %! [p, atab, stats] = anova2 (data, 1, "off", "linear"); %! assert (atab{2,2}, 2174.95, 1e-10); %! assert (atab{3,2}, 8371.7, 1e-10); %! assert (atab{4,2}, 2404.3, 1e-10); %! assert (atab{5,2}, 12950.95, 1e-10); %! assert (atab{2,4}, 724.983333333333, 1e-10); %! assert (atab{3,4}, 2092.925, 1e-10); %! assert (atab{4,4}, 200.358333333333, 1e-10); %! assert (atab{2,5}, 3.61843363972882, 1e-10); %! assert (atab{3,5}, 10.445909412303, 1e-10); %! assert (atab{2,6}, 0.087266112738617, 1e-10); %! assert (atab{3,6}, 0.000698397753556, 1e-10); %!test %! ## Test for anova2 ("nested") - comparison with results from GraphPad Prism 8 %! data = [4.5924 7.3809 21.322; -0.5488 9.2085 25.0426; ... %! 6.1605 13.1147 22.66; 2.3374 15.2654 24.1283; ... %! 5.1873 12.4188 16.5927; 3.3579 14.3951 10.2129; ... %! 6.3092 8.5986 9.8934; 3.2831 3.4945 10.0203]; %! [p, atab, stats] = anova2 (data, 4, "off", "nested"); %! assert (atab{2,2}, 745.360306290833, 1e-10); %! assert (atab{3,2}, 278.01854140125, 1e-10); %! assert (atab{4,2}, 180.180377467501, 1e-10); %! assert (atab{5,2}, 1203.55922515958, 1e-10); %! assert (atab{2,4}, 372.680153145417, 1e-10); %! assert (atab{3,4}, 92.67284713375, 1e-10); %! assert (atab{4,4}, 10.0100209704167, 1e-10); %! assert (atab{2,5}, 4.02146005730833, 1e-10); %! assert (atab{3,5}, 9.25800729165627, 1e-10); %! assert (atab{2,6}, 0.141597630656771, 1e-10); %! assert (atab{3,6}, 0.000636643812875719, 1e-10); statistics-release-1.7.3/inst/anovan.m000066400000000000000000002225741475240274700177770ustar00rootroot00000000000000## Copyright (C) 2003-2005 Andy Adler ## Copyright (C) 2021 Christian Scholz ## Copyright (C) 2022 Andreas Bertsatos ## Copyright (C) 2022 Andrew Penn ## Copyright (C) 2024 Swayam Shah ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} anovan (@var{Y}, @var{GROUP}) ## @deftypefnx {statistics} {@var{p} =} anovan (@var{Y}, @var{GROUP}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}] =} anovan (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}, @var{stats}] =} anovan (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}, @var{stats}, @var{terms}] =} anovan (@dots{}) ## ## Perform a multi (N)-way analysis of (co)variance (ANOVA or ANCOVA) to ## evaluate the effect of one or more categorical or continuous predictors (i.e. ## independent variables) on a continuous outcome (i.e. dependent variable). The ## algorithms used make @code{anovan} suitable for balanced or unbalanced ## factorial (crossed) designs. By default, @code{anovan} treats all factors ## as fixed. Examples of function usage can be found by entering the command ## @code{demo anovan}. A bootstrap resampling variant of this function, ## @code{bootlm}, is available in the statistics-resampling package and has ## similar usage. ## ## Data is a single vector @var{Y} with groups specified by a corresponding ## matrix or cell array of group labels @var{GROUP}, where each column of ## @var{GROUP} has the same number of rows as @var{Y}. For example, if ## @code{@var{Y} = [23; 27; 31; 29; 30; 32]; @var{GROUP} = [1, 2; 1, 3; 1, 2; 2, 3; 2, 3; 3, 2];} ## then observation 23 was measured under conditions 1,2; observation 27 was ## measured under conditions 1,3; and so on. If the @var{GROUP} provided is empty, ## then the linear model is fit with just the intercept (no predictors). ## ## @code{anovan} can take a number of optional parameters as name-value pairs. ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "continuous", @var{continuous})} ## ## @itemize ## @item ## @var{continuous} is a vector of indices indicating which of the columns (i.e. ## factors) in @var{GROUP} should be treated as continuous predictors rather ## than as categorical predictors. The relationship between continuous ## predictors and the outcome should be linear. ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "random", @var{random})} ## ## @itemize ## @item ## @var{random} is a vector of indices indicating which of the columns (i.e. ## factors) in @var{GROUP} should be treated as random effects rather than ## fixed effects. Octave @code{anovan} provides only basic support for random ## effects. Specifically, since all F-statistics in @code{anovan} are ## calculated using the mean-squared error (MSE), any interaction terms ## containing a random effect are dropped from the model term definitions and ## their associated variance is pooled with the residual, unexplained variance ## making up the MSE. In effect, the model then fitted equates to a linear mixed ## model with random intercept(s). Variable names for random factors are ## appended with a ' symbol. ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "model", @var{modeltype})} ## ## @itemize ## @item ## @var{modeltype} can specified as one of the following: ## ## @itemize ## @item ## "linear" (default) : compute @math{N} main effects with no interactions. ## ## @item ## "interaction" : compute @math{N} effects and @math{N*(N-1)} two-factor ## interactions ## ## @item ## "full" : compute the @math{N} main effects and interactions at all levels ## ## @item ## a scalar integer : representing the maximum interaction order ## ## @item ## a matrix of term definitions : each row is a term and each column is a factor ## @end itemize ## ## @example ## -- Example: ## A two-way ANOVA with interaction would be: [1 0; 0 1; 1 1] ## @end example ## ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "sstype", @var{sstype})} ## ## @itemize ## @item ## @var{sstype} can specified as one of the following: ## ## @itemize ## @item ## 1 : Type I sequential sums-of-squares. ## ## @item ## 2 or "h" : Type II partially sequential (or hierarchical) sums-of-squares ## ## @item ## 3 (default) : Type III partial, constrained or marginal sums-of-squares ## ## @end itemize ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "varnames", @var{varnames})} ## ## @itemize ## @item ## @var{varnames} must be a cell array of strings with each element containing a ## factor name for each column of @var{GROUP}. By default (if not parsed as ## optional argument), @var{varnames} are "X1","X2","X3", etc. ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "alpha", @var{alpha})} ## ## @itemize ## @item ## @var{alpha} must be a scalar value between 0 and 1 requesting ## @math{100*(1-@var{alpha})%} confidence bounds for the regression coefficients ## returned in @var{stats}.coeffs (default 0.05 for 95% confidence). ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "display", @var{dispopt})} ## ## @itemize ## @item ## @var{dispopt} can be either "on" (default) or "off" and controls the display ## of the model formula, table of model parameters, the ANOVA table and the ## diagnostic plots. The F-statistic and p-values are formatted in APA-style. ## To avoid p-hacking, the table of model parameters is only displayed if we set ## planned contrasts (see below). ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "contrasts", @var{contrasts})} ## ## @itemize ## @item ## @var{contrasts} can be specified as one of the following: ## ## @itemize ## @item ## A string corresponding to one of the built-in contrasts listed below: ## ## @itemize ## @item ## "simple" or "anova" (default): Simple (ANOVA) contrast coding. (The first ## level appearing in the @var{GROUP} column is the reference level) ## ## @item ## "poly": Polynomial contrast coding for trend analysis. ## ## @item ## "helmert": Helmert contrast coding: the difference between each level with ## the mean of the subsequent levels. ## ## @item ## "effect": Deviation effect coding. (The first level appearing in the ## @var{GROUP} column is omitted). ## ## @item ## "sdif" or "sdiff": Successive differences contrast coding: the difference ## between each level with the previous level. ## ## @item ## "treatment": Treatment contrast (or dummy) coding. (The first level appearing ## in the @var{GROUP} column is the reference level). These contrasts are not ## compatible with @var{sstype} = 3. ## ## @end itemize ## ## @item ## A matrix containing a custom contrast coding scheme (i.e. the generalized ## inverse of contrast weights). Rows in the contrast matrices correspond to ## factor levels in the order that they first appear in the @var{GROUP} column. ## The matrix must contain the same number of columns as there are the number of ## factor levels minus one. ## @end itemize ## ## If the anovan model contains more than one factor and a built-in contrast ## coding scheme was specified, then those contrasts are applied to all factors. ## To specify different contrasts for different factors in the model, ## @var{contrasts} should be a cell array with the same number of cells as there ## are columns in @var{GROUP}. Each cell should define contrasts for the ## respective column in @var{GROUP} by one of the methods described above. If ## cells are left empty, then the default contrasts are applied. Contrasts for ## cells corresponding to continuous factors are ignored. ## @end itemize ## ## @code{[@dots{}] = anovan (@var{Y}, @var{GROUP}, "weights", @var{weights})} ## ## @itemize ## @item ## @var{weights} is an optional vector of weights to be used when fitting the ## linear model. Weighted least squares (WLS) is used with weights (that is, ## minimizing @code{sum (@var{weights} * @var{residuals} .^ 2))}; otherwise ## ordinary least squares (OLS) is used (default is empty for OLS). ## @end itemize ## ## @code{anovan} can return up to four output arguments: ## ## @code{@var{p} = anovan (@dots{})} returns a vector of p-values, one for each ## term. ## ## @code{[@var{p}, @var{atab}] = anovan (@dots{})} returns a cell array ## containing the ANOVA table. ## ## @code{[@var{p}, @var{atab}, @var{stats}] = anovan (@dots{})} returns a ## structure containing additional statistics, including degrees of freedom and ## effect sizes for each term in the linear model, the design matrix, the ## variance-covariance matrix, (weighted) model residuals, and the mean squared ## error. The columns of @var{stats}.coeffs (from left-to-right) report the ## model coefficients, standard errors, lower and upper @math{100*(1-alpha)%} ## confidence interval bounds, t-statistics, and p-values relating to the ## contrasts. The number appended to each term name in @var{stats}.coeffnames ## corresponds to the column number in the relevant contrast matrix for that ## factor. The @var{stats} structure can be used as input for @code{multcompare}. ## ## @code{[@var{p}, @var{atab}, @var{stats}, @var{terms}] = anovan (@dots{})} ## returns the model term definitions. ## ## @seealso{anova1, anova2, multcompare, fitlm} ## @end deftypefn function [P, T, STATS, TERMS] = anovan (Y, GROUP, varargin) if (nargin < 2) error (strcat (["anovan usage: ""anovan (Y, GROUP)""; "], ... [" atleast 2 input arguments required"])); endif ## Check supplied parameters if ((numel (varargin) / 2) != fix (numel (varargin) / 2)) error ("anovan: wrong number of arguments.") endif MODELTYPE = "linear"; DISPLAY = "on"; SSTYPE = 3; VARNAMES = []; CONTINUOUS = []; RANDOM = []; CONTRASTS = {}; ALPHA = 0.05; WEIGHTS = []; for idx = 3:2:nargin name = varargin{idx-2}; value = varargin{idx-1}; switch (lower (name)) case "model" MODELTYPE = value; case "continuous" CONTINUOUS = value; case "random" RANDOM = value; case "nested" error (strcat (["anovan: nested ANOVA is not supported. Please use"], ... [" anova2 for fully balanced nested ANOVA designs."])); case "sstype" SSTYPE = value; case "varnames" VARNAMES = value; case {"display","displayopt"} DISPLAY = value; case "contrasts" CONTRASTS = value; case "alpha" ALPHA = value; case "weights" WEIGHTS = value; otherwise error (sprintf ("anovan: parameter %s is not supported", name)); endswitch endfor ## Evaluate continuous input argument if (isnumeric (CONTINUOUS)) if (any (CONTINUOUS != abs (fix (CONTINUOUS)))) error (strcat (["anovan: the value provided for the CONTINUOUS"], ... [" parameter must be a positive integer"])); endif else error (strcat (["anovan: the value provided for the CONTINUOUS"], ... [" parameter must be numeric"])); endif ## Accomodate for different formats for GROUP ## GROUP can be a matrix of numeric identifiers of a cell arrays ## of strings or numeric idenitiers N = size (GROUP, 2); # number of anova "ways" n = numel (Y); # total number of observations if (prod (size (Y)) != n) error ("anovan: for ""anovan (Y, GROUP)"", Y must be a vector"); endif if (numel (unique (CONTINUOUS)) > N) error (strcat (["anovan: the number of factors assigned as continuous"], ... [" cannot exceed the number of factors in GROUP"])); endif if (any ((CONTINUOUS > N) || any (CONTINUOUS <= 0))) error (strcat (["anovan: one or more indices provided in the value"], ... [" for the continuous parameter are out of range"])); endif cont_vec = false (1, N); cont_vec(CONTINUOUS) = true; if (iscell (GROUP)) if (size (GROUP, 1) == 1) tmp = cell (n, N); for j = 1:N if (isnumeric (GROUP{j})) if (ismember (j, CONTINUOUS)) tmp(:,j) = num2cell (GROUP{j}); else tmp(:,j) = cellstr (num2str (GROUP{j})); endif else if (ismember (j, CONTINUOUS)) error ("anovan: continuous factors must be a numeric datatype"); endif tmp(:,j) = GROUP{j}; endif endfor GROUP = tmp; endif endif if (! isempty (GROUP)) if (size (GROUP,1) != n) error ("anovan: GROUP must be a matrix with the same number of rows as Y"); endif endif if (! isempty (VARNAMES)) if (iscell (VARNAMES)) if (all (cellfun (@ischar, VARNAMES))) nvarnames = numel(VARNAMES); else error (strcat (["anovan: all variable names must be character"], ... [" or character arrays"])); endif elseif (ischar (VARNAMES)) nvarnames = 1; VARNAMES = {VARNAMES}; elseif (isstring (VARNAMES)) nvarnames = 1; VARNAMES = {char(VARNAMES)}; else error (strcat (["anovan: varnames is not of a valid type. Must be a cell"], ... [" array of character arrays, character array or string"])); endif else nvarnames = N; VARNAMES = arrayfun(@(x) ["X",num2str(x)], 1:N, "UniformOutput", 0); endif if (nvarnames != N) error (strcat (["anovan: number of variable names is not equal"], ... [" to the number of grouping variables"])); endif ## Evaluate random argument (if applicable) if (! isempty(RANDOM)) if (isnumeric (RANDOM)) if (any (RANDOM != abs (fix (RANDOM)))) error (strcat (["anovan: the value provided for the RANDOM"], ... [" parameter must be a positive integer"])); endif else error (strcat (["anovan: the value provided for the RANDOM"], ... [" parameter must be numeric"])); endif if (numel (RANDOM) > N) error (strcat (["anovan: the number of elements in RANDOM cannot"], ... [" exceed the number of columns in GROUP."])); endif if (max (RANDOM) > N) error (strcat (["anovan: the indices listed in RANDOM cannot"], ... [" exceed the number of columns in GROUP."])); endif for v = 1:N if (ismember (v, RANDOM)) VARNAMES{v} = strcat (VARNAMES{v},"'"); endif endfor endif ## Evaluate contrasts (if applicable) if isempty (CONTRASTS) CONTRASTS = cell (1, N); planned = false; else if (ischar(CONTRASTS)) contr_str = CONTRASTS; CONTRASTS = cell (1, N); CONTRASTS(:) = {contr_str}; endif if (! iscell (CONTRASTS)) CONTRASTS = {CONTRASTS}; endif for i = 1:N if (! isempty (CONTRASTS{i})) msg = strcat(["columns in CONTRASTS must sum to"], ... [" 0 for SSTYPE 3. Switching to SSTYPE 2 instead."]); if (isnumeric(CONTRASTS{i})) ## Check whether all the columns sum to 0 if (any (abs (sum (CONTRASTS{i})) > eps ('single'))) warning (sprintf ( ... 'Note that the CONTRASTS for predictor %u do not sum to zero', i)); endif ## Check whether contrasts are orthogonal if (any (abs (reshape (corr (CONTRASTS{i}) - ... eye (size (CONTRASTS{i}, 2)), [], 1))... > eps ('single'))) warning (sprintf ( ... 'Note that the CONTRASTS for predictor %u are not orthogonal', i)); endif else if (! ismember (lower (CONTRASTS{i}), ... {"simple","anova","poly","helmert","effect",... "sdif","sdiff","treatment"})) error (strcat(["anovan: valid built-in contrasts are:"], ... [" ""simple"", ""poly"", ""helmert"","],... ["""effect"", ""sdif"" or ""treatment"""])); endif if (strcmpi (CONTRASTS{i}, "treatment") && (SSTYPE==3)) warning (msg); SSTYPE = 2; endif endif endif endfor planned = true; endif ## Evaluate alpha input argument if (! isa (ALPHA,'numeric') || numel (ALPHA) != 1) error("anovan: alpha must be a numeric scalar value"); endif if ((ALPHA <= 0) || (ALPHA >= 1)) error("anovan: alpha must be a value between 0 and 1"); endif ## Remove NaN or non-finite observations if (isempty (GROUP)) excl = any ([isnan(Y), isinf(Y)], 2); else XC = GROUP(:,CONTINUOUS); if iscell(XC) XC = cell2mat (XC); endif excl = any ([isnan(Y), isinf(Y), any(isnan(XC),2), any(isinf(XC),2)], 2); GROUP(excl,:) = []; endif Y(excl) = []; if (size (Y, 1) == 1) Y = Y.'; # if Y is a row vector, make it a column vector endif n = numel (Y); # recalculate total number of observations ## Evaluate weights input argument if (! isempty(WEIGHTS)) if (! isnumeric(WEIGHTS)) error ("anovan: WEIGHTS must be a numeric datatype"); endif if (any (size (WEIGHTS) != [n,1])) error ("anovan: WEIGHTS must be a vector with the same dimensions as Y"); endif if (any(!(WEIGHTS > 0)) || any (isinf (WEIGHTS))) error ("anovan: WEIGHTS must be a vector of positive finite values"); endif # Create diaganal matrix of normalized weights W = diag (WEIGHTS / mean (WEIGHTS)); else # Create identity matrix W = eye (n);; endif ## Evaluate model type input argument and create terms matrix if not provided msg = strcat (["anovan: the number of columns in the term definitions"], ... [" cannot exceed the number of columns of GROUP"]); if (ischar (MODELTYPE)) switch (lower (MODELTYPE)) case "linear" MODELTYPE = 1; case {"interaction","interactions"} MODELTYPE = 2; case "full" MODELTYPE = N; otherwise error ("anovan: model type not recognised"); endswitch endif if (isscalar (MODELTYPE)) TERMS = cell (MODELTYPE,1); v = false (1, N); switch (lower (MODELTYPE)) case 1 ## Create term definitions for an additive linear model TERMS = eye (N); case 2 ## Create term definitions for a model with two factor interactions if (N > 1) Nx = nchoosek (N, 2); else Nx = 0; endif TERMS = zeros (N + Nx, N); TERMS(1:N,:) = eye (N); cnt = N + 1; for j = 1:N for i = j:N-1 TERMS(cnt,j) = 1; TERMS(cnt,i+1) = 1; cnt++; endfor endfor otherwise if (MODELTYPE > N) error (msg); endif ## Create term definitions for a full model Nx = zeros (1, N-1); Nx = 0; for k = 1:N Nx = Nx + nchoosek(N,k); endfor for j = 1:MODELTYPE v(1:j) = 1; TERMS(j) = flipud (unique (perms (v), "rows")); endfor TERMS = cell2mat (TERMS); endswitch TERMS = logical (TERMS); else ## Assume that the user provided a suitable matrix of term definitions if (size (MODELTYPE, 2) > N) error (msg); endif if (! all (ismember (MODELTYPE(:), [0,1]))) error (strcat (["anovan: elements of the model terms matrix"], ... [" must be either 0 or 1"])); endif TERMS = logical (MODELTYPE); endif ## Evaluate terms matrix Ng = sum (TERMS, 2); if (any (diff (Ng) < 0)) error (strcat (["anovan: the model terms matrix must list main"], ... [" effects above/before interactions"])); endif ## Drop terms that include interactions with factors specified as random effects. drop = any (bsxfun (@and, TERMS(:,RANDOM), (Ng > 1)), 2); TERMS (drop, :) = []; Ng(drop) = []; ## Evaluate terms Nm = sum (Ng == 1); Nx = sum (Ng > 1); Nt = Nm + Nx; if (any (any (TERMS(1:Nm,:), 1) != any (TERMS, 1))) error (strcat (["anovan: all factors involved in interactions"], ... [" must have a main effect"])); endif ## Calculate total sum-of-squares ct = sum (Y)^2 / n; % correction term sst = sum (Y.^2) - ct; dft = n - 1; ## Create design matrix mDesignMatrix (); ## Fit linear models, and calculate sums-of-squares for ANOVA switch (lower (SSTYPE)) case 1 ## Type I sequential sums-of-squares (SSTYPE = 1) R = sst; ss = zeros (Nt,1); for j = 1:Nt XS = cell2mat (X(1:j+1)); [b, sse] = lmfit (XS, Y, W); ss(j) = R - sse; R = sse; endfor [b, sse, resid, ucov, hat] = lmfit (XS, Y, W); sstype_char = "I"; case {2,'h'} ## Type II (partially sequential, or hierarchical) sums-of-squares ss = zeros (Nt,1); for j = 1:Nt i = find (TERMS(j,:)); k = cat (1, 1, 1 + find (any (!TERMS(:,i),2))); XS = cell2mat (X(k)); [jnk, R1] = lmfit (XS, Y, W); k = cat (1, j+1, k); XS = cell2mat (X(k)); [jnk, R2] = lmfit (XS, Y, W); ss(j) = R1 - R2; endfor [b, sse, resid, ucov, hat] = lmfit (cell2mat (X), Y, W); sstype_char = "II"; case 3 ## Type III (partial, constrained or marginal) sums-of-squares ss = zeros (Nt, 1); [b, sse, resid, ucov, hat] = lmfit (cell2mat (X), Y, W); for j = 1:Nt XS = cell2mat (X(1:Nt+1 != j+1)); [jnk, R] = lmfit (XS, Y, W); ss(j) = R - sse; endfor sstype_char = "III"; otherwise error ("anovan: sstype value not supported"); endswitch ss = max (0, ss); # Truncate negative SS at 0 dfe = dft - sum (df); ms = ss ./ df; mse = sse / dfe; eta_sq = ss ./ sst; partial_eta_sq = ss ./ (ss + sse); F = ms / mse; P = 1 - fcdf (F, df, dfe); ## Prepare model formula and cell array containing the ANOVA table T = cell (Nt + 3, 7); T(1,:) = {"Source", "Sum Sq.", "d.f.", "Mean Sq.", "Eta Sq.", "F", "Prob>F"}; T(2:Nt+1,2:7) = num2cell ([ss df ms partial_eta_sq F P]); T(end-1,1:4) = {"Error", sse, dfe, mse}; T(end,1:3) = {"Total", sst, dft}; formula = sprintf ("Y ~ 1"); # Initialise model formula for i = 1:Nt str = sprintf ("%s*", VARNAMES{find (TERMS(i,:))}); T(i+1,1) = str(1:end-1); ## Append model term to formula str = regexprep (str, "\\*", ":"); if (strcmp (str(end-1), "'")) ## Random intercept term formula = sprintf ("%s + (1|%s)", formula, str(1:end-2)); ## Remove statistics for random factors from the ANOVA table #T(RANDOM+1,4:7) = cell(1,4); #P(RANDOM) = NaN; else ## Fixed effect term formula = sprintf ("%s + %s", formula, str(1:end-1)); endif endfor ## Calculate a standard error, t-statistic and p-value for each ## of the regression coefficients (fixed effects only) t_crit = tinv (1 - ALPHA / 2, dfe); se = sqrt (diag (ucov) * mse); t = b ./ se; p = 2 * (1 - (tcdf (abs (t), dfe))); coeff_stats = zeros (1 + sum (df), 4); coeff_stats(:,1) = b; # coefficients coeff_stats(:,2) = se; # standard errors coeff_stats(:,3) = b - se * t_crit; # Lower CI bound coeff_stats(:,4) = b + se * t_crit; # Upper CI bound coeff_stats(:,5) = t; # t-statistics coeff_stats(:,6) = p; # p-values ## Assign NaN to p-value to avoid printing statistics relating to ## coefficients for 'random' effects hi = 1 + cumsum(df); for ignore = RANDOM p(hi(ignore)-df(ignore)+1:hi(ignore)) = NaN; endfor ## Compute leverage values and Cook's distance h = diag (hat); % Leverage values D = resid.^2 / ((1 + sum (df)) * mse) ... .* h ./ (1 - h).^2; % Cook's distance ## Create STATS structure for MULTCOMPARE STATS = struct ("source","anovan", ... "resid", resid, ... # These are weighted (not raw) residuals "coeffs", coeff_stats, ... "Rtr", [], ... # Not used by Octave "rowbasis", [], ... # Not used by Octave "dfe", dfe, ... "mse", mse, ... "nullproject", [], ... # Not used by Octave "terms", TERMS, ... "nlevels", nlevels, ... "continuous", cont_vec, ... "vmeans", vmeans, ... "termcols", termcols, ... "coeffnames", {cellstr(char(coeffnames{:}))}, ... "vars", [], ... # Not used by Octave "varnames", {VARNAMES}, ... "grpnames", {levels}, ... "vnested", [], ... # Not used since "nested" argument name is not supported "ems", [], ... # Not used since "nested" argument name is not supported "denom", [], ... # Not used since interactions with random factors is not supported "dfdenom", [], ... # Not used since interactions with random factors is not supported "msdenom", [], ... # Not used since interactions with random factors is not supported "varest", [], ... # Not used since interactions with random factors is not supported "varci", [], ... # Not used since interactions with random factors is not supported "txtdenom", [], ... # Not used since interactions with random factors is not supported "txtems", [], ... # Not used since interactions with random factors is not supported "rtnames", [], ... # Not used since interactions with random factors is not supported ## Additional STATS fields used exclusively by Octave "center_continuous", center_continuous, ... "random", RANDOM, ... "formula", formula, ... "alpha", ALPHA, ... "df", df, ... "contrasts", {CONTRASTS}, ... "X", sparse (cell2mat (X)), ... "Y", Y, ... "W", sparse (W), ... "lmfit", @lmfit, ... "vcov", sparse (ucov * mse), ... "CooksD", D, ... "grps", gid, ... "eta_squared", eta_sq, ... "partial_eta_squared", partial_eta_sq); ## Print ANOVA table switch (lower (DISPLAY)) case {"on", true} ## Print model formula fprintf("\nMODEL FORMULA (based on Wilkinson's notation):\n\n%s\n", formula); ## If applicable, print parameter estimates (a.k.a contrasts) for fixed effects if (planned && ! isempty(GROUP)) ## Parameter estimates correspond to the contrasts we set. To avoid ## p-hacking, don't print contrasts if we don't specify them to start with fprintf("\nMODEL PARAMETERS (contrasts for the fixed effects)\n\n"); fprintf("Parameter Estimate SE Lower.CI Upper.CI t Prob>|t|\n"); fprintf("--------------------------------------------------------------------------------\n"); for j = 1:size (coeff_stats, 1) if (p(j) < 0.001) fprintf ("%-20s %10.3g %9.3g %9.3g %9.3g %8.2f <.001 \n", ... STATS.coeffnames{j}, STATS.coeffs(j,1:end-1)); elseif (p(j) < 0.9995) fprintf ("%-20s %10.3g %9.3g %9.3g %9.3g %8.2f .%03u \n", ... STATS.coeffnames{j}, STATS.coeffs(j,1:end-1), round (p(j) * 1e+03)); elseif (isnan(p(j))) ## Don't display coefficients for 'random' effects since they were ## treated as fixed effects else fprintf ("%-20s %10.3g %9.3g %9.3g %9.3g %8.2f 1.000 \n", ... STATS.coeffnames{j}, STATS.coeffs(j,1:end-1)); endif endfor endif ## Print ANOVA table [nrows, ncols] = size (T); fprintf("\nANOVA TABLE (Type %s sums-of-squares):\n\n", sstype_char); fprintf("Source Sum Sq. d.f. Mean Sq. R Sq. F Prob>F\n"); fprintf("--------------------------------------------------------------------------------\n"); for i = 1:Nt str = T{i+1,1}; l = numel(str); # Needed to truncate source term name at 18 characters ## Format and print the statistics for each model term ## Format F statistics and p-values in APA style if (P(i) < 0.001) fprintf ("%-20s %10.5g %6d %10.5g %4.3f %11.2f <.001 \n", ... str(1:min(18,l)), T{i+1,2:end-1}); elseif (P(i) < 0.9995) fprintf ("%-20s %10.5g %6d %10.5g %4.3f %11.2f .%03u \n", ... str(1:min(18,l)), T{i+1,2:end-1}, round (P(i) * 1e+03)); elseif (isnan(P(i))) fprintf ("%-20s %10.5g %6d \n", str(1:min(18,l)), T{i+1,2:3}); else fprintf ("%-20s %10.5g %6d %10.5g %4.3f %11.2f 1.000 \n", ... str(1:min(18,l)), T{i+1,2:end-1}); endif endfor fprintf("Error %10.5g %6d %10.5g\n", T{end-1,2:4}); fprintf("Total %10.5g %6d \n", T{end,2:3}); fprintf("\n"); ## Make figure of diagnostic plots figure ("Name", "Diagnostic Plots: Model Residuals"); t = STATS.resid ./ (sqrt (mse * (1 - h))); % Studentized residuals fit = STATS.X * STATS.coeffs(:,1); % Fitted values [jnk, DI] = sort (D, "descend"); % Indices of sorted D nk = 4; % Top nk residuals with largest D ## Normal quantile-quantile plot subplot (2, 2, 1); x = ((1 : n)' - .5) / n; [ts, I] = sort (t); q = norminv (x); plot (q, ts, "ok", "markersize", 3); box off; grid on; xlabel ("Theoretical quantiles"); ylabel ("Studentized Residuals"); title ("Normal Q-Q Plot"); arrayfun (@(i) text (q(I == DI(i)), t(DI(i)), ... sprintf (" %u", DI(i))), [1:min(nk,n)]) iqr = [0.25; 0.75]; yl = quantile (t, iqr, 1, 6); xl = norminv (iqr); slope = diff (yl) / diff (xl); int = yl(1) - slope * xl(1); ax1_xlim = get (gca, "XLim"); hold on; plot (ax1_xlim, slope * ax1_xlim + int, "k-"); hold off; set (gca, "Xlim", ax1_xlim); ## Spread-Location Plot subplot (2, 2, 2); plot (fit, sqrt (abs (t)), "ko", "markersize", 3); box off; xlabel ("Fitted values"); ylabel ("sqrt ( | Studentized Residuals | )"); title ("Spread-Location Plot") ax2_xlim = get (gca, "XLim"); hold on; plot (ax2_xlim, ones (1, 2) * sqrt (2), "k:"); plot (ax2_xlim, ones (1, 2) * sqrt (3), "k-."); plot (ax2_xlim, ones (1, 2) * sqrt (4), "k--"); hold off; arrayfun (@(i) text (fit(DI(i)), sqrt (abs (t(DI(i)))), ... sprintf (" %u", DI(i))), [1:min(nk,n)]); xlim (ax2_xlim); ## Residual-Leverage plot subplot (2, 2, 3); plot (h, t, "ko", "markersize", 3); box off; xlabel ("Leverage") ylabel ("Studentized Residuals"); title ("Residual-Leverage Plot") ax3_xlim = get (gca, "XLim"); ax3_ylim = get (gca, "YLim"); hold on; plot (ax3_xlim, zeros (1, 2), "k-"); hold off; arrayfun (@(i) text (h(DI(i)), t(DI(i)), ... sprintf (" %u", DI(i))), [1:min(nk,n)]); set (gca, "ygrid", "on"); xlim (ax3_xlim); ylim (ax3_ylim); ## Cook's distance stem plot subplot (2, 2, 4); stem (D, "ko", "markersize", 3); box off; xlabel ("Obs. number") ylabel ("Cook's distance") title ("Cook's Distance Stem Plot") xlim ([0, n]); ax4_xlim = get (gca, "XLim"); ax4_ylim = get (gca, "YLim"); hold on; plot (ax4_xlim, ones (1, 2) * 4 / dfe, "k:"); plot (ax4_xlim, ones (1, 2) * 0.5, "k-."); plot (ax4_xlim, ones (1, 2), "k--"); hold off; arrayfun (@(i) text (DI(i), D(DI(i)), ... sprintf (" %u", DI(i))), [1:min(nk,n)]); xlim (ax4_xlim); ylim (ax4_ylim); set (findall ( gcf, "-property", "FontSize"), "FontSize", 7) case {"off", false} ## do nothing otherwise error ("anovan: wrong value for 'display' parameter."); endswitch function mDesignMatrix () ## Nested function that returns a cell array of the design matrix for ## each term in the model ## Input variables it uses: ## GROUP, TERMS, CONTINUOUS, CONTRASTS, VARNAMES, n, Nm, Nx, Ng ## Variables it creates or modifies: ## X, grpnames, nlevels, df, termcols, coeffnames, vmeans, gid, CONTRASTS ## EVALUATE FACTOR LEVELS levels = cell (Nm, 1); gid = zeros (n, Nm); nlevels = zeros (Nm, 1); df = zeros (Nm + Nx, 1); termcols = ones (1 + Nm + Nx, 1); for j = 1:Nm if (any (j == CONTINUOUS)) ## CONTINUOUS PREDICTOR nlevels(j) = 1; termcols(j+1) = 1; df(j) = 1; if iscell (GROUP(:,j)) gid(:,j) = cell2mat ([GROUP(:,j)]); else gid(:,j) = GROUP(:,j); end else ## CATEGORICAL PREDICTOR levels{j} = unique (GROUP(:,j), "stable"); if isnumeric (levels{j}) levels{j} = num2cell (levels{j}); endif nlevels(j) = numel (levels{j}); for k = 1:nlevels(j) gid(ismember (GROUP(:,j),levels{j}{k}),j) = k; endfor termcols(j+1) = nlevels(j); df(j) = nlevels(j) - 1; endif endfor ## MAKE DESIGN MATRIX ## MAIN EFFECTS X = cell (1, 1 + Nm + Nx); X(1) = ones (n, 1); coeffnames = cell (1, 1 + Nm + Nx); coeffnames(1) = "(Intercept)"; vmeans = zeros (Nm, 1); center_continuous = cont_vec; for j = 1:Nm if (any (j == CONTINUOUS)) ## CONTINUOUS PREDICTOR if iscell (GROUP(:,j)) X(1+j) = cell2mat (GROUP(:,j)); else X(1+j) = GROUP(:,j); end if (strcmpi (CONTRASTS{j}, 'treatment')) ## Don't center continuous variables if contrasts are 'treatment' center_continuous(j) = false; CONTRASTS{j} = []; else center_continuous(j) = true; vmeans(j) = mean ([X{1+j}]); X(1+j) = [X{1+j}] - vmeans(j); endif ## Create names of the coefficients relating to continuous main effects coeffnames{1+j} = VARNAMES{j}; else ## CATEGORICAL PREDICTOR if (isempty (CONTRASTS{j})) CONTRASTS{j} = contr_simple (nlevels(j)); else switch (lower (CONTRASTS{j})) case {"simple","anova"} ## SIMPLE EFFECT CODING (DEFAULT) ## The first level is the reference level CONTRASTS{j} = contr_simple (nlevels(j)); case "poly" ## POLYNOMIAL CONTRAST CODING CONTRASTS{j} = contr_poly (nlevels(j)); case "helmert" ## HELMERT CONTRAST CODING CONTRASTS{j} = contr_helmert (nlevels(j)); case "effect" ## DEVIATION EFFECT CONTRAST CODING CONTRASTS{j} = contr_sum (nlevels(j)); case {"sdif","sdiff"} ## SUCCESSIVE DEVIATIONS CONTRAST CODING CONTRASTS{j} = contr_sdif (nlevels(j)); case "treatment" ## The first level is the reference level CONTRASTS{j} = contr_treatment (nlevels(j)); otherwise ## EVALUATE CUSTOM CONTRAST MATRIX ## Check that the contrast matrix provided is the correct size if (! all (size (CONTRASTS{j},1) == nlevels(j))) error (strcat (["anovan: the number of rows in the contrast"], ... [" matrices should equal the number of factor levels"])); endif if (! all (size (CONTRASTS{j},2) == df(j))) error (strcat (["anovan: the number of columns in each contrast"], ... [" matrix should equal the degrees of freedom (i.e."], ... [" number of levels minus 1) for that factor"])); endif if (! all (any (CONTRASTS{j}))) error (strcat (["anovan: a contrast must be coded in each"], ... [" column of the contrast matrices"])); endif endswitch endif C = CONTRASTS{j}; func = @(x) x(gid(:,j)); X(1+j) = cell2mat (cellfun (func, num2cell (C, 1), "UniformOutput", false)); ## Create names of the coefficients relating to continuous main effects coeffnames{1+j} = cell (df(j),1); for v = 1:df(j) coeffnames{1+j}{v} = sprintf ("%s_%u", VARNAMES{j}, v); endfor endif endfor ## INTERACTION TERMS if (Nx > 0) row = TERMS((Ng > 1),:); for i = 1:Nx I = 1 + find (row(i,:)); df(Nm+i) = prod (df(I-1)); termcols(1+Nm+i) = prod (df(I-1) + 1); tmp = ones (n,1); for j = 1:numel(I); tmp = num2cell (tmp, 1); for k = 1:numel(tmp) tmp(k) = bsxfun (@times, tmp{k}, X{I(j)}); endfor tmp = cell2mat (tmp); endfor X{1+Nm+i} = tmp; coeffnames{1+Nm+i} = cell (df(Nm+i),1); for v = 1:df(Nm+i) str = sprintf ("%s:", VARNAMES{I-1}); coeffnames{1+Nm+i}{v} = strcat (str(1:end-1), "_", num2str (v)); endfor endfor endif endfunction endfunction ## BUILT IN CONTRAST CODING FUNCTIONS function C = contr_simple (N) ## Create contrast matrix (of doubles) using simple (ANOVA) contrast coding ## These contrasts are centered (i.e. sum to 0) ## Ideal for unordered factors, with comparison to a reference level ## The first factor level is the reference level C = cat (1, zeros (1,N-1), eye(N-1)) - 1/N; endfunction function C = contr_poly (N) ## Create contrast matrix (of doubles) using polynomial contrast coding ## for trend analysis of ordered categorical factor levels ## These contrasts are orthogonal and centered (i.e. sum to 0) ## Ideal for ordered factors [C, jnk] = qr (bsxfun (@power, [1:N]' - mean ([1:N]'), [0:N-1])); C(:,1) = []; s = ones (1, N-1); s(1:2:N-1) *= -1; f = (sign(C(1,:)) != s); C(:,f) *= -1; endfunction function C = contr_helmert (N) ## Create contrast matrix (of doubles) using Helmert coding contrasts ## These contrasts are orthogonal and centered (i.e. sum to 0) C = cat (1, tril (-ones (N-1), -1) + diag (N-1:-1:1), ... -ones (1, N-1)) ./ (N:-1:2); endfunction function C = contr_sum (N) ## Create contrast matrix (of doubles) using deviation effect coding ## These contrasts are centered (i.e. sum to 0) C = cat (1, - (ones (1,N-1)), eye (N-1)); endfunction function C = contr_sdif (N) ## Create contrast matrix (of doubles) using successive differences coding ## These contrasts are centered (i.e. sum to 0) C = tril (ones (N, N - 1), -1) - ones (N, 1) / N * [N - 1 : -1 : 1]; endfunction function C = contr_treatment (N) ## Create contrast matrix (of doubles) using treatment contrast coding ## Not compatible with SSTYPE 3 since contrasts are not centered ## Ideal for unordered factors, with comparison to a reference level ## The first factor level is the reference level C = cat (1, zeros (1,N-1), eye(N-1)); endfunction ## FUNCTION TO FIT THE LINEAR MODEL function [b, sse, resid, ucov, hat] = lmfit (X, Y, W) ## Get model coefficients by solving the linear equation by QR decomposition ## The number of free parameters (i.e. intercept + coefficients) is equal ## to n - dfe. If optional arument W is provided, it should be a diagonal ## matrix of weights or a positive definite covariance matrix if (nargin < 3) ## If no weights are provided, create an identity matrix n = numel (Y); W = eye (n); endif C = chol (W); XW = C*X; YW = C*Y; [Q, R] = qr (XW, 0); b = R \ Q' * YW; ## Get fitted values fit = Q'\R * b; # This is equivalent to fit = XW * b; ## Get residuals from the fit resid = YW - fit; ## Calculate the residual sums-of-squares sse = sum (resid.^2); ## Calculate the unscaled covariance matrix (i.e. inv (X'*X )) if (nargout > 3) ucov = R \ Q' / XW'; endif ## Calculate the Hat matrix if (nargout > 4) w = diag (W); rw = sqrt (w); Q1 = diag (1 ./ rw) * Q; Q2 = diag (rw) * Q; hat = Q1 * Q2'; endif endfunction %!demo %! %! # Two-sample unpaired test on independent samples (equivalent to Student's %! # t-test). Note that the absolute value of t-statistic can be obtained by %! # taking the square root of the reported F statistic. In this example, %! # t = sqrt (1.44) = 1.20. %! %! score = [54 23 45 54 45 43 34 65 77 46 65]'; %! gender = {"male" "male" "male" "male" "male" "female" "female" "female" ... %! "female" "female" "female"}'; %! %! [P, ATAB, STATS] = anovan (score, gender, "display", "on", "varnames", "gender"); %!demo %! %! # Two-sample paired test on dependent or matched samples equivalent to a %! # paired t-test. As for the first example, the t-statistic can be obtained by %! # taking the square root of the reported F statistic. Note that the interaction %! # between treatment x subject was dropped from the full model by assigning %! # subject as a random factor ('). %! %! score = [4.5 5.6; 3.7 6.4; 5.3 6.4; 5.4 6.0; 3.9 5.7]'; %! treatment = {"before" "after"; "before" "after"; "before" "after"; %! "before" "after"; "before" "after"}'; %! subject = {"GS" "GS"; "JM" "JM"; "HM" "HM"; "JW" "JW"; "PS" "PS"}'; %! %! [P, ATAB, STATS] = anovan (score(:), {treatment(:), subject(:)}, ... %! "model", "full", "random", 2, "sstype", 2, ... %! "varnames", {"treatment", "subject"}, ... %! "display", "on"); %!demo %! %! # One-way ANOVA on the data from a study on the strength of structural beams, %! # in Hogg and Ledolter (1987) Engineering Statistics. New York: MacMillan %! %! strength = [82 86 79 83 84 85 86 87 74 82 ... %! 78 75 76 77 79 79 77 78 82 79]'; %! alloy = {"st","st","st","st","st","st","st","st", ... %! "al1","al1","al1","al1","al1","al1", ... %! "al2","al2","al2","al2","al2","al2"}'; %! %! [P, ATAB, STATS] = anovan (strength, alloy, "display", "on", ... %! "varnames", "alloy"); %!demo %! %! # One-way repeated measures ANOVA on the data from a study on the number of %! # words recalled by 10 subjects for three time condtions, in Loftus & Masson %! # (1994) Psychon Bull Rev. 1(4):476-490, Table 2. Note that the interaction %! # between seconds x subject was dropped from the full model by assigning %! # subject as a random factor ('). %! %! words = [10 13 13; 6 8 8; 11 14 14; 22 23 25; 16 18 20; ... %! 15 17 17; 1 1 4; 12 15 17; 9 12 12; 8 9 12]; %! seconds = [1 2 5; 1 2 5; 1 2 5; 1 2 5; 1 2 5; ... %! 1 2 5; 1 2 5; 1 2 5; 1 2 5; 1 2 5;]; %! subject = [ 1 1 1; 2 2 2; 3 3 3; 4 4 4; 5 5 5; ... %! 6 6 6; 7 7 7; 8 8 8; 9 9 9; 10 10 10]; %! %! [P, ATAB, STATS] = anovan (words(:), {seconds(:), subject(:)}, ... %! "model", "full", "random", 2, "sstype", 2, ... %! "display", "on", "varnames", {"seconds", "subject"}); %!demo %! %! # Balanced two-way ANOVA with interaction on the data from a study of popcorn %! # brands and popper types, in Hogg and Ledolter (1987) Engineering Statistics. %! # New York: MacMillan %! %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! brands = {"Gourmet", "National", "Generic"; ... %! "Gourmet", "National", "Generic"; ... %! "Gourmet", "National", "Generic"; ... %! "Gourmet", "National", "Generic"; ... %! "Gourmet", "National", "Generic"; ... %! "Gourmet", "National", "Generic"}; %! popper = {"oil", "oil", "oil"; "oil", "oil", "oil"; "oil", "oil", "oil"; ... %! "air", "air", "air"; "air", "air", "air"; "air", "air", "air"}; %! %! [P, ATAB, STATS] = anovan (popcorn(:), {brands(:), popper(:)}, ... %! "display", "on", "model", "full", ... %! "varnames", {"brands", "popper"}); %!demo %! %! # Unbalanced two-way ANOVA (2x2) on the data from a study on the effects of %! # gender and having a college degree on salaries of company employees, %! # in Maxwell, Delaney and Kelly (2018): Chapter 7, Table 15 %! %! salary = [24 26 25 24 27 24 27 23 15 17 20 16, ... %! 25 29 27 19 18 21 20 21 22 19]'; %! gender = {"f" "f" "f" "f" "f" "f" "f" "f" "f" "f" "f" "f"... %! "m" "m" "m" "m" "m" "m" "m" "m" "m" "m"}'; %! degree = [1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0]'; %! %! [P, ATAB, STATS] = anovan (salary, {gender, degree}, "model", "full", ... %! "sstype", 3, "display", "on", "varnames", ... %! {"gender", "degree"}); %!demo %! %! # Unbalanced two-way ANOVA (3x2) on the data from a study of the effect of %! # adding sugar and/or milk on the tendency of coffee to make people babble, %! # in from Navarro (2019): 16.10 %! %! sugar = {"real" "fake" "fake" "real" "real" "real" "none" "none" "none" ... %! "fake" "fake" "fake" "real" "real" "real" "none" "none" "fake"}'; %! milk = {"yes" "no" "no" "yes" "yes" "no" "yes" "yes" "yes" ... %! "no" "no" "yes" "no" "no" "no" "no" "no" "yes"}'; %! babble = [4.6 4.4 3.9 5.6 5.1 5.5 3.9 3.5 3.7... %! 5.6 4.7 5.9 6.0 5.4 6.6 5.8 5.3 5.7]'; %! %! [P, ATAB, STATS] = anovan (babble, {sugar, milk}, "model", "full", ... %! "sstype", 3, "display", "on", ... %! "varnames", {"sugar", "milk"}); %!demo %! %! # Unbalanced three-way ANOVA (3x2x2) on the data from a study of the effects %! # of three different drugs, biofeedback and diet on patient blood pressure, %! # adapted* from Maxwell, Delaney and Kelly (2018): Chapter 8, Table 12 %! # * Missing values introduced to make the sample sizes unequal to test the %! # calculation of different types of sums-of-squares %! %! drug = {"X" "X" "X" "X" "X" "X" "X" "X" "X" "X" "X" "X" ... %! "X" "X" "X" "X" "X" "X" "X" "X" "X" "X" "X" "X"; %! "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" ... %! "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y"; %! "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" ... %! "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z" "Z"}; %! feedback = [1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0; %! 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0; %! 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0]; %! diet = [0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1; %! 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1; %! 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1]; %! BP = [170 175 165 180 160 158 161 173 157 152 181 190 ... %! 173 194 197 190 176 198 164 190 169 164 176 175; %! 186 194 201 215 219 209 164 166 159 182 187 174 ... %! 189 194 217 206 199 195 171 173 196 199 180 NaN; %! 180 187 199 170 204 194 162 184 183 156 180 173 ... %! 202 228 190 206 224 204 205 199 170 160 NaN NaN]; %! %! [P, ATAB, STATS] = anovan (BP(:), {drug(:), feedback(:), diet(:)}, ... %! "model", "full", "sstype", 3, ... %! "display", "on", ... %! "varnames", {"drug", "feedback", "diet"}); %!demo %! %! # Balanced three-way ANOVA (2x2x2) with one of the factors being a blocking %! # factor. The data is from a randomized block design study on the effects %! # of antioxidant treatment on glutathione-S-transferase (GST) levels in %! # different mouse strains, from Festing (2014), ILAR Journal, 55(3):427-476. %! # Note that all interactions involving block were dropped from the full model %! # by assigning block as a random factor ('). %! %! measurement = [444 614 423 625 408 856 447 719 ... %! 764 831 586 782 609 1002 606 766]'; %! strain= {"NIH","NIH","BALB/C","BALB/C","A/J","A/J","129/Ola","129/Ola", ... %! "NIH","NIH","BALB/C","BALB/C","A/J","A/J","129/Ola","129/Ola"}'; %! treatment={"C" "T" "C" "T" "C" "T" "C" "T" "C" "T" "C" "T" "C" "T" "C" "T"}'; %! block = [1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2]'; %! %! [P, ATAB, STATS] = anovan (measurement/10, {strain, treatment, block}, ... %! "sstype", 2, "model", "full", "random", 3, ... %! "display", "on", ... %! "varnames", {"strain", "treatment", "block"}); %!demo %! %! # One-way ANCOVA on data from a study of the additive effects of species %! # and temperature on chirpy pulses of crickets, from Stitch, The Worst Stats %! # Text eveR %! %! pulse = [67.9 65.1 77.3 78.7 79.4 80.4 85.8 86.6 87.5 89.1 ... %! 98.6 100.8 99.3 101.7 44.3 47.2 47.6 49.6 50.3 51.8 ... %! 60 58.5 58.9 60.7 69.8 70.9 76.2 76.1 77 77.7 84.7]'; %! temp = [20.8 20.8 24 24 24 24 26.2 26.2 26.2 26.2 28.4 ... %! 29 30.4 30.4 17.2 18.3 18.3 18.3 18.9 18.9 20.4 ... %! 21 21 22.1 23.5 24.2 25.9 26.5 26.5 26.5 28.6]'; %! species = {"ex" "ex" "ex" "ex" "ex" "ex" "ex" "ex" "ex" "ex" "ex" ... %! "ex" "ex" "ex" "niv" "niv" "niv" "niv" "niv" "niv" "niv" ... %! "niv" "niv" "niv" "niv" "niv" "niv" "niv" "niv" "niv" "niv"}; %! %! [P, ATAB, STATS] = anovan (pulse, {species, temp}, "model", "linear", ... %! "continuous", 2, "sstype", "h", "display", "on", ... %! "varnames", {"species", "temp"}); %!demo %! %! # Factorial ANCOVA on data from a study of the effects of treatment and %! # exercise on stress reduction score after adjusting for age. Data from R %! # datarium package). %! %! score = [95.6 82.2 97.2 96.4 81.4 83.6 89.4 83.8 83.3 85.7 ... %! 97.2 78.2 78.9 91.8 86.9 84.1 88.6 89.8 87.3 85.4 ... %! 81.8 65.8 68.1 70.0 69.9 75.1 72.3 70.9 71.5 72.5 ... %! 84.9 96.1 94.6 82.5 90.7 87.0 86.8 93.3 87.6 92.4 ... %! 100. 80.5 92.9 84.0 88.4 91.1 85.7 91.3 92.3 87.9 ... %! 91.7 88.6 75.8 75.7 75.3 82.4 80.1 86.0 81.8 82.5]'; %! treatment = {"yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" ... %! "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" ... %! "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" ... %! "no" "no" "no" "no" "no" "no" "no" "no" "no" "no" ... %! "no" "no" "no" "no" "no" "no" "no" "no" "no" "no" ... %! "no" "no" "no" "no" "no" "no" "no" "no" "no" "no"}'; %! exercise = {"lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" ... %! "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" ... %! "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" ... %! "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" ... %! "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" ... %! "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi"}'; %! age = [59 65 70 66 61 65 57 61 58 55 62 61 60 59 55 57 60 63 62 57 ... %! 58 56 57 59 59 60 55 53 55 58 68 62 61 54 59 63 60 67 60 67 ... %! 75 54 57 62 65 60 58 61 65 57 56 58 58 58 52 53 60 62 61 61]'; %! %! [P, ATAB, STATS] = anovan (score, {treatment, exercise, age}, ... %! "model", [1 0 0; 0 1 0; 0 0 1; 1 1 0], ... %! "continuous", 3, "sstype", "h", "display", "on", ... %! "varnames", {"treatment", "exercise", "age"}); %!demo %! %! # Unbalanced one-way ANOVA with custom, orthogonal contrasts. The statistics %! # relating to the contrasts are shown in the table of model parameters, and %! # can be retrieved from the STATS.coeffs output. %! %! dv = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! g = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 ... %! 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! C = [ 0.4001601 0.3333333 0.5 0.0 %! 0.4001601 0.3333333 -0.5 0.0 %! 0.4001601 -0.6666667 0.0 0.0 %! -0.6002401 0.0000000 0.0 0.5 %! -0.6002401 0.0000000 0.0 -0.5]; %! %! [P,ATAB, STATS] = anovan (dv, g, "contrasts", C, "varnames", "score", ... %! "alpha", 0.05, "display", "on"); %!demo %! %! # One-way ANOVA with the linear model fit by weighted least squares to %! # account for heteroskedasticity. In this example, the variance appears %! # proportional to the outcome, so weights have been estimated by initially %! # fitting the model without weights and regressing the absolute residuals on %! # the fitted values. Although this data could have been analysed by Welch's %! # ANOVA test, the approach here can generalize to ANOVA models with more than %! # one factor. %! %! g = [1, 1, 1, 1, 1, 1, 1, 1, ... %! 2, 2, 2, 2, 2, 2, 2, 2, ... %! 3, 3, 3, 3, 3, 3, 3, 3]'; %! y = [13, 16, 16, 7, 11, 5, 1, 9, ... %! 10, 25, 66, 43, 47, 56, 6, 39, ... %! 11, 39, 26, 35, 25, 14, 24, 17]'; %! %! [P,ATAB,STATS] = anovan(y, g, "display", "off"); %! fitted = STATS.X * STATS.coeffs(:,1); # fitted values %! b = polyfit (fitted, abs (STATS.resid), 1); %! v = polyval (b, fitted); # Variance as a function of the fitted values %! figure("Name", "Regression of the absolute residuals on the fitted values"); %! plot (fitted, abs (STATS.resid),'ob');hold on; plot(fitted,v,'-r'); hold off; %! xlabel("Fitted values"); ylabel("Absolute residuals"); %! %! [P,ATAB,STATS] = anovan (y, g, "weights", v.^-1); ## Test 1 for anovan example 1 ## Test compares anovan to results from MATLAB's anovan and ttest2 functions %!test %! score = [54 23 45 54 45 43 34 65 77 46 65]'; %! gender = {'male' 'male' 'male' 'male' 'male' 'female' 'female' 'female' ... %! 'female' 'female' 'female'}'; %! %! [P, T, STATS] = anovan (score,gender,'display','off'); %! assert (P(1), 0.2612876773271042, 1e-09); # compared to p calculated by MATLAB anovan %! assert (sqrt(T{2,6}), abs(1.198608733288208), 1e-09); # compared to abs(t) calculated from sqrt(F) by MATLAB anovan %! assert (P(1), 0.2612876773271047, 1e-09); # compared to p calculated by MATLAB ttest2 %! assert (sqrt(T{2,6}), abs(-1.198608733288208), 1e-09); # compared to abs(t) calculated by MATLAB ttest2 ## Test 2 for anovan example 2 ## Test compares anovan to results from MATLAB's anovan and ttest functions %!test %! score = [4.5 5.6; 3.7 6.4; 5.3 6.4; 5.4 6.0; 3.9 5.7]'; %! treatment = {'before' 'after'; 'before' 'after'; 'before' 'after'; %! 'before' 'after'; 'before' 'after'}'; %! subject = {'GS' 'GS'; 'JM' 'JM'; 'HM' 'HM'; 'JW' 'JW'; 'PS' 'PS'}'; %! %! [P, ATAB, STATS] = anovan (score(:),{treatment(:),subject(:)},'display','off','sstype',2); %! assert (P(1), 0.016004356735364, 1e-09); # compared to p calculated by MATLAB anovan %! assert (sqrt(ATAB{2,6}), abs(4.00941576558195), 1e-09); # compared to abs(t) calculated from sqrt(F) by MATLAB anovan %! assert (P(1), 0.016004356735364, 1e-09); # compared to p calculated by MATLAB ttest2 %! assert (sqrt(ATAB{2,6}), abs(-4.00941576558195), 1e-09); # compared to abs(t) calculated by MATLAB ttest2 ## Test 3 for anovan example 3 ## Test compares anovan to results from MATLAB's anovan and anova1 functions %!test %! strength = [82 86 79 83 84 85 86 87 74 82 ... %! 78 75 76 77 79 79 77 78 82 79]'; %! alloy = {'st','st','st','st','st','st','st','st', ... %! 'al1','al1','al1','al1','al1','al1', ... %! 'al2','al2','al2','al2','al2','al2'}'; %! %! [P, ATAB, STATS] = anovan (strength,{alloy},'display','off'); %! assert (P(1), 0.000152643638830491, 1e-09); %! assert (ATAB{2,6}, 15.4, 1e-09); ## Test 4 for anovan example 4 ## Test compares anovan to results from MATLAB's anovan function %!test %! words = [10 13 13; 6 8 8; 11 14 14; 22 23 25; 16 18 20; ... %! 15 17 17; 1 1 4; 12 15 17; 9 12 12; 8 9 12]; %! subject = [ 1 1 1; 2 2 2; 3 3 3; 4 4 4; 5 5 5; ... %! 6 6 6; 7 7 7; 8 8 8; 9 9 9; 10 10 10]; %! seconds = [1 2 5; 1 2 5; 1 2 5; 1 2 5; 1 2 5; ... %! 1 2 5; 1 2 5; 1 2 5; 1 2 5; 1 2 5;]; %! %! [P, ATAB, STATS] = anovan (words(:),{seconds(:),subject(:)},'model','full','random',2,'sstype',2,'display','off'); %! assert (P(1), 1.51865926758752e-07, 1e-09); %! assert (ATAB{2,2}, 52.2666666666667, 1e-09); %! assert (ATAB{3,2}, 942.533333333333, 1e-09); %! assert (ATAB{4,2}, 11.0666666666667, 1e-09); ## Test 5 for anovan example 5 ## Test compares anovan to results from MATLAB's anovan function %!test %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! brands = {'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'}; %! popper = {'oil', 'oil', 'oil'; 'oil', 'oil', 'oil'; 'oil', 'oil', 'oil'; ... %! 'air', 'air', 'air'; 'air', 'air', 'air'; 'air', 'air', 'air'}; %! %! [P, ATAB, STATS] = anovan (popcorn(:),{brands(:),popper(:)},'display','off','model','full'); %! assert (P(1), 7.67895738278171e-07, 1e-09); %! assert (P(2), 0.000100373896304998, 1e-09); %! assert (P(3), 0.746215396636649, 1e-09); %! assert (ATAB{2,6}, 56.7, 1e-09); %! assert (ATAB{3,6}, 32.4, 1e-09); %! assert (ATAB{4,6}, 0.29999999999997, 1e-09); ## Test 6 for anovan example 6 ## Test compares anovan to results from MATLAB's anovan function %!test %! salary = [24 26 25 24 27 24 27 23 15 17 20 16, ... %! 25 29 27 19 18 21 20 21 22 19]'; %! gender = {'f' 'f' 'f' 'f' 'f' 'f' 'f' 'f' 'f' 'f' 'f' 'f'... %! 'm' 'm' 'm' 'm' 'm' 'm' 'm' 'm' 'm' 'm'}'; %! degree = [1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0]'; %! %! [P, ATAB, STATS] = anovan (salary,{gender,degree},'model','full','sstype',1,'display','off'); %! assert (P(1), 0.747462549227232, 1e-09); %! assert (P(2), 1.03809316857694e-08, 1e-09); %! assert (P(3), 0.523689833702691, 1e-09); %! assert (ATAB{2,2}, 0.296969696969699, 1e-09); %! assert (ATAB{3,2}, 272.391841491841, 1e-09); %! assert (ATAB{4,2}, 1.17482517482512, 1e-09); %! assert (ATAB{5,2}, 50.0000000000001, 1e-09); %! [P, ATAB, STATS] = anovan (salary,{degree,gender},'model','full','sstype',1,'display','off'); %! assert (P(1), 2.53445097305047e-08, 1e-09); %! assert (P(2), 0.00388133678528749, 1e-09); %! assert (P(3), 0.523689833702671, 1e-09); %! assert (ATAB{2,2}, 242.227272727273, 1e-09); %! assert (ATAB{3,2}, 30.4615384615384, 1e-09); %! assert (ATAB{4,2}, 1.17482517482523, 1e-09); %! assert (ATAB{5,2}, 50.0000000000001, 1e-09); %! [P, ATAB, STATS] = anovan (salary,{gender,degree},'model','full','sstype',2,'display','off'); %! assert (P(1), 0.00388133678528743, 1e-09); %! assert (P(2), 1.03809316857694e-08, 1e-09); %! assert (P(3), 0.523689833702691, 1e-09); %! assert (ATAB{2,2}, 30.4615384615385, 1e-09); %! assert (ATAB{3,2}, 272.391841491841, 1e-09); %! assert (ATAB{4,2}, 1.17482517482512, 1e-09); %! assert (ATAB{5,2}, 50.0000000000001, 1e-09); %! [P, ATAB, STATS] = anovan (salary,{gender,degree},'model','full','sstype',3,'display','off'); %! assert (P(1), 0.00442898146583742, 1e-09); %! assert (P(2), 1.30634252053587e-08, 1e-09); %! assert (P(3), 0.523689833702691, 1e-09); %! assert (ATAB{2,2}, 29.3706293706294, 1e-09); %! assert (ATAB{3,2}, 264.335664335664, 1e-09); %! assert (ATAB{4,2}, 1.17482517482512, 1e-09); %! assert (ATAB{5,2}, 50.0000000000001, 1e-09); ## Test 7 for anovan example 7 ## Test compares anovan to results from MATLAB's anovan function %!test %! sugar = {'real' 'fake' 'fake' 'real' 'real' 'real' 'none' 'none' 'none' ... %! 'fake' 'fake' 'fake' 'real' 'real' 'real' 'none' 'none' 'fake'}'; %! milk = {'yes' 'no' 'no' 'yes' 'yes' 'no' 'yes' 'yes' 'yes' ... %! 'no' 'no' 'yes' 'no' 'no' 'no' 'no' 'no' 'yes'}'; %! babble = [4.6 4.4 3.9 5.6 5.1 5.5 3.9 3.5 3.7... %! 5.6 4.7 5.9 6.0 5.4 6.6 5.8 5.3 5.7]'; %! %! [P, ATAB, STATS] = anovan (babble,{sugar,milk},'model','full','sstype',1,'display','off'); %! assert (P(1), 0.0108632139833963, 1e-09); %! assert (P(2), 0.0810606976703546, 1e-09); %! assert (P(3), 0.00175433329935627, 1e-09); %! assert (ATAB{2,2}, 3.55752380952381, 1e-09); %! assert (ATAB{3,2}, 0.956108477471702, 1e-09); %! assert (ATAB{4,2}, 5.94386771300448, 1e-09); %! assert (ATAB{5,2}, 3.1625, 1e-09); %! [P, ATAB, STATS] = anovan (babble,{milk,sugar},'model','full','sstype',1,'display','off'); %! assert (P(1), 0.0373333189297505, 1e-09); %! assert (P(2), 0.017075098787169, 1e-09); %! assert (P(3), 0.00175433329935627, 1e-09); %! assert (ATAB{2,2}, 1.444, 1e-09); %! assert (ATAB{3,2}, 3.06963228699552, 1e-09); %! assert (ATAB{4,2}, 5.94386771300448, 1e-09); %! assert (ATAB{5,2}, 3.1625, 1e-09); %! [P, ATAB, STATS] = anovan (babble,{sugar,milk},'model','full','sstype',2,'display','off'); %! assert (P(1), 0.017075098787169, 1e-09); %! assert (P(2), 0.0810606976703546, 1e-09); %! assert (P(3), 0.00175433329935627, 1e-09); %! assert (ATAB{2,2}, 3.06963228699552, 1e-09); %! assert (ATAB{3,2}, 0.956108477471702, 1e-09); %! assert (ATAB{4,2}, 5.94386771300448, 1e-09); %! assert (ATAB{5,2}, 3.1625, 1e-09); %! [P, ATAB, STATS] = anovan (babble,{sugar,milk},'model','full','sstype',3,'display','off'); %! assert (P(1), 0.0454263063473954, 1e-09); %! assert (P(2), 0.0746719907091438, 1e-09); %! assert (P(3), 0.00175433329935627, 1e-09); %! assert (ATAB{2,2}, 2.13184977578476, 1e-09); %! assert (ATAB{3,2}, 1.00413461538462, 1e-09); %! assert (ATAB{4,2}, 5.94386771300448, 1e-09); %! assert (ATAB{5,2}, 3.1625, 1e-09); ## Test 8 for anovan example 8 ## Test compares anovan to results from MATLAB's anovan function %!test %! drug = {'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' ... %! 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X'; %! 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' ... %! 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y' 'Y'; %! 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' ... %! 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z'}; %! feedback = [1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0; %! 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0; %! 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0]; %! diet = [0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1; %! 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1; %! 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1]; %! BP = [170 175 165 180 160 158 161 173 157 152 181 190 ... %! 173 194 197 190 176 198 164 190 169 164 176 175; %! 186 194 201 215 219 209 164 166 159 182 187 174 ... %! 189 194 217 206 199 195 171 173 196 199 180 NaN; %! 180 187 199 170 204 194 162 184 183 156 180 173 ... %! 202 228 190 206 224 204 205 199 170 160 NaN NaN]; %! %! [P, ATAB, STATS] = anovan (BP(:),{drug(:),feedback(:),diet(:)},'model','full','sstype', 1,'display','off'); %! assert (P(1), 7.02561843825325e-05, 1e-09); %! assert (P(2), 0.000425806013389362, 1e-09); %! assert (P(3), 6.16780773446401e-07, 1e-09); %! assert (P(4), 0.261347622678438, 1e-09); %! assert (P(5), 0.0542278432357043, 1e-09); %! assert (P(6), 0.590353225626655, 1e-09); %! assert (P(7), 0.0861628249564267, 1e-09); %! assert (ATAB{2,2}, 3614.70355731226, 1e-09); %! assert (ATAB{3,2}, 2227.46639771024, 1e-09); %! assert (ATAB{4,2}, 5008.25614451819, 1e-09); %! assert (ATAB{5,2}, 437.066007908781, 1e-09); %! assert (ATAB{6,2}, 976.180770397332, 1e-09); %! assert (ATAB{7,2}, 46.616653365254, 1e-09); %! assert (ATAB{8,2}, 814.345251396648, 1e-09); %! assert (ATAB{9,2}, 9065.8, 1e-09); %! [P, ATAB, STATS] = anovan (BP(:),{drug(:),feedback(:),diet(:)},'model','full','sstype',2,'display','off'); %! assert (P(1), 9.4879638470754e-05, 1e-09); %! assert (P(2), 0.00124177666315809, 1e-09); %! assert (P(3), 6.86162012732911e-07, 1e-09); %! assert (P(4), 0.260856132341256, 1e-09); %! assert (P(5), 0.0523758623892078, 1e-09); %! assert (P(6), 0.590353225626655, 1e-09); %! assert (P(7), 0.0861628249564267, 1e-09); %! assert (ATAB{2,2}, 3481.72176560122, 1e-09); %! assert (ATAB{3,2}, 1837.08812970469, 1e-09); %! assert (ATAB{4,2}, 4957.20277938622, 1e-09); %! assert (ATAB{5,2}, 437.693674777847, 1e-09); %! assert (ATAB{6,2}, 988.431929811402, 1e-09); %! assert (ATAB{7,2}, 46.616653365254, 1e-09); %! assert (ATAB{8,2}, 814.345251396648, 1e-09); %! assert (ATAB{9,2}, 9065.8, 1e-09); %! [P, ATAB, STATS] = anovan (BP(:),{drug(:),feedback(:),diet(:)},'model','full','sstype', 3,'display','off'); %! assert (P(1), 0.000106518678028207, 1e-09); %! assert (P(2), 0.00125371366571508, 1e-09); %! assert (P(3), 5.30813260778464e-07, 1e-09); %! assert (P(4), 0.308353667232981, 1e-09); %! assert (P(5), 0.0562901327343161, 1e-09); %! assert (P(6), 0.599091042141092, 1e-09); %! assert (P(7), 0.0861628249564267, 1e-09); %! assert (ATAB{2,2}, 3430.88156424581, 1e-09); %! assert (ATAB{3,2}, 1833.68031496063, 1e-09); %! assert (ATAB{4,2}, 5080.48346456693, 1e-09); %! assert (ATAB{5,2}, 382.07709497207, 1e-09); %! assert (ATAB{6,2}, 963.037988826813, 1e-09); %! assert (ATAB{7,2}, 44.4519685039322, 1e-09); %! assert (ATAB{8,2}, 814.345251396648, 1e-09); %! assert (ATAB{9,2}, 9065.8, 1e-09); ## Test 9 for anovan example 9 ## Test compares anovan to results from MATLAB's anovan function %!test %! measurement = [444 614 423 625 408 856 447 719 ... %! 764 831 586 782 609 1002 606 766]'; %! strain= {'NIH','NIH','BALB/C','BALB/C','A/J','A/J','129/Ola','129/Ola', ... %! 'NIH','NIH','BALB/C','BALB/C','A/J','A/J','129/Ola','129/Ola'}'; %! treatment={'C' 'T' 'C' 'T' 'C' 'T' 'C' 'T' 'C' 'T' 'C' 'T' 'C' 'T' 'C' 'T'}'; %! block = [1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2]'; %! %! [P, ATAB, STATS] = anovan (measurement/10,{strain,treatment,block},'model','full','random',3,'display','off'); %! assert (P(1), 0.0914352969909372, 1e-09); %! assert (P(2), 5.04077373924908e-05, 1e-09); %! assert (P(4), 0.0283196918836667, 1e-09); %! assert (ATAB{2,2}, 286.132500000002, 1e-09); %! assert (ATAB{3,2}, 2275.29, 1e-09); %! assert (ATAB{4,2}, 1242.5625, 1e-09); %! assert (ATAB{5,2}, 495.905000000001, 1e-09); %! assert (ATAB{6,2}, 207.007499999999, 1e-09); ## Test 10 for anovan example 10 ## Test compares anovan to results from MATLAB's anovan function %!test %! pulse = [67.9 65.1 77.3 78.7 79.4 80.4 85.8 86.6 87.5 89.1 ... %! 98.6 100.8 99.3 101.7 44.3 47.2 47.6 49.6 50.3 51.8 ... %! 60 58.5 58.9 60.7 69.8 70.9 76.2 76.1 77 77.7 84.7]'; %! temp = [20.8 20.8 24 24 24 24 26.2 26.2 26.2 26.2 28.4 ... %! 29 30.4 30.4 17.2 18.3 18.3 18.3 18.9 18.9 20.4 ... %! 21 21 22.1 23.5 24.2 25.9 26.5 26.5 26.5 28.6]'; %! species = {'ex' 'ex' 'ex' 'ex' 'ex' 'ex' 'ex' 'ex' 'ex' 'ex' 'ex' ... %! 'ex' 'ex' 'ex' 'niv' 'niv' 'niv' 'niv' 'niv' 'niv' 'niv' ... %! 'niv' 'niv' 'niv' 'niv' 'niv' 'niv' 'niv' 'niv' 'niv' 'niv'}; %! %! [P, ATAB, STATS] = anovan (pulse,{species,temp},'model','linear','continuous',2,'sstype','h','display','off'); %! assert (P(1), 6.27153318786007e-14, 1e-09); %! assert (P(2), 2.48773241196644e-25, 1e-09); %! assert (ATAB{2,2}, 598.003953318404, 1e-09); %! assert (ATAB{3,2}, 4376.08256843712, 1e-09); %! assert (ATAB{4,2}, 89.3498685376726, 1e-09); %! assert (ATAB{2,6}, 187.399388123951, 1e-09); %! assert (ATAB{3,6}, 1371.35413763454, 1e-09); ## Test 11 for anovan example 11 ## Test compares anovan to results from MATLAB's anovan function %!test %! score = [95.6 82.2 97.2 96.4 81.4 83.6 89.4 83.8 83.3 85.7 ... %! 97.2 78.2 78.9 91.8 86.9 84.1 88.6 89.8 87.3 85.4 ... %! 81.8 65.8 68.1 70.0 69.9 75.1 72.3 70.9 71.5 72.5 ... %! 84.9 96.1 94.6 82.5 90.7 87.0 86.8 93.3 87.6 92.4 ... %! 100. 80.5 92.9 84.0 88.4 91.1 85.7 91.3 92.3 87.9 ... %! 91.7 88.6 75.8 75.7 75.3 82.4 80.1 86.0 81.8 82.5]'; %! treatment = {'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' ... %! 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' ... %! 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' 'yes' ... %! 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' ... %! 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' ... %! 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no' 'no'}'; %! exercise = {'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' ... %! 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' ... %! 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' ... %! 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' 'lo' ... %! 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' 'mid' ... %! 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi' 'hi'}'; %! age = [59 65 70 66 61 65 57 61 58 55 62 61 60 59 55 57 60 63 62 57 ... %! 58 56 57 59 59 60 55 53 55 58 68 62 61 54 59 63 60 67 60 67 ... %! 75 54 57 62 65 60 58 61 65 57 56 58 58 58 52 53 60 62 61 61]'; %! %! [P, ATAB, STATS] = anovan (score,{treatment,exercise,age},'model','full','continuous',3,'sstype','h','display','off'); %! assert (P(5), 0.9245630968248468, 1e-09); %! assert (P(6), 0.791115159521822, 1e-09); %! assert (P(7), 0.9296668751457956, 1e-09); %! [P, ATAB, STATS] = anovan (score,{treatment,exercise,age},'model',[1 0 0; 0 1 0; 0 0 1; 1 1 0],'continuous',3,'sstype','h','display','off'); %! assert (P(1), 0.00158132928938933, 1e-09); %! assert (P(2), 2.12537505039986e-07, 1e-09); %! assert (P(3), 0.00390292555160047, 1e-09); %! assert (P(4), 0.0164086580775543, 1e-09); %! assert (ATAB{2,6}, 11.0956027650549, 1e-09); %! assert (ATAB{3,6}, 20.8195665467178, 1e-09); %! assert (ATAB{4,6}, 9.10966630720186, 1e-09); %! assert (ATAB{5,6}, 4.4457923698584, 1e-09); ## Test 12 for anovan example 12 ## Test compares anovan regression coefficients to R: ## https://www.uvm.edu/~statdhtx/StatPages/Unequal-ns/Unequal_n%27s_contrasts.html %!test %! dv = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! g = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! C = [ 0.4001601 0.3333333 0.5 0.0 %! 0.4001601 0.3333333 -0.5 0.0 %! 0.4001601 -0.6666667 0.0 0.0 %! -0.6002401 0.0000000 0.0 0.5 %! -0.6002401 0.0000000 0.0 -0.5]; %! %! [P,ATAB,STATS] = anovan (dv,g,'contrasts',{C},'display','off'); %! assert (STATS.coeffs(1,1), 19.4001, 1e-04); %! assert (STATS.coeffs(2,1), -9.3297, 1e-04); %! assert (STATS.coeffs(3,1), -5.0000, 1e-04); %! assert (STATS.coeffs(4,1), -8.0000, 1e-04); %! assert (STATS.coeffs(5,1), -8.0000, 1e-04); %! assert (STATS.coeffs(1,2), 0.4831, 1e-04); %! assert (STATS.coeffs(2,2), 0.9694, 1e-04); %! assert (STATS.coeffs(3,2), 1.3073, 1e-04); %! assert (STATS.coeffs(4,2), 1.6411, 1e-04); %! assert (STATS.coeffs(5,2), 1.4507, 1e-04); %! assert (STATS.coeffs(1,5), 40.161, 1e-03); %! assert (STATS.coeffs(2,5), -9.624, 1e-03); %! assert (STATS.coeffs(3,5), -3.825, 1e-03); %! assert (STATS.coeffs(4,5), -4.875, 1e-03); %! assert (STATS.coeffs(5,5), -5.515, 1e-03); %! assert (STATS.coeffs(2,6), 5.74e-11, 1e-12); %! assert (STATS.coeffs(3,6), 0.000572, 1e-06); %! assert (STATS.coeffs(4,6), 2.86e-05, 1e-07); %! assert (STATS.coeffs(5,6), 4.44e-06, 1e-08); statistics-release-1.7.3/inst/bar3.m000066400000000000000000000433401475240274700173340ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} bar3 (@var{z}) ## @deftypefnx {statistics} {} bar3 (@var{y}, @var{z}) ## @deftypefnx {statistics} {} bar3 (@dots{}, @var{width}) ## @deftypefnx {statistics} {} bar3 (@dots{}, @var{style}) ## @deftypefnx {statistics} {} bar3 (@dots{}, @var{color}) ## @deftypefnx {statistics} {} bar3 (@dots{}, @var{name}, @var{value}) ## @deftypefnx {statistics} {} bar3 (@var{ax}, @dots{}) ## @deftypefnx {statistics} {@var{p} =} bar3 (@dots{}) ## ## Plot a 3D bar graph. ## ## @code{bar3 (@var{z})} plots 3D bar graph for the elements of @var{z}. Each ## bar corresponds to an element in @var{z}, which can be a scalar, vector, or ## 2D matrix. By default, each column in @var{z} is considered as a series and ## it is handled as a distinct series of bars. When @var{z} is a vector, unlike ## MATLAB, which plots it as a single series of bars, Octave discriminates ## between a row and column vector of @var{z}. Hence, when @var{z} is column ## vector, it is plotted as a single series of bars (same color), whereas when ## @var{z} is row vector, each bar is plotted as a different group (different ## colors). For an @math{MxN} matrix, the function plots the bars corresponding ## to each row on the @qcode{y-axis} ranging from @math{1} to @math{M} and each ## column on the @qcode{x-axis} ranging from @math{1} to @math{N}. ## ## @code{bar3 (@var{y}, @var{z})} plots a 3D bar graph of the elements in ## @var{z} at the @qcode{y-values} specified in @var{y}. It should be noted ## that @var{y} only affects the tick names along the @qcode{y-axis} rather the ## actual values. If you want to specify non-numerical values for @var{y}, you ## can specify it with the paired @var{name}/@var{value} syntax shown below. ## ## @code{bar3 (@dots{}, @var{width})} sets the width of the bars along the ## @qcode{x-} and @qcode{y-axes} and controls the separation of bars among each ## other. @var{width} can take any value in the range @math{(0,1]}. By default, ## @var{width} is 0.8 and the bars have a small separation. If width is 1, the ## bars touch one another. Alternatively, you can define @var{width} as a two- ## element vector using the paired @var{name}/@var{value} syntax shown below, in ## which case you can control the bar separation along each axis independently. ## ## @code{bar3 (@dots{}, @var{style})} specifies the style of the bars, where ## @var{style} can be @qcode{'detached'}, @qcode{'grouped'}, or ## @qcode{'stacked'}. The default style is @qcode {'detached'}. ## ## @code{bar3 (@dots{}, @var{color}} displays all bars using the color specified ## by color. For example, use @qcode{'red'} or @qcode{'red'} to specify all red ## bars. When you want to specify colors for several groups, @var{color} can be ## a cellstr vector with each element specifying the color of each group. ## @var{color} can also be specified as a numerical @math{Mx3} matrix, where ## each row corresponds to a RGB value with its elements in the range ## @math{[0,1]}. If only one color is specified, then it applies to all bars. ## If the number of colors equals the number of groups, then each color is ## applied to each group. If the number of colors equals the number of elements ## in @var{z}, then each inidividual bar is assigned the particular color. You ## can also define @var{color} using the paired @var{name}/@var{value} syntax ## shown below. ## ## @code{bar3 (@dots{}, @var{name}, @var{value})} specifies one or more of the ## following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"width"} @tab A two-element vector specifying the width if ## of the bars along the @qcode{x-} and @qcode{y-axes}, respectively. Each ## element must be in the range @math{(0,1]}. ## ## @item @tab @qcode{"color"} @tab A character or a cellstr vector, or a ## numerical @math{Mx3} matrix following the same conventions as the @var{color} ## input argument. ## ## @item @tab @qcode{"xlabel"} @tab A cellstr vector specifying the group names ## along the @qcode{x-axis}. ## ## @item @tab @qcode{"ylabel"} @tab A cellstr vector specifying the names of the ## bars in the same series along the @qcode{y-axis}. ## @end multitable ## ## @code{bar3 (@var{ax}, @dots{})} can also take an axes handle @var{ax} as a ## first argument in which case it plots into the axes specified by @var{ax} ## instead of into the current axes specified by @code{gca ()}. The optional ## argument @var{ax} can precede any of the input argument combinations in the ## previous syntaxes. ## ## @code{@var{p} = bar3 (@dots{})} returns a patch handle @var{p}, which can be ## used to set properties of the bars after displaying the 3D bar graph. ## ## @seealso{boxplot, hist3} ## @end deftypefn function [varargout] = bar3 (varargin) if (nargin < 1) print_usage (); endif ## Check if first input is an axes handle if (isaxes (varargin{1})) ax = varargin{1}; varargin(1) = []; new_axes = false; else new_axes = true; endif ## Parse input argument Z if (numel (varargin) < 1) print_usage (); endif z = varargin{1}; varargin(1) = []; if (! isnumeric (z)) error ("bar3: Z must be numeric."); endif ## Add defaults y = []; width = 0.8; depth = 0.8; style = "detached"; color = []; xlabel = []; ylabel = []; ## Valid Colors for input validation vc = {'red', 'r', 'green', 'g', 'blue', 'b', 'cyan', 'c', ... 'magenta', 'm', 'yellow', 'y', 'black', 'k', 'white', 'w'}; ## Parse extra input arguments while (numel (varargin) > 0) tmp = varargin{1}; if (isnumeric (tmp) && isempty (y)) if (isvector (tmp) && isvector (z) && numel (tmp) == numel (z)) y = z; z = tmp; elseif (all (size (tmp) > 1) && size (tmp, 1) == numel (z) && isvector (z)) y = z; z = tmp; elseif (isscalar (tmp)) y = NaN; if (tmp > 0 && tmp <= 1) width = tmp; depth = tmp; else error ("bar3: WIDTH must be a scalar in the range (0,1]."); endif elseif (size (tmp, 2) == 3 && all (tmp(:) >= 0) && all (tmp(:) <= 1)) y = NaN; color = tmp; else error ("bar3: inconsistent size in Y and Z input arguments."); endif varargin(1) = []; elseif (isnumeric (tmp) && isscalar (tmp)) if (tmp > 0 && tmp <= 1) width = tmp; depth = tmp; else error ("bar3: WIDTH must be a scalar in the range (0,1]."); endif varargin(1) = []; elseif (isnumeric (tmp)) if (size (tmp, 2) == 3 && all (tmp(:) >= 0) && all (tmp(:) <= 1)) color = tmp; else error (["bar3: numeric COLOR must be a 1x3 vector of an Nx3 matrix", ... " where each value is between 0 and 1 inclusive."]); endif color = tmp; varargin(1) = []; elseif (ischar (tmp)) if (any (strcmpi (tmp, {"detached", "grouped", "stacked"}))) style = tmp; varargin(1) = []; elseif (any (strcmpi (tmp, vc))) color = tmp; varargin(1) = []; elseif (strcmpi (tmp, "width")) if (numel (varargin) < 2) error ("bar3: missing value for optional argument 'width'."); endif w = varargin{2}; if (isscalar (w) && isnumeric (w) && isfinite (w) && w > 0 && w <= 1) width = w; depth = w; elseif (numel (w) == 2 && isnumeric (w) && isfinite (w) && all (w > 0) && all (w <= 1)) width = w(1); depth = w(2); else error ("bar3: invalid value for optional argument 'width'."); endif varargin([1:2]) = []; elseif (strcmpi (tmp, "color")) if (numel (varargin) < 2) error ("bar3: missing value for optional argument 'color'."); endif c = varargin{2}; if (iscellstr (c)) is_vc = all (cell2mat (cellfun (@(x) any (strcmpi (vc, x)), ... c, "UniformOutput", false))); if (is_vc) color = c; else error ("bar3: invalid value for optional argument 'color'."); endif elseif (ischar (c) && isvector (c)) if (any (strcmpi (c, vc))) color = c; else error ("bar3: invalid value for optional argument 'color'."); endif elseif (isnumeric (c)) if (size (c, 2) == 3 && all (c(:) >= 0) && all (c(:) <= 1)) color = c; else error (["bar3: numeric COLOR must be a 1x3 vector of an Nx3", ... " matrix where each value is between 0 and 1 inclusive."]); endif else error ("bar3: invalid value for optional argument 'color'."); endif varargin([1:2]) = []; elseif (strcmpi (tmp, "xlabel")) if (numel (varargin) < 2) error ("bar3: missing value for optional argument 'xlabel'."); endif xlabel = varargin{2}; if (! iscellstr (xlabel)) error ("bar3: invalid value for optional argument 'xlabel'."); endif varargin([1:2]) = []; elseif (strcmpi (tmp, "ylabel")) if (numel (varargin) < 2) error ("bar3: missing value for optional argument 'ylabel'."); endif ylabel = varargin{2}; if (! iscellstr (ylabel)) error ("bar3: invalid value for optional argument 'ylabel'."); endif varargin([1:2]) = []; else error ("bar3: invalid optional argument."); endif elseif (iscellstr (tmp)) is_vc = all (cell2mat (cellfun (@(x) any (strcmpi (vc, x)), ... tmp, "UniformOutput", false))); if (is_vc) color = tmp; else error ("bar3: invalid value for optional COLOR argument."); endif varargin(1) = []; else error ("bar3: invalid optional argument."); endif endwhile ## Get number of column bars from z input [ny, nx] = size (z); ## Check xlabel and ylabel if (! isempty (xlabel) && numel (xlabel) != nx) error ("bar3: the elements in 'xlabel' must equal the columns in Z."); endif if (! isempty (ylabel) && numel (ylabel) != ny) error ("bar3: the elements in 'ylabel' must equal the rows in Z."); endif ## Check COLOR for valid dimensions if (isempty (color)) if (nx == 1) cargs = {'FaceColor', 'b'}; elseif (nx == 0) defc = [0,0,1;1,0,1;0,1,0;1,1,0]; fvcd = kron (defc([1:nx],:), ones (6 * ny, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; else fvcd = kron ((1:nx)', ones (6 * ny, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat', ... 'CDataMapping', 'scaled'}; endif elseif (isnumeric (color)) if (size (color, 1) == numel (z)) fvcd = kron (color, ones (6, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; elseif (size (color, 1) == nx) fvcd = kron (color, ones (6 * ny, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; elseif (size (color, 1) == ny) fvcd = repmat (kron (color, ones (6, 1)), nx, 1); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; elseif (size (color, 1) == 1) cargs = {'FaceVertexCData', color, 'FaceColor', 'flat'}; endif elseif (ischar (color)) cargs = {'FaceColor', color}; elseif (iscellstr (color)) endif ## Construct a "template" column-bar (8 vertices and 6 faces) centered at ## origin, with height = 1, and width along x-axis and depth along y-axis hw = width / 2; hd = depth / 2; ## Scale the bar's base when groupping together if (strcmpi (style, "grouped")) sc = nx + 1; hw = hw / sc; hd = hd / sc; endif [X, Y, Z] = ndgrid ([-hw, hw], [-hd, hd], [0, 1]); V = [X(:), Y(:), Z(:)]; F = [1, 2, 4, 3; 5, 6, 8, 7; 1, 2, 6, 5; 3, 4, 8, 7; 1, 5, 7, 3; 2, 6, 8, 4]; ## Replicate faces to the required number of bars increments = 0:8:8 * (nx * ny - 1); F = bsxfun (@plus, F, permute (increments, [1, 3, 2])); F = reshape (permute (F, [2, 1, 3]), 4, []).'; ## Replicate vertices to the required number of bars [offsetX, offsetY] = meshgrid (1:nx, 1:ny); offset = [offsetX(:), offsetY(:)]; offset(:,3) = 0; V = bsxfun (@plus, V, permute (offset, [3, 2, 1])); V = reshape (permute (V, [2, 1, 3]), 3, []).'; if (strcmpi (style, "detached")) ## Adjust bar heights according to values in z input V(:,3) = V(:,3) .* kron (z(:), ones (8,1)); elseif (strcmpi (style, "grouped")) ## Adjust bar heights according to values in z input V(:,3) = V(:,3) .* kron (z(:), ones (8,1)); ## Move groups along x axis V(:,1) = V(:,1) - kron (kron ([0:nx-1], ones (ny, 1))(:), ones (8,1)); ## Move groups along y axis offset = [-nx+1:2:nx-1] * (hd / width); V(:,2) = V(:,2) + kron (kron (ones (1, ny), offset)(:), ones (8,1)); nx = 1; elseif (strcmpi (style, "stacked")) ## Move groups along x axis V(:,1) = V(:,1) - kron (kron ([0:nx-1], ones (ny, 1))(:), ones (8,1)); ## Adjust bar heights according to values in z input ZC = cumsum (z,2); Q1 = kron (ZC(:), ones (8,1)); ZC(:,end) = []; ZC = [zeros(ny,1), ZC]; Q2 = kron (ZC(:), ones (8,1)); V(:,3) = V(:,3) .* Q1; idx = V(:,3) == 0; V(idx,3) = Q2(idx); ## Collapse x axis nx = 1; endif ## Draw column bars as patches specified by faces/vertices if (new_axes) ax = gca (); endif p = patch ('Faces', F, 'Vertices', V, 'EdgeColor', 'k', 'Parent', ax, cargs{:}); ## Set view port and axes view (ax, 3); grid (ax, 'on'); axis tight; xlim ([0.5, nx+0.5]); ylim ([0.5, ny+0.5]); set (ax, 'XTick', 1:nx, 'YTick', 1:ny, 'Box', 'off', 'YDir', 'reverse'); ## Fix aspect ratio so that bars appear square when rotating the bar plot if (nx > ny) set (ax, 'PlotBoxAspectRatio', [1, ny/nx, (sqrt(5)-1)/2]); elseif (nx < ny) set (ax, 'PlotBoxAspectRatio', [nx/ny, 1, (sqrt(5)-1)/2]); else set (ax, 'PlotBoxAspectRatio', [1, 1, (sqrt(5)-1)/2]); endif ## Add tick labels in axes (if requested) if (! isempty (xlabel)) set (ax, 'XTickLabel', xlabel); endif if (! isempty (ylabel)) set (ax, 'YTickLabel', ylabel); elseif (! isempty (y) && ! isnan (y)) ylabel = arrayfun (@(x) sprintf ('%d', x), y, 'UniformOutput', false); set (ax, 'YTickLabel', ylabel); endif ## Return handle to patch object if requested if nargout > 0 varargout{1} = p; endif endfunction %!demo %! ## Ploting 5 bars in the same series. %! %! z = [50; 40; 30; 20; 10]; %! bar3 (z); %!demo %! ## Ploting 5 bars in different groups. %! %! z = [50, 40, 30, 20, 10]; %! bar3 (z); %!demo %! ## A 3D bar graph with each series corresponding to a column in z. %! %! z = [1, 4, 7; 2, 5, 8; 3, 6, 9; 4, 7, 10]; %! bar3 (z); %!demo %! ## Specify y-axis locations as tick names. y must be a column vector! %! %! y = [1950, 1960, 1970, 1980, 1990]'; %! z = [16, 8, 4, 2, 1]'; %! bar3 (y, z); %!demo %! ## Plot 3 series as a grouped plot without any space between the grouped bars %! %! z = [70 50 33 10; 75 55 35 15; 80 60 40 20]; %! bar3 (z, 1, 'grouped'); %!demo %! ## Plot a stacked style 3D bar graph %! %! z = [19, 30, 21, 30; 40, 16, 32, 12]; %! b = bar3 (z, 0.5, 'stacked'); ## Test input validation %!error bar3 ("A") %!error bar3 ({2,3,4,5}) %!error ... %! bar3 ([1,2,3]', ones (2)) %!error ... %! bar3 ([1:5], 1.2) %!error ... %! bar3 ([1:5]', ones (5), 1.2) %!error ... %! bar3 ([1:5]', ones (5), [0.8, 0.7]) %!error ... %! bar3 (ones (5), 'width') %!error ... %! bar3 (ones (5), 'width', 1.2) %!error ... %! bar3 (ones (5), 'width', [0.8, 0.8, 0.8]) %!error ... %! bar3 (ones (5), 'color') %!error ... %! bar3 (ones (5), 'color', [0.8, 0.8]) %!error ... %! bar3 (ones (5), 'color', "brown") %!error ... %! bar3 (ones (5), 'color', {"r", "k", "c", "m", "brown"}) %!error ... %! bar3 (ones (5), 'xlabel') %!error ... %! bar3 (ones (5), 'xlabel', 4) %!error ... %! bar3 (ones (5), 'ylabel') %!error ... %! bar3 (ones (5), 'ylabel', 4) %!error bar3 (ones (5), 'this', 4) %!error ... %! bar3 (ones (5), 'xlabel', {"A", "B", "C"}) %!error ... %! bar3 (ones (5), 'ylabel', {"A", "B", "C"}) statistics-release-1.7.3/inst/bar3h.m000066400000000000000000000435411475240274700175070ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} bar3h (@var{y}) ## @deftypefnx {statistics} {} bar3h (@var{z}, @var{y}) ## @deftypefnx {statistics} {} bar3h (@dots{}, @var{width}) ## @deftypefnx {statistics} {} bar3h (@dots{}, @var{style}) ## @deftypefnx {statistics} {} bar3h (@dots{}, @var{color}) ## @deftypefnx {statistics} {} bar3h (@dots{}, @var{name}, @var{value}) ## @deftypefnx {statistics} {} bar3h (@var{ax}, @dots{}) ## @deftypefnx {statistics} {@var{p} =} bar3h (@dots{}) ## ## Plot a horizontal 3D bar graph. ## ## @code{bar3h (@var{y})} plots 3D bar graph for the elements of @var{y}. Each ## bar corresponds to an element in @var{y}, which can be a scalar, vector, or ## 2D matrix. By default, each column in @var{y} is considered as a series and ## it is handled as a distinct series of bars. When @var{y} is a vector, unlike ## MATLAB, which plots it as a single series of bars, Octave discriminates ## between a row and column vector of @var{y}. Hence, when @var{y} is column ## vector, it is plotted as a single series of bars (same color), whereas when ## @var{y} is row vector, each bar is plotted as a different group (different ## colors). For an @math{MxN} matrix, the function plots the bars corresponding ## to each row on the @qcode{z-axis} ranging from @math{1} to @math{M} and each ## column on the @qcode{x-axis} ranging from @math{1} to @math{N}. ## ## @code{bar3h (@var{z}, @var{y})} plots a 3D bar graph of the elements in ## @var{y} at the @qcode{z-values} specified in @var{z}. It should be noted ## that @var{z} only affects the tick names along the @qcode{z-axis} rather the ## actual values. If you want to specify non-numerical values for @var{z}, you ## can specify it with the paired @var{name}/@var{value} syntax shown below. ## ## @code{bar3h (@dots{}, @var{width})} sets the width of the bars along the ## @qcode{x-} and @qcode{z-axes} and controls the separation of bars among each ## other. @var{width} can take any value in the range @math{(0,1]}. By default, ## @var{width} is 0.8 and the bars have a small separation. If width is 1, the ## bars touch one another. Alternatively, you can define @var{width} as a two- ## element vector using the paired @var{name}/@var{value} syntax shown below, in ## which case you can control the bar separation along each axis independently. ## ## @code{bar3h (@dots{}, @var{style})} specifies the style of the bars, where ## @var{style} can be @qcode{'detached'}, @qcode{'grouped'}, or ## @qcode{'stacked'}. The default style is @qcode {'detached'}. ## ## @code{bar3h (@dots{}, @var{color}} displays all bars using the color ## specified by color. For example, use @qcode{'red'} or @qcode{'red'} to ## specify all red bars. When you want to specify colors for several groups, ## @var{color} can be a cellstr vector with each element specifying the color of ## each group. @var{color} can also be specified as a numerical @math{Mx3} ## matrix, where each row corresponds to a RGB value with its elements in the ## range @math{[0,1]}. If only one color is specified, then it applies to all ## bars. If the number of colors equals the number of groups, then each color ## is applied to each group. If the number of colors equals the number of ## elements in @var{y}, then each inidividual bar is assigned the particular ## color. You can also define @var{color} using the paired ## @var{name}/@var{value} syntax shown below. ## ## @code{bar3h (@dots{}, @var{name}, @var{value})} specifies one or more of the ## following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"width"} @tab A two-element vector specifying the width if ## of the bars along the @qcode{x-} and @qcode{z-axes}, respectively. Each ## element must be in the range @math{(0,1]}. ## ## @item @tab @qcode{"color"} @tab A character or a cellstr vector, or a ## numerical @math{Mx3} matrix following the same conventions as the @var{color} ## input argument. ## ## @item @tab @qcode{"xlabel"} @tab A cellstr vector specifying the group names ## along the @qcode{x-axis}. ## ## @item @tab @qcode{"zlabel"} @tab A cellstr vector specifying the names of the ## bars in the same series along the @qcode{z-axis}. ## @end multitable ## ## @code{bar3h (@var{ax}, @dots{})} can also take an axes handle @var{ax} as a ## first argument in which case it plots into the axes specified by @var{ax} ## instead of into the current axes specified by @code{gca ()}. The optional ## argument @var{ax} can precede any of the input argument combinations in the ## previous syntaxes. ## ## @code{@var{p} = bar3h (@dots{})} returns a patch handle @var{p}, which can be ## used to set properties of the bars after displaying the 3D bar graph. ## ## @seealso{boxplot, hist3} ## @end deftypefn function [varargout] = bar3h (varargin) if (nargin < 1) print_usage (); endif ## Check if first input is an axes handle if (isaxes (varargin{1})) ax = varargin{1}; varargin(1) = []; new_axes = false; else new_axes = true; endif ## Parse input argument Z if (numel (varargin) < 1) print_usage (); endif y = varargin{1}; varargin(1) = []; if (! isnumeric (y)) error ("bar3h: Z must be numeric."); endif ## Add defaults z = []; width = 0.8; depth = 0.8; style = "detached"; color = []; xlabel = []; zlabel = []; ## Valid Colors for input validation vc = {'red', 'r', 'green', 'g', 'blue', 'b', 'cyan', 'c', ... 'magenta', 'm', 'yellow', 'z', 'black', 'k', 'white', 'w'}; ## Parse extra input arguments while (numel (varargin) > 0) tmp = varargin{1}; if (isnumeric (tmp) && isempty (z)) if (isvector (tmp) && isvector (y) && numel (tmp) == numel (y)) z = y; y = tmp; elseif (all (size (tmp) > 1) && size (tmp, 1) == numel (y) && isvector (y)) z = y; y = tmp; elseif (isscalar (tmp)) z = NaN; if (tmp > 0 && tmp <= 1) width = tmp; depth = tmp; else error ("bar3h: WIDTH must be a scalar in the range (0,1]."); endif elseif (size (tmp, 2) == 3 && all (tmp(:) >= 0) && all (tmp(:) <= 1)) z = NaN; color = tmp; else error ("bar3h: inconsistent size in Y and Z input arguments."); endif varargin(1) = []; elseif (isnumeric (tmp) && isscalar (tmp)) if (tmp > 0 && tmp <= 1) width = tmp; depth = tmp; else error ("bar3h: WIDTH must be a scalar in the range (0,1]."); endif varargin(1) = []; elseif (isnumeric (tmp)) if (size (tmp, 2) == 3 && all (tmp(:) >= 0) && all (tmp(:) <= 1)) color = tmp; else error (["bar3h: numeric COLOR must be a 1x3 vector of an Nx3 matrix", ... " where each value is between 0 and 1 inclusive."]); endif color = tmp; varargin(1) = []; elseif (ischar (tmp)) if (any (strcmpi (tmp, {"detached", "grouped", "stacked"}))) style = tmp; varargin(1) = []; elseif (any (strcmpi (tmp, vc))) color = tmp; varargin(1) = []; elseif (strcmpi (tmp, "width")) if (numel (varargin) < 2) error ("bar3h: missing value for optional argument 'width'."); endif w = varargin{2}; if (isscalar (w) && isnumeric (w) && isfinite (w) && w > 0 && w <= 1) width = w; depth = w; elseif (numel (w) == 2 && isnumeric (w) && isfinite (w) && all (w > 0) && all (w <= 1)) width = w(1); depth = w(2); else error ("bar3h: invalid value for optional argument 'width'."); endif varargin([1:2]) = []; elseif (strcmpi (tmp, "color")) if (numel (varargin) < 2) error ("bar3h: missing value for optional argument 'color'."); endif c = varargin{2}; if (iscellstr (c)) is_vc = all (cell2mat (cellfun (@(x) any (strcmpi (vc, x)), ... c, "UniformOutput", false))); if (is_vc) color = c; else error ("bar3h: invalid value for optional argument 'color'."); endif elseif (ischar (c) && isvector (c)) if (any (strcmpi (c, vc))) color = c; else error ("bar3h: invalid value for optional argument 'color'."); endif elseif (isnumeric (c)) if (size (c, 2) == 3 && all (c(:) >= 0) && all (c(:) <= 1)) color = c; else error (["bar3h: numeric COLOR must be a 1x3 vector of an Nx3", ... " matrix where each value is between 0 and 1 inclusive."]); endif else error ("bar3h: invalid value for optional argument 'color'."); endif varargin([1:2]) = []; elseif (strcmpi (tmp, "xlabel")) if (numel (varargin) < 2) error ("bar3h: missing value for optional argument 'xlabel'."); endif xlabel = varargin{2}; if (! iscellstr (xlabel)) error ("bar3h: invalid value for optional argument 'xlabel'."); endif varargin([1:2]) = []; elseif (strcmpi (tmp, "zlabel")) if (numel (varargin) < 2) error ("bar3h: missing value for optional argument 'zlabel'."); endif zlabel = varargin{2}; if (! iscellstr (zlabel)) error ("bar3h: invalid value for optional argument 'zlabel'."); endif varargin([1:2]) = []; else error ("bar3h: invalid optional argument."); endif elseif (iscellstr (tmp)) is_vc = all (cell2mat (cellfun (@(x) any (strcmpi (vc, x)), ... tmp, "UniformOutput", false))); if (is_vc) color = tmp; else error ("bar3h: invalid value for optional COLOR argument."); endif varargin(1) = []; else error ("bar3h: invalid optional argument."); endif endwhile ## Get number of column bars from y input [nz, nx] = size (y); ## Check xlabel and zlabel if (! isempty (xlabel) && numel (xlabel) != nx) error ("bar3h: the elements in 'xlabel' must equal the columns in Z."); endif if (! isempty (zlabel) && numel (zlabel) != nz) error ("bar3h: the elements in 'zlabel' must equal the rows in Z."); endif ## Check COLOR for valid dimensions if (isempty (color)) if (nx == 1) cargs = {'FaceColor', 'b'}; elseif (nx == 0) defc = [0,0,1;1,0,1;0,1,0;1,1,0]; fvcd = kron (defc([1:nx],:), ones (6 * nz, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; else fvcd = kron ((1:nx)', ones (6 * nz, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat', ... 'CDataMapping', 'scaled'}; endif elseif (isnumeric (color)) if (size (color, 1) == numel (y)) fvcd = kron (color, ones (6, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; elseif (size (color, 1) == nx) fvcd = kron (color, ones (6 * nz, 1)); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; elseif (size (color, 1) == nz) fvcd = repmat (kron (color, ones (6, 1)), nx, 1); cargs = {'FaceVertexCData', fvcd, 'FaceColor', 'flat'}; elseif (size (color, 1) == 1) cargs = {'FaceVertexCData', color, 'FaceColor', 'flat'}; endif elseif (ischar (color)) cargs = {'FaceColor', color}; elseif (iscellstr (color)) endif ## Construct a "template" column-bar (8 vertices and 6 faces) centered at ## origin, with height = 1, and width along x-axis and depth along z-axis hw = width / 2; hd = depth / 2; ## Scale the bar's base when groupping together if (strcmpi (style, "grouped")) sc = nx + 1; hw = hw / sc; hd = hd / sc; endif [X, Y, Z] = ndgrid ([-hw, hw], [0, 1], [-hd, hd]); V = [X(:), Y(:), Z(:)]; F = [1, 2, 4, 3; 5, 6, 8, 7; 1, 2, 6, 5; 3, 4, 8, 7; 1, 5, 7, 3; 2, 6, 8, 4]; ## Replicate faces to the required number of bars increments = 0:8:8 * (nx * nz - 1); F = bsxfun (@plus, F, permute (increments, [1, 3, 2])); F = reshape (permute (F, [2, 1, 3]), 4, []).'; ## Replicate vertices to the required number of bars [offsetX, offsetZ] = meshgrid (1:nx, 1:nz); offset = [offsetX(:), zeros(numel (y), 1), offsetZ(:)]; #offset(:,3) = 0; V = bsxfun (@plus, V, permute (offset, [3, 2, 1])); V = reshape (permute (V, [2, 1, 3]), 3, []).'; A = NaN; if (strcmpi (style, "detached")) ## Adjust bar heights according to values in y input V(:,2) = V(:,2) .* kron (y(:), ones (8,1)); elseif (strcmpi (style, "grouped")) ## Adjust bar heights according to values in y input V(:,2) = V(:,2) .* kron (y(:), ones (8,1)); ## Move groups along x axis V(:,1) = V(:,1) - kron (kron ([0:nx-1], ones (nz, 1))(:), ones (8,1)); ## Move groups along z axis offset = [-nx+1:2:nx-1] * (hd / width); V(:,3) = V(:,3) + kron (kron (ones (1, nz), offset)(:), ones (8,1)); nx = 1; elseif (strcmpi (style, "stacked")) ## Move groups along x axis V(:,1) = V(:,1) - kron (kron ([0:nx-1], ones (nz, 1))(:), ones (8,1)); ## Adjust bar heights according to values in y input ZC = cumsum (y,2); Q1 = kron (ZC(:), ones (8,1)); ZC(:,end) = []; ZC = [zeros(nz,1), ZC]; Q2 = kron (ZC(:), ones (8,1)); V(:,2) = V(:,2) .* Q1; idx = V(:,2) == 0; V(idx,2) = Q2(idx); ## Collapse x axis nx = 1; endif ## Draw column bars as patches specified by faces/vertices if (new_axes) ax = gca (); endif p = patch ('Faces', F, 'Vertices', V, 'EdgeColor', 'k', 'Parent', ax, cargs{:}); ## Set view port and axes view (ax, 3); grid (ax, 'on'); axis tight; xlim ([0.5, nx+0.5]); zlim ([0.5, nz+0.5]); set (ax, 'XTick', 1:nx, 'ZTick', 1:nz, 'Box', 'off', 'YDir', 'reverse'); ## Fix aspect ratio so that bars appear square when rotating the bar plot if (nx > nz) set (ax, 'PlotBoxAspectRatio', [1, (sqrt(5)-1)/2, nz/nx]); elseif (nx < nz) set (ax, 'PlotBoxAspectRatio', [nx/nz, (sqrt(5)-1)/2, 1]); else set (ax, 'PlotBoxAspectRatio', [1, (sqrt(5)-1)/2, 1]); endif ## Add tick labels in axes (if requested) if (! isempty (xlabel)) set (ax, 'XTickLabel', xlabel); endif if (! isempty (zlabel)) set (ax, 'ZTickLabel', zlabel); elseif (! isempty (z) && ! isnan (z)) zlabel = arrayfun (@(x) sprintf ('%d', x), z, 'UniformOutput', false); set (ax, 'ZTickLabel', zlabel); endif ## Return handle to patch object if requested if nargout > 0 varargout{1} = p; endif endfunction %!demo %! ## Ploting 5 bars in the same series. %! %! y = [50; 40; 30; 20; 10]; %! bar3h (y); %!demo %! ## Ploting 5 bars in different groups. %! %! y = [50, 40, 30, 20, 10]; %! bar3h (y); %!demo %! ## A 3D bar graph with each series corresponding to a column in y. %! %! y = [1, 4, 7; 2, 5, 8; 3, 6, 9; 4, 7, 10]; %! bar3h (y); %!demo %! ## Specify z-axis locations as tick names. z must be a column vector! %! %! z = [1950, 1960, 1970, 1980, 1990]'; %! y = [16, 8, 4, 2, 1]'; %! bar3h (z, y); %!demo %! ## Plot 3 series as a grouped plot without any space between the grouped bars %! %! y = [70 50 33 10; 75 55 35 15; 80 60 40 20]; %! bar3h (y, 1, 'grouped'); %!demo %! ## Plot a stacked style 3D bar graph %! %! y = [19, 30, 21, 30; 40, 16, 32, 12]; %! b = bar3h (y, 0.5, 'stacked'); ## Test input validation %!error bar3h ("A") %!error bar3h ({2,3,4,5}) %!error ... %! bar3h ([1,2,3]', ones (2)) %!error ... %! bar3h ([1:5], 1.2) %!error ... %! bar3h ([1:5]', ones (5), 1.2) %!error ... %! bar3h ([1:5]', ones (5), [0.8, 0.7]) %!error ... %! bar3h (ones (5), 'width') %!error ... %! bar3h (ones (5), 'width', 1.2) %!error ... %! bar3h (ones (5), 'width', [0.8, 0.8, 0.8]) %!error ... %! bar3h (ones (5), 'color') %!error ... %! bar3h (ones (5), 'color', [0.8, 0.8]) %!error ... %! bar3h (ones (5), 'color', "brown") %!error ... %! bar3h (ones (5), 'color', {"r", "k", "c", "m", "brown"}) %!error ... %! bar3h (ones (5), 'xlabel') %!error ... %! bar3h (ones (5), 'xlabel', 4) %!error ... %! bar3h (ones (5), 'zlabel') %!error ... %! bar3h (ones (5), 'zlabel', 4) %!error bar3h (ones (5), 'this', 4) %!error ... %! bar3h (ones (5), 'xlabel', {"A", "B", "C"}) %!error ... %! bar3h (ones (5), 'zlabel', {"A", "B", "C"}) statistics-release-1.7.3/inst/bartlett_test.m000066400000000000000000000217621475240274700213710ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} bartlett_test (@var{x}) ## @deftypefnx {statistics} {@var{h} =} bartlett_test (@var{x}, @var{group}) ## @deftypefnx {statistics} {@var{h} =} bartlett_test (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {@var{h} =} bartlett_test (@var{x}, @var{group}, @var{alpha}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} bartlett_test (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{chisq}] =} bartlett_test (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{chisq}, @var{df}] =} bartlett_test (@dots{}) ## ## Perform a Bartlett test for the homogeneity of variances. ## ## Under the null hypothesis of equal variances, the test statistic @var{chisq} ## approximately follows a chi-square distribution with @var{df} degrees of ## freedom. ## ## The p-value (1 minus the CDF of this distribution at @var{chisq}) is ## returned in @var{pval}. @var{h} = 1 if the null hypothesis is rejected at ## the significance level of @var{alpha}. Otherwise @var{h} = 0. ## ## Input Arguments: ## ## @itemize ## @item ## @var{x} contains the data and it can either be a vector or matrix. ## If @var{x} is a matrix, then each column is treated as a separate group. ## If @var{x} is a vector, then the @var{group} argument is mandatory. ## NaN values are omitted. ## ## @item ## @var{group} contains the names for each group. If @var{x} is a vector, then ## @var{group} must be a vector of the same length, or a string array or cell ## array of strings with one row for each element of @var{x}. @var{x} values ## corresponding to the same value of @var{group} are placed in the same group. ## If @var{x} is a matrix, then @var{group} can either be a cell array of ## strings of a character array, with one row per column of @var{x} in the same ## way it is used in @code{anova1} function. If @var{x} is a matrix, then ## @var{group} can be omitted either by entering an empty array ([]) or by ## parsing only @var{alpha} as a second argument (if required to change its ## default value). ## ## @item ## @var{alpha} is the statistical significance value at which the null ## hypothesis is rejected. Its default value is 0.05 and it can be parsed ## either as a second argument (when @var{group} is omitted) or as a third ## argument. ## @end itemize ## ## @seealso{levene_test, vartest2, vartestn} ## @end deftypefn function [h, pval, chisq, df] = bartlett_test (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 3) error ("bartlett_test: invalid number of input arguments."); endif ## Add defaults group = []; alpha = 0.05; ## Check for 2nd argument being ALPHA or GROUP if (nargin > 1) if (isscalar (varargin{1}) && isnumeric (varargin{1}) ... && numel (varargin{1}) == 1) alpha = varargin{1}; ## Check for valid alpha value if (alpha <= 0 || alpha >= 1) error ("bartlett_test: wrong value for alpha."); endif elseif (isvector (varargin{1}) && numel (varargin{1} > 1)) if ((size (x, 2) == 1 && size (x, 1) == numel (varargin{1})) || ... (size (x, 2) > 1 && size (x, 2) == numel (varargin{1}))) group = varargin{1}; else error ("bartlett_test: GROUP and X mismatch."); endif elseif (isempty (varargin{1})) ## Do nothing else error ("bartlett_test: invalid second input argument."); endif endif ## Check for 3rd argument if (nargin > 2) alpha = varargin{2}; ## Check for valid alpha value if (! isscalar (alpha) || ! isnumeric (alpha) || alpha <= 0 || alpha >= 1) error ("bartlett_test: wrong value for alpha."); endif endif ## Convert group to cell array from character array, make it a column if (! isempty (group) && ischar (group)) group = cellstr (group); endif if (size (group, 1) == 1) group = group'; endif ## If x is a matrix, convert it to column vector and create a ## corresponging column vector for groups if (length (x) < prod (size (x))) [n, m] = size (x); x = x(:); gi = reshape (repmat ((1:m), n, 1), n*m, 1); if (length (group) == 0) ## no group names are provided group = gi; elseif (size (group, 1) == m) ## group names exist and match columns group = group(gi,:); else error ("bartlett_test: columns in X and GROUP length do not match."); endif endif ## Check that x and group are the same size if (! all (numel (x) == numel (group))) error (srtcat (["bartlett_test: GROUP must be a vector with the same"], ... [" number of rows as x."])); endif ## Identify NaN values (if any) and remove them from X along with ## their corresponding values from group vector nonan = ! isnan (x); x = x(nonan); group = group(nonan, :); ## Convert group to indices and separate names [group_id, group_names] = grp2idx (group); group_id = group_id(:); ## Get sample size (n_i) and var (s^2_i) for each group with n_i > 1 groups = size (group_names, 1); rgroup = []; n_i = zeros (1, groups); s_i = n_i; for k = 1:groups group_size = find (group_id == k); if (length (group_size) > 1) n_i(k) = length (group_size); s_i(k) = var (x(group_size)); else warning (strcat (sprintf ("bartlett_test: GROUP %s has a single", ... group_names{k}), [" sample and is not included in the test.\n"])); rgroup = [rgroup, k]; n_i(k) = 1; s_i(k) = NaN; endif endfor ## Remove groups with a single sample if (! isempty (rgroup)) n_i(rgroup) = []; s_i(rgroup) = []; k = k - numel (rgroup); endif ## Compute total sample size (N) and pooled variance (S) N = sum (n_i); S = (1 / (N - k)) * sum ((n_i - 1) .* s_i); ## Calculate B statistic. That is, B ~ X^2(k-1) B_nom = (N - k) * log (S) - sum ((n_i - 1) .* log (s_i)); B_den = 1 + (1 / (3 * (k - 1))) * (sum (1 ./ (n_i - 1)) - (1 / (N - k))); chisq = B_nom / B_den; ## Calculate p-value from the chi-square distribution df = k - 1; pval = 1 - chi2cdf (chisq, df); ## Determine the test outcome h = double (pval < alpha); endfunction ## Test input validation %!error bartlett_test () %!error ... %! bartlett_test (1, 2, 3, 4); %!error bartlett_test (randn (50, 2), 0); %!error ... %! bartlett_test (randn (50, 2), [1, 2, 3]); %!error ... %! bartlett_test (randn (50, 1), ones (55, 1)); %!error ... %! bartlett_test (randn (50, 1), ones (50, 2)); %!error ... %! bartlett_test (randn (50, 2), [], 1.2); %!error ... %! bartlett_test (randn (50, 2), [], "alpha"); %!error ... %! bartlett_test (randn (50, 1), [ones(25, 1); 2*ones(25, 1)], 1.2); %!error ... %! bartlett_test (randn (50, 1), [ones(25, 1); 2*ones(25, 1)], "err"); %!warning ... %! bartlett_test (randn (50, 1), [ones(24, 1); 2*ones(25, 1); 3]); ## Test results %!test %! load examgrades %! [h, pval, chisq, df] = bartlett_test (grades); %! assert (h, 1); %! assert (pval, 7.908647337018238e-08, 1e-14); %! assert (chisq, 38.73324, 1e-5); %! assert (df, 4); %!test %! load examgrades %! [h, pval, chisq, df] = bartlett_test (grades(:,[2:4])); %! assert (h, 1); %! assert (pval, 0.01172, 1e-5); %! assert (chisq, 8.89274, 1e-5); %! assert (df, 2); %!test %! load examgrades %! [h, pval, chisq, df] = bartlett_test (grades(:,[1,4])); %! assert (h, 0); %! assert (pval, 0.88118, 1e-5); %! assert (chisq, 0.02234, 1e-5); %! assert (df, 1); %!test %! load examgrades %! grades = [grades; nan(10, 5)]; %! [h, pval, chisq, df] = bartlett_test (grades(:,[1,4])); %! assert (h, 0); %! assert (pval, 0.88118, 1e-5); %! assert (chisq, 0.02234, 1e-5); %! assert (df, 1); %!test %! load examgrades %! [h, pval, chisq, df] = bartlett_test (grades(:,[2,5]), 0.01); %! assert (h, 0); %! assert (pval, 0.01791, 1e-5); %! assert (chisq, 5.60486, 1e-5); %! assert (df, 1); statistics-release-1.7.3/inst/barttest.m000066400000000000000000000142061475240274700203340ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{ndim} =} barttest (@var{x}) ## @deftypefnx {statistics} {@var{ndim} =} barttest (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{ndim}, @var{pval}] =} barttest (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{ndim}, @var{pval}, @var{chisq}] =} barttest (@var{x}, @var{alpha}) ## ## Bartlett's test of sphericity for correlation. ## ## It compares an observed correlation matrix to the identity matrix in order to ## check if there is a certain redundancy between the variables that we can ## summarize with a few number of factors. A statistically significant test ## shows that the variables (columns) in @var{x} are correlated, thus it makes ## sense to perform some dimensionality reduction of the data in @var{x}. ## ## @code{@var{ndim} = barttest (@var{x}, @var{alpha})} returns the number of ## dimensions necessary to explain the nonrandom variation in the data matrix ## @var{x} at the @var{alpha} significance level. @var{alpha} is an optional ## input argument and, when not provided, it is 0.05 by default. ## ## @code{[@var{ndim}, @var{pval}, @var{chisq}] = barttest (@dots{})} also ## returns the significance values @var{pval} for the hypothesis test for each ## dimension as well as the associated chi^2 values in @var{chisq} ## ## @end deftypefn function [ndim, pval, chisq] = barttest (x, alpha); ## Check for valid number of input arguments if (nargin < 1 || nargin >2) error ("barttest: invalid number of input arguments."); endif ## Check for NaN values in X if (any (isnan( x(:)))) error ("barttest: NaN values in input are not allowed."); endif ## Add default value for alpha if not supplied if (nargin == 1) alpha = 0.05; endif ## Check for valid value of alpha if (! isscalar (alpha) || ! isnumeric (alpha) || alpha <= 0 || alpha >= 1) error ("barttest: wrong value for alpha."); endif ## Check size of data [row, col] = size (x); if (col <= 1 || row <= 1) error ("barttest: not enough data in X."); endif ## Compute the eigenvalues of X in a more efficient way latent = sort ((svd (x - repmat (mean (x, 1), row, 1)) .^ 2) / (row - 1)); ## The degrees of ffredom should be N-1, where N is the sample size row -= 1; k = (0:col - 2)'; pk = col - k; loglatent = flipud (cumsum (log (latent))); ## Compute the chi-square statistic logsum = log (flipud ((latent(1) + cumsum (latent(2:col))) ./ flipud(pk))); chisq = (pk .* logsum - loglatent(1:col - 1)) * row; ## Calculate the degrees of freedom df = (pk - 1) .* (pk + 2) / 2; ## Find the corresponding p-values pval = 1 - chi2cdf (chisq, df); ## Get ndim dim = min (find (pval > alpha)); if (isempty (dim)) ndim = col; return; endif if (dim == 1) ndim = NaN; warning ("barttest: heuristics are violated."); else ndim = dim - 1; endif endfunction ## Test input validation %!error barttest () %!error barttest ([2,NaN;3,4]) %!error barttest (ones (30, 4), "alpha") %!error barttest (ones (30, 4), 0) %!error barttest (ones (30, 4), 1.2) %!error barttest (ones (30, 4), [0.2, 0.05]) %!error barttest (ones (30, 1)) %!error barttest (ones (30, 1), 0.05) ## Test results %!test %! x = [2, 3, 4, 5, 6, 7, 8, 9; 1, 2, 3, 4, 5, 6, 7, 8]'; %! [ndim, pval, chisq] = barttest (x); %! assert (ndim, 2); %! assert (pval, 0); %! ## assert (chisq, 512.0558, 1e-4); Result differs between octave 6 and 7 ? %!test %! x = [0.53767, 0.62702, -0.10224, -0.25485, 1.4193, 1.5237 ; ... %! 1.8339, 1.6452, -0.24145, -0.23444, 0.29158, 0.1634 ; ... %! -2.2588, -2.1351, 0.31286, 0.39396, 0.19781, 0.20995 ; ... %! 0.86217, 1.0835, 0.31286, 0.46499, 1.5877, 1.495 ; ... %! 0.31877, 0.38454, -0.86488, -0.63839, -0.80447, -0.7536 ; ... %! -1.3077, -1.1487, -0.030051, -0.017629, 0.69662, 0.60497 ; ... %! -0.43359, -0.32672, -0.16488, -0.37364, 0.83509, 0.89586 ; ... %! 0.34262, 0.29639, 0.62771, 0.51672, -0.24372, -0.13698 ; ... %! 3.5784, 3.5841, 1.0933, 0.93258, 0.21567, 0.455 ; ... %! 2.7694, 2.6307, 1.1093, 1.4298, -1.1658, -1.1816 ; ... %! -1.3499, -1.2111, -0.86365, -0.94186, -1.148, -1.4381 ; ... %! 3.0349, 2.8428, 0.077359, 0.18211, 0.10487, -0.014613; ... %! 0.7254, 0.56737, -1.2141, -1.2291, 0.72225, 0.90612 ; ... %! -0.063055,-0.17662, -1.1135, -0.97701, 2.5855, 2.4084 ; ... %! 0.71474, 0.29225, -0.0068493, -0.11468, -0.66689, -0.52466 ; ... %! -0.20497, -7.8874e-06, 1.5326, 1.3195, 0.18733, 0.20296 ; ... %! -0.12414, -0.077029, -0.76967, -0.96262, -0.082494, 0.121 ; ... %! 1.4897, 1.3683, 0.37138, 0.43653, -1.933, -2.1903 ; ... %! 1.409, 1.5882, -0.22558, -0.24835, -0.43897, -0.46247 ; ... %! 1.4172, 1.1616, 1.1174, 1.0785, -1.7947, -1.9471 ]; %! [ndim, pval, chisq] = barttest (x); %! assert (ndim, 3); %! assert (pval, [0; 0; 0; 0.52063; 0.34314], 1e-5); %! chisq_out = [251.6802; 210.2670; 153.1773; 4.2026; 2.1392]; %! assert (chisq, chisq_out, 1e-4); statistics-release-1.7.3/inst/binotest.m000066400000000000000000000117651475240274700203420ustar00rootroot00000000000000## Copyright (C) 2016 Andreas Stahel ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{ci}] =} binotest (@var{pos}, @var{N}, @var{p0}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}] =} binotest (@var{pos}, @var{N}, @var{p0}, @var{Name}, @var{Value}) ## ## Test for probability @var{p} of a binomial sample ## ## Perform a test of the null hypothesis @var{p} == @var{p0} for a sample ## of size @var{N} with @var{pos} positive results. ## ## ## Name-Value pair arguments can be used to set various options. ## @qcode{"alpha"} can be used to specify the significance level ## of the test (the default value is 0.05). The option @qcode{"tail"}, ## can be used to select the desired alternative hypotheses. If the ## value is @qcode{"both"} (default) the null is tested against the two-sided ## alternative @code{@var{p} != @var{p0}}. The value of @var{pval} is ## determined by adding the probabilities of all event less or equally ## likely than the observed number @var{pos} of positive events. ## If the value of @qcode{"tail"} is @qcode{"right"} ## the one-sided alternative @code{@var{p} > @var{p0}} is considered. ## Similarly for @qcode{"left"}, the one-sided alternative ## @code{@var{p} < @var{p0}} is considered. ## ## If @var{h} is 0 the null hypothesis is accepted, if it is 1 the null ## hypothesis is rejected. The p-value of the test is returned in @var{pval}. ## A 100(1-alpha)% confidence interval is returned in @var{ci}. ## ## @end deftypefn function [h, p, ci] = binotest(pos,n,p0,varargin) % Set default arguments alpha = 0.05; tail = 'both'; i = 1; while ( i <= length(varargin) ) switch lower(varargin{i}) case 'alpha' i = i + 1; alpha = varargin{i}; case 'tail' i = i + 1; tail = varargin{i}; otherwise error('Invalid Name argument.',[]); end i = i + 1; end if ~isa(tail,'char') error('tail argument to vartest must be a string\n',[]); end if (n<=0) error('binotest: required n>0\n',[]); end if (p0<0)|(p0>1) error('binotest: required 0<= p0 <= 1\n',[]); end if (pos<0)|(pos>n) error('binotest: required 0<= pos <= n\n',[]); end % Based on the "tail" argument determine the P-value, the critical values, % and the confidence interval. switch lower(tail) case 'both' A_low = binoinv(alpha/2,n,p0)/n; A_high = binoinv(1-alpha/2,n,p0)/n; p_pos = binopdf(pos,n,p0); p_all = binopdf([0:n],n,p0); ind = find(p_all <=p_pos); % p = min(1,sum(p_all(ind))); p = sum(p_all(ind)); if pos==0 p_low = 0; else p_low = fzero(@(pl)1-binocdf(pos-1,n,pl)-alpha/2,[0 1]); endif if pos==n p_high = 1; else p_high = fzero(@(ph) binocdf(pos,n,ph) -alpha/2,[0,1]); endif ci = [p_low,p_high]; case 'left' p = 1-binocdf(pos-1,n,p0); if pos==n p_high = 1; else p_high = fzero(@(ph) binocdf(pos,n,ph) -alpha,[0,1]); endif ci = [0, p_high]; case 'right' p = binocdf(pos,n,p0); if pos==0 p_low = 0; else p_low = fzero(@(pl)1-binocdf(pos-1,n,pl)-alpha,[0 1]); endif ci = [p_low 1]; otherwise error('Invalid fifth (tail) argument to binotest\n',[]); end % Determine the test outcome % MATLAB returns this a double instead of a logical array h = double(p < alpha); end %!demo %! % flip a coin 1000 times, showing 475 heads %! % Hypothesis: coin is fair, i.e. p=1/2 %! [h,p_val,ci] = binotest(475,1000,0.5) %! % Result: h = 0 : null hypothesis not rejected, coin could be fair %! % P value 0.12, i.e. hypothesis not rejected for alpha up to 12% %! % 0.444 <= p <= 0.506 with 95% confidence %!demo %! % flip a coin 100 times, showing 65 heads %! % Hypothesis: coin shows less than 50% heads, i.e. p<=1/2 %! [h,p_val,ci] = binotest(65,100,0.5,'tail','left','alpha',0.01) %! % Result: h = 1 : null hypothesis is rejected, i.e. coin shows more heads than tails %! % P value 0.0018, i.e. hypothesis not rejected for alpha up to 0.18% %! % 0 <= p <= 0.76 with 99% confidence %!test #example from https://en.wikipedia.org/wiki/Binomial_test %! [h,p_val,ci] = binotest (51,235,1/6); %! assert (p_val, 0.0437, 0.00005) %! [h,p_val,ci] = binotest (51,235,1/6,'tail','left'); %! assert (p_val, 0.027, 0.0005) statistics-release-1.7.3/inst/boxplot.m000066400000000000000000001045641475240274700202020ustar00rootroot00000000000000## Copyright (C) 2002 Alberto Terruzzi ## Copyright (C) 2006 Alberto Pose ## Copyright (C) 2011 Pascal Dupuis ## Copyright (C) 2012 Juan Pablo Carbajal ## Copyright (C) 2016 Pascal Dupuis ## Copyright (C) 2020 Andreas Bertsatos ## Copyright (C) 2020 Philip Nienhuis (prnienhuis@users.sf.net) ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{s} =} boxplot (@var{data}) ## @deftypefnx {statistics} {@var{s} =} boxplot (@var{data}, @var{group}) ## @deftypefnx {statistics} {@var{s} =} boxplot (@var{data}, @var{notched}, @var{symbol}, @var{orientation}, @var{whisker}, @dots{}) ## @deftypefnx {statistics} {@var{s} =} boxplot (@var{data}, @var{group}, @var{notched}, @var{symbol}, @var{orientation}, @var{whisker}, @dots{}) ## @deftypefnx {statistics} {@var{s} =} boxplot (@var{data}, @var{options}) ## @deftypefnx {statistics} {@var{s} =} boxplot (@var{data}, @var{group}, @var{options}, @dots{}) ## @deftypefnx {statistics} {[@dots{}, @var{h}] =} boxplot (@var{data}, @dots{}) ## ## Produce a box plot. ## ## A box plot is a graphical display that simultaneously describes several ## important features of a data set, such as center, spread, departure from ## symmetry, and identification of observations that lie unusually far from ## the bulk of the data. ## ## Input arguments (case-insensitive) recognized by boxplot are: ## ## @itemize ## @item ## @var{data} is a matrix with one column for each data set, or a cell vector ## with one cell for each data set. Each cell must contain a numerical row or ## column vector (NaN and NA are ignored) and not a nested vector of cells. ## ## @item ## @var{notched} = 1 produces a notched-box plot. Notches represent a robust ## estimate of the uncertainty about the median. ## ## @var{notched} = 0 (default) produces a rectangular box plot. ## ## @var{notched} within the interval (0,1) produces a notch of the specified ## depth. Notched values outside (0,1) are amusing if not exactly impractical. ## ## @item ## @var{symbol} sets the symbol for the outlier values. The default symbol ## for points that lie outside 3 times the interquartile range is 'o'; ## the default symbol for points between 1.5 and 3 times the interquartile ## range is '+'. @* ## Alternative @var{symbol} settings: ## ## @var{symbol} = '.': points between 1.5 and 3 times the IQR are marked with ## '.' and points outside 3 times IQR with 'o'. ## ## @var{symbol} = ['x','*']: points between 1.5 and 3 times the IQR are marked ## with 'x' and points outside 3 times IQR with '*'. ## ## @item ## @var{orientation} = 0 makes the boxes horizontally. @* ## @var{orientation} = 1 plots the boxes vertically (default). Alternatively, ## orientation can be passed as a string, e.g., 'vertical' or 'horizontal'. ## ## @item ## @var{whisker} defines the length of the whiskers as a function of the IQR ## (default = 1.5). If @var{whisker} = 0 then @code{boxplot} displays all data ## values outside the box using the plotting symbol for points that lie ## outside 3 times the IQR. ## ## @item ## @var{group} may be passed as an optional argument only in the second ## position after @var{data}. @var{group} contains a numerical vector defining ## separate categories, each plotted in a different box, for each set of ## @var{DATA} values that share the same @var{group} value or values. With ## the formalism (@var{data}, @var{group}), both must be vectors of the same ## length. ## ## @item ## @var{options} are additional paired arguments passed with the formalism ## (Name, Value) that provide extra functionality as listed below. ## @var{options} can be passed at any order after the initial arguments and ## are case-insensitive. ## ## @multitable {Name} {Value} {description} @columnfractions .2 .2 .6 ## @item 'Notch' @tab 'on' @tab Notched by 0.25 of the boxes width. ## @item @tab 'off' @tab Produces a straight box. ## @item @tab scalar @tab Proportional width of the notch. ## ## @item 'Symbol' @tab '.' @tab Defines only outliers between 1.5 and 3 IQR. ## @item @tab ['x','*'] @tab 2nd character defines outliers > 3 IQR ## ## @item 'Orientation' @tab 'vertical' @tab Default value, can also be defined ## with numerical 1. ## @item @tab 'horizontal' @tab Can also be defined with numerical 0. ## ## @item 'Whisker' @tab scalar @tab Multiplier of IQR (default is 1.5). ## ## @item 'OutlierTags' @tab 'on' or 1 @tab Plot the vector index of the outlier ## value next to its point. ## @item @tab 'off' or 0 @tab No tags are plotted (default value). ## ## @item 'Sample_IDs' @tab 'cell' @tab A cell vector with one cell for each ## data set containing a nested cell vector with each sample's ID (should be ## a string). If this option is passed, then all outliers are tagged with ## their respective sample's ID string instead of their vector's index. ## ## @item 'BoxWidth' @tab 'proportional' @tab Create boxes with their width ## proportional to the number of samples in their respective dataset (default ## value). ## @item @tab 'fixed' @tab Make all boxes with equal width. ## ## @item 'Widths' @tab scalar @tab Scaling factor for box widths (default ## value is 0.4). ## ## @item 'CapWidths' @tab scalar @tab Scaling factor for whisker cap widths ## (default value is 1, which results to 'Widths'/8 halflength) ## ## @item 'BoxStyle' @tab 'outline' @tab Draw boxes as outlines (default value). ## @item @tab 'filled' @tab Fill boxes with a color (outlines are still ## plotted). ## ## @item 'Positions' @tab vector @tab Numerical vector that defines the ## position of each data set. It must have the same length as the number of ## groups in a desired manner. This vector merely defines the points along ## the group axis, which by default is [1:number of groups]. ## ## @item 'Labels' @tab cell @tab A cell vector of strings containing the names ## of each group. By default each group is labeled numerically according to ## its order in the data set ## ## @item 'Colors' @tab character string or Nx3 numerical matrix @tab If just ## one character or 1x3 vector of RGB values, specify the fill color of all ## boxes when BoxStyle = 'filled'. If a character string or Nx3 matrix is ## entered, box #1's fill color corrresponds to the first character or first ## matrix row, and the next boxes' fill colors corresponds to the next ## characters or rows. If the char string or Nx3 array is exhausted the color ## selection wraps around. ## @end multitable ## @end itemize ## ## Supplemental arguments not described above (@dots{}) are concatenated and ## passed to the plot() function. ## ## The returned matrix @var{s} has one column for each data set as follows: ## ## @multitable @columnfractions .1 .8 ## @item 1 @tab Minimum ## @item 2 @tab 1st quartile ## @item 3 @tab 2nd quartile (median) ## @item 4 @tab 3rd quartile ## @item 5 @tab Maximum ## @item 6 @tab Lower confidence limit for median ## @item 7 @tab Upper confidence limit for median ## @end multitable ## ## The returned structure @var{h} contains handles to the plot elements, ## allowing customization of the visualization using set/get functions. ## ## Example ## ## @example ## title ("Grade 3 heights"); ## axis ([0,3]); ## set(gca (), "xtick", [1 2], "xticklabel", @{"girls", "boys"@}); ## boxplot (@{randn(10,1)*5+140, randn(13,1)*8+135@}); ## @end example ## ## @end deftypefn function [s_o, hs_o] = boxplot (data, varargin) ## Assign parameter defaults if (nargin < 1) print_usage; endif ## Check data if (! (isnumeric (data) || iscell (data))) error ("boxplot: numerical array or cell array containing data expected."); elseif (iscell (data)) ## Check if cell contain numerical data if (! all (cellfun ("isnumeric", data))) error ("boxplot: data cells must contain numerical data."); endif endif ## Default values maxwhisker = 1.5; orientation = 1; symbol = ["+", "o"]; notched = 0; plot_opts = {}; groups = []; sample_IDs = {}; outlier_tags = 0; box_width = "proportional"; widths = 0.4; capwid = 1; box_style = 0; positions = []; labels = {}; nug = 0; bcolor = "y"; ## Optional arguments analysis numarg = nargin - 1; indopt = 1; group_exists = 0; while (numarg) dummy = varargin{indopt++}; if ((! ischar (dummy) || iscellstr (dummy)) && indopt < 6) ## MATLAB allows passing the second argument as a grouping vector if (length (dummy) > 1) if (2 != indopt) error ("boxplot: grouping vector may only be passed as second arg."); endif if (isnumeric (dummy)) groups = dummy; group_exists = 1; else error ("boxplot: grouping vector must be numerical"); endif elseif (length (dummy) == 1) ## Old way: positional argument switch indopt - group_exists case 2 notched = dummy; case 4 orientation = dummy; case 5 maxwhisker = dummy; otherwise error("boxplot: no positional argument allowed at position %d", ... --indopt); endswitch endif numarg--; continue; else if (3 == (indopt - group_exists) && length (dummy) <= 2) symbol = dummy; numarg--; continue; else ## Check for additional paired arguments switch lower (dummy) case "notch" notched = varargin{indopt}; ## Check for string input: "on" or "off" if (ischar (notched)) if (strcmpi (notched, "on")) notched = 1; elseif (strcmpi (notched, "off")) notched = 0; else msg = ["boxplot: 'Notch' input argument accepts only 'on',", ... " 'off' or a numeric scalar as value"]; error (msg); endif elseif (! (isnumeric (notched) && isreal (notched))) error ("boxplot: illegal Notch value"); endif case "symbol" symbol = varargin{indopt}; if (! ischar (symbol)) error ("boxplot; Symbol(s) must be character(s)"); endif case "orientation" orientation = varargin{indopt}; if (ischar (orientation)) ## Check for string input: "vertical" or "horizontal" if (strcmpi (orientation, "vertical")) orientation = 1; elseif (strcmpi (orientation, "horizontal")) orientation = 0; else msg = ["boxplot: 'Orientation' input argument accepts only", ... " 'vertical' (or 1) or 'horizontal' (or 0) as value"]; error (msg); endif elseif (! (isnumeric (orientation) && isreal (orientation))) error ("boxplot: illegal Orientation value"); endif case "whisker" maxwhisker = varargin{indopt}; if (! isscalar (maxwhisker) || ... ! (isnumeric (maxwhisker) && isreal (maxwhisker))) msg = ["boxplot: 'Whisker' input argument accepts only", ... " a real scalar value as input parameter"]; error(msg); endif case "outliertags" outlier_tags = varargin{indopt}; ## Check for string input: "on" or "off" if (ischar (outlier_tags)) if (strcmpi (outlier_tags, "on")) outlier_tags = 1; elseif (strcmpi (outlier_tags, "off")) outlier_tags = 0; else msg = ["boxplot: 'OutlierTags' input argument accepts only", ... " 'on' (or 1) or 'off' (or 0) as value"]; error (msg); endif elseif (! (isnumeric (outlier_tags) && isreal (outlier_tags))) error ("boxplot: illegal OutlierTags value"); endif case "sample_ids" sample_IDs = varargin{indopt}; if (! iscell (sample_IDs)) msg = ["boxplot: 'Sample_IDs' input argument accepts only", ... " a cell array as value"]; error (msg); endif outlier_tags = 1; case "boxwidth" box_width = varargin{indopt}; ## Check for string input: "fixed" or "proportional" if (! ischar (box_width) || ... ! ismember (lower (box_width), {"fixed", "proportional"})) msg = ["boxplot: 'BoxWidth' input argument accepts only", ... " 'fixed' or 'proportional' as value"]; error (msg); endif box_width = lower (box_width); case "widths" widths = varargin{indopt}; if (! isscalar (widths) || ! (isnumeric (widths) && isreal (widths))) msg = ["boxplot: 'Widths' input argument accepts only", ... " a real scalar value as value"]; error (msg); endif case "capwidths" capwid = varargin{indopt}; if (! isscalar (capwid) || ! (isnumeric (capwid) && isreal (capwid))) msg = ["boxplot: 'CapWidths' input argument accepts only", ... " a real scalar value as value"]; error (msg); endif case "boxstyle" box_style = varargin{indopt}; ## Check for string input: "outline" or "filled" if (! ischar (box_style) || ... ! ismember (lower (box_style), {"outline", "filled"})) msg = ["boxplot: 'BoxStyle' input argument accepts only", ... " 'outline' or 'filled' as value"]; error (msg); endif box_style = lower (box_style); case "positions" positions = varargin{indopt}; if (! isvector (positions) || ! isnumeric (positions)) msg = ["boxplot: 'Positions' input argument accepts only", ... " a numeric vector as value"]; error (msg); endif case "labels" labels = varargin{indopt}; if (! iscellstr (labels)) msg = ["boxplot: 'Labels' input argument accepts only", ... " a cellstr array as value"]; error (msg); endif case "colors" bcolor = varargin{indopt}; if (! (ischar (bcolor) || ... (isnumeric (bcolor) && size (bcolor, 2) == 3))) msg = ["boxplot: 'Colors' input argument accepts only", ... " a character (string) or Nx3 numeric array as value"]; error (msg); endif otherwise ## Take two args and append them to plot_opts plot_opts(1, end+1:end+2) = {dummy, varargin{indopt}}; endswitch endif numarg -= 2; indopt++; endif endwhile if (1 == length (symbol)) symbol(2) = symbol(1); endif if (1 == notched) notched = 0.25; endif a = 1-notched; ## Figure out how many data sets we have if (isempty (groups)) if (iscell (data)) nc = nug = length (data); for ind_c = (1:nc) lc(ind_c) = length (data{ind_c}); endfor else if (isvector (data)) data = data(:); endif nc = nug = columns (data); lc = ones (1, nc) * rows (data); endif groups = (1:nc); ## In case sample_IDs exists. check that it has same size as data if (! isempty (sample_IDs) && length (sample_IDs) == 1) for ind_c = (1:nc) if (lc(ind_c) != length (sample_IDs)) error ("boxplot: Sample_IDs must match the data"); endif endfor elseif (! isempty (sample_IDs) && length (sample_IDs) == nc) for ind_c = (1:nc) if (lc(ind_c) != length (sample_IDs{ind_c})) error ("boxplot: Sample_IDs must match the data"); endif endfor elseif (! isempty (sample_IDs) && length (sample_IDs) != nc) error ("boxplot: Sample_IDs must match the data"); endif ## Create labels according to number of datasets as ordered in data ## in case they are not provided by the user as optional argument if (isempty (labels)) for i = 1:nc column_label = num2str (groups(i)); labels(i) = {column_label}; endfor endif else if (! isvector (data)) error ("boxplot: with the formalism (data, group), both must be vectors"); endif ## If sample IDs given, check that their size matches the data if (! isempty (sample_IDs)) if (length (sample_IDs) != 1 || length (sample_IDs{1}) != length (data)) error ("boxplot: Sample_IDs must match the data"); endif nug = unique (groups); dummy_data = cell (1, length (nug)); dummy_sIDs = cell (1, length (nug)); ## Check if groups are parsed as a numeric vector if (isnumeric (groups)) for ind_c = (1:length (nug)) dummy_data(ind_c) = data(groups == nug(ind_c)); dummy_sIDs(ind_c) = {sample_IDs{1}(groups == nug(ind_c))}; endfor ## Create labels according to unique numeric groups in case ## they are not provided by the user as optional argument if (isempty (labels)) for i = 1:nug column_label = num2str (groups(i)); labels(i) = {column_label}; endfor endif ## Check if groups are parsed as a cell string vector elseif iscellstr (groups) for ind_c = (1:length (nug)) dummy_data(ind_c) = data(ismember (group, nug(ind_c))); dummy_sIDs(ind_c) = {sample_IDs{1}(ismember (group, nug(ind_c)))}; endfor ## Create labels according to unique cell string groups in case ## they are not provided by the user as optional argument if (isempty (labels)) labels = nug; endif else error ("boxplot: group argument must be numeric or cell string vector"); endif data = dummy_data; groups = nug(:).'; nc = length (nug); sample_IDs = dummy_sIDs; else nug = unique (groups); dummy_data = cell (1, length (nug)); ## Check if groups are parsed as a numeric vector if (isnumeric (groups)) for ind_c = (1:length (nug)) dummy_data(ind_c) = data(groups == nug(ind_c)); endfor ## Create labels according to unique numeric groups in case ## they are not provided by the user as optional argument if (isempty (labels)) for i = 1:nug column_label = num2str (groups(i)); labels(i) = {column_label}; endfor endif ## Check if groups are parsed as a cell string vector elseif (iscellstr (groups)) for ind_c = (1:length (nug)) dummy_data(ind_c) = data(ismember (group, nug(ind_c))); endfor ## Create labels according to unique cell string groups in case ## they are not provided by the user as optional argument if (isempty (labels)) labels = nug; endif else error ("boxplot: group argument must be numeric vector or cell string"); endif data = dummy_data; nc = length (nug); if (iscell (groups)) groups = [1:nc]; else groups = nug(:).'; endif endif endif ## Compute statistics. ## s will contain ## 1,5 min and max ## 2,3,4 1st, 2nd and 3rd quartile ## 6,7 lower and upper confidence intervals for median s = zeros (7, nc); box = zeros (1, nc); ## Arrange the boxes into desired positions (if requested, otherwise leave ## default 1:nc) if (! isempty (positions)) groups = positions; endif ## Initialize whisker matrices to correct size and all necessary outlier ## variables whisker_x = ones (2, 1) * [groups, groups]; whisker_y = zeros (2, 2 * nc); outliers_x = []; outliers_y = []; outliers_idx = []; outliers_IDs = {}; outliers2_x = []; outliers2_y = []; outliers2_idx = []; outliers2_IDs = {}; for indi = (1:nc) ## Get the next data set from the array or cell array if (iscell (data)) col = data{indi}(:); if (!isempty (sample_IDs)) sIDs = sample_IDs{indi}; else sIDs = num2cell([1:length(col)]); endif else col = data(:, indi); sIDs = num2cell([1:length(col)]); endif ## Skip missing data (NaN, NA) and remove respective sample IDs. ## Do this only on nonempty data if (length (col) > 0) remove_samples = find (isnan (col) | isna (col)); if (length (remove_samples) > 0) col(remove_samples) = []; sIDs(remove_samples) = []; endif endif ## Remember data length nd = length (col); box(indi) = nd; if (nd > 1) ## Min, max and quartiles s(1:5, indi) = statistics (col)(1:5); ## Confidence interval for the median est = 1.57 * (s(4, indi) - s(2, indi)) / sqrt (nd); s(6, indi) = max ([s(3, indi) - est, s(2, indi)]); s(7, indi) = min ([s(3, indi) + est, s(4, indi)]); ## Whiskers out to the last point within the desired inter-quartile range IQR = maxwhisker * (s(4, indi) - s(2, indi)); whisker_y(:, indi) = [min(col(col >= s(2, indi) - IQR)); s(2, indi)]; whisker_y(:, nc+indi) = [max(col(col <= s(4, indi) + IQR)); s(4, indi)]; ## Outliers beyond 1 and 2 inter-quartile ranges outliers = col((col < s(2, indi) - IQR & col >= s(2, indi) - 2 * IQR) | ... (col > s(4, indi) + IQR & col <= s(4, indi) + 2 * IQR)); outliers2 = col(col < s(2, indi) - 2 * IQR | col > s(4, indi) + 2 * IQR); ## Get outliers indices from this dataset if (length (outliers) > 0) for out_i = 1:length (outliers) outliers_idx = [outliers_idx; (find (col == outliers(out_i)))]; outliers_IDs = {outliers_IDs{:}, sIDs{(find (col == outliers(out_i)))}}; endfor endif if (length (outliers2) > 0) for out_i = 1:length (outliers2) outliers2_idx = [outliers2_idx; find(col == outliers2(out_i))]; outliers2_IDs = {outliers2_IDs{:}, sIDs{find(col == outliers2(out_i))}}; endfor endif outliers_x = [outliers_x; (groups(indi) * ones (size (outliers)))]; outliers_y = [outliers_y; outliers]; outliers2_x = [outliers2_x; (groups(indi) * ones (size (outliers2)))]; outliers2_y = [outliers2_y; outliers2]; elseif (1 == nd) ## All statistics collapse to the value of the point s(:, indi) = col; ## Single point data sets are plotted as outliers. outliers_x = [outliers_x; groups(indi)]; outliers_y = [outliers_y; col]; ## Append the single point's index to keep the outliers' vector aligned outliers_idx = [outliers_idx; 1]; outliers_IDs = {outliers_IDs{:}, sIDs{:}}; else ## No statistics if no points s(:, indi) = NaN; endif endfor ## Note which boxes don't have enough stats chop = find (box <= 1); ## Replicate widths (if scalar or shorter vector) to match the number of boxes widths = widths(repmat (1:length (widths), 1, nc)); ## Truncate just in case :) widths([nc+1:end]) = []; ## Draw a box around the quartiles, with box width being fixed or proportional ## to the number of items in the box. if (strcmpi (box_width, "proportional")) box = box .* (widths ./ max (box)); else box = box .* (widths ./ box); endif ## Draw notches if desired. quartile_x = ones (11, 1) * groups + ... [-a; -1; -1; 1 ; 1; a; 1; 1; -1; -1; -a] * box; quartile_y = s([3, 7, 4, 4, 7, 3, 6, 2, 2, 6, 3], :); ## Draw a line through the median median_x = ones (2, 1) * groups + [-a; +a] * box; median_y = s([3, 3], :); ## Chop all boxes which don't have enough stats quartile_x(:, chop) = []; quartile_y(:, chop) = []; whisker_x(:, [chop, chop + nc]) = []; whisker_y(:, [chop, chop + nc]) = []; median_x(:, chop) = []; median_y(:, chop) = []; box(chop) = []; ## Add caps to the remaining whiskers cap_x = whisker_x; if (strcmpi (box_width, "proportional")) cap_x(1, :) -= repmat (((capwid * box .* (widths ./ max (box))) / 8), 1, 2); cap_x(2, :) += repmat (((capwid * box .* (widths ./ max (box))) / 8), 1, 2); else cap_x(1, :) -= repmat ((capwid * widths / 8), 1, 2); cap_x(2, :) += repmat ((capwid * widths / 8), 1, 2); endif cap_y = whisker_y([1, 1], :); ## Calculate coordinates for outlier tags outliers_tags_x = outliers_x + 0.08; outliers_tags_y = outliers_y; outliers2_tags_x = outliers2_x + 0.08; outliers2_tags_y = outliers2_y; ## Do the plot hold_status = ishold (); if (orientation) ## Define outlier_tags' vertical alignment outlier_tags_alignment = {"horizontalalignment", "left"}; if (box_style) f = fillbox (quartile_x, quartile_y, bcolor); endif h = plot (quartile_x, quartile_y, "b;;", whisker_x, whisker_y, "b;;", cap_x, cap_y, "b;;", median_x, median_y, "r;;", outliers_x, outliers_y, [symbol(1), "r;;"], outliers2_x, outliers2_y, [symbol(2), "r;;"], plot_opts{:}); ## Print outlier tags if (outlier_tags == 1 && outliers_x > 0) t1 = plot_tags (outliers_tags_x, outliers_tags_y, outliers_idx, outliers_IDs, sample_IDs, outlier_tags_alignment); endif if (outlier_tags == 1 && outliers2_x > 0) t2 = plot_tags (outliers2_tags_x, outliers2_tags_y, outliers2_idx, outliers2_IDs, sample_IDs, outlier_tags_alignment); endif else ## Define outlier_tags' horizontal alignment outlier_tags_alignment = {"horizontalalignment", "left", "rotation", 90}; if (box_style) f = fillbox (quartile_y, quartile_x, bcolor); endif h = plot (quartile_y, quartile_x, "b;;", whisker_y, whisker_x, "b;;", cap_y, cap_x, "b;;", median_y, median_x, "r;;", outliers_y, outliers_x, [symbol(1), "r;;"], outliers2_y, outliers2_x, [symbol(2), "r;;"], plot_opts{:}); ## Print outlier tags if (outlier_tags == 1 && outliers_x > 0) t1 = plot_tags (outliers_tags_y, outliers_tags_x, outliers_idx, outliers_IDs, sample_IDs, outlier_tags_alignment); endif if (outlier_tags == 1 && outliers2_x > 0) t2 = plot_tags (outliers2_tags_y, outliers2_tags_x, outliers2_idx, outliers2_IDs, sample_IDs, outlier_tags_alignment); endif endif ## Distribute handles for box outlines and box fill (if any) nq = 1 : size (quartile_x, 2); hs.box = h(nq); if (box_style) nf = 1 : length (groups); hs.box_fill = f(nf); else hs.box_fill = []; endif ## Distribute handles for whiskers (including caps) and median lines nw = nq(end) + [1 : 2 * (size (whisker_x, 2))]; hs.whisker = h(nw); nm = nw(end)+ [1 : (size (median_x, 2))]; hs.median = h(nm); ## Distribute handles for outliers (if any) and their respective tags ## (if applicable) no = nm; if (! isempty (outliers_y)) no = nm(end) + [1 : size(outliers_y, 2)]; hs.outliers = h(no); if (outlier_tags == 1) nt = 1 : length (outliers_tags_y); hs.out_tags = t1(nt); else hs.out_tags = []; endif else hs.outliers = []; hs.out_tags = []; endif ## Distribute handles for extreme outliers (if any) and their respective tags ## (if applicable) if (! isempty (outliers2_y)) no2 = no(end) + [1 : size(outliers2_y, 2)]; hs.outliers2 = h(no2); if (outlier_tags == 1) nt2 = 1 : length (outliers2_tags_y); hs.out_tags2 = t2(nt2); else hs.out_tags2 = []; endif else hs.outliers2 = []; hs.out_tags2 = []; end ## Redraw the median lines to avoid colour overlapping in case of 'filled' ## BoxStyle if (box_style) set (hs.median, "color", "r"); endif ## Print labels according to orientation and return handle if (orientation) set (gca(), "xtick", groups, "xticklabel", labels); hs.labels = get (gcf, "currentaxes"); else set (gca(), "ytick", groups, "yticklabel", labels); hs.labels = get (gcf, "currentaxes"); endif ## Retain original ishold status if (! hold_status) hold off; endif ## Return output arguments if desired if (nargout >= 1) s_o = s; endif if (nargout == 2) hs_o = hs; endif endfunction function htags = plot_tags (out_tags_x, out_tags_y, out_idx, out_IDs, ... sample_IDs, opt) for i=1 : length (out_tags_x) if (! isempty (sample_IDs)) htags(i) = text (out_tags_x(i), out_tags_y(i), out_IDs{i}, opt{:}); else htags(i) = text (out_tags_x(i), out_tags_y(i), num2str (out_idx(i)), ... opt{:}); endif endfor endfunction function f = fillbox (quartile_y, quartile_x, bcolor) f = []; for icol = 1 : columns (quartile_x) if (ischar (bcolor)) f = [ f; fill(quartile_y(:, icol), quartile_x(:, icol), ... bcolor(mod (icol-1, numel (bcolor))+1)) ]; else f = [ f; fill(quartile_y(:, icol), quartile_x(:, icol), ... bcolor(mod (icol-1, size (bcolor, 1))+1, :)) ]; endif hold on; endfor endfunction %!demo %! axis ([0, 3]); %! randn ("seed", 1); # for reproducibility %! girls = randn (10, 1) * 5 + 140; %! randn ("seed", 2); # for reproducibility %! boys = randn (13, 1) * 8 + 135; %! boxplot ({girls, boys}); %! set (gca (), "xtick", [1 2], "xticklabel", {"girls", "boys"}) %! title ("Grade 3 heights"); %!demo %! randn ("seed", 7); # for reproducibility %! A = randn (10, 1) * 5 + 140; %! randn ("seed", 8); # for reproducibility %! B = randn (25, 1) * 8 + 135; %! randn ("seed", 9); # for reproducibility %! C = randn (20, 1) * 6 + 165; %! data = [A; B; C]; %! groups = [(ones (10, 1)); (ones (25, 1) * 2); (ones (20, 1) * 3)]; %! labels = {"Team A", "Team B", "Team C"}; %! pos = [2, 1, 3]; %! boxplot (data, groups, "Notch", "on", "Labels", labels, "Positions", pos, ... %! "OutlierTags", "on", "BoxStyle", "filled"); %! title ("Example of Group splitting with paired vectors"); %!demo %! randn ("seed", 1); # for reproducibility %! data = randn (100, 9); %! boxplot (data, "notch", "on", "boxstyle", "filled", ... %! "colors", "ygcwkmb", "whisker", 1.2); %! title ("Example of different colors specified with characters"); %!demo %! randn ("seed", 5); # for reproducibility %! data = randn (100, 13); %! colors = [0.7 0.7 0.7; ... %! 0.0 0.4 0.9; ... %! 0.7 0.4 0.3; ... %! 0.7 0.1 0.7; ... %! 0.8 0.7 0.4; ... %! 0.1 0.8 0.5; ... %! 0.9 0.9 0.2]; %! boxplot (data, "notch", "on", "boxstyle", "filled", ... %! "colors", colors, "whisker", 1.3, "boxwidth", "proportional"); %! title ("Example of different colors specified as RGB values"); ## Input data validation %!error boxplot ("a") %!error boxplot ({[1 2 3], "a"}) %!error boxplot ([1 2 3], 1, {2, 3}) %!error boxplot ([1 2 3], {"a", "b"}) %!error <'Notch' input argument accepts> boxplot ([1:10], "notch", "any") %!error boxplot ([1:10], "notch", i) %!error boxplot ([1:10], "notch", {}) %!error boxplot (1, "symbol", 1) %!error <'Orientation' input argument accepts only> boxplot (1, "orientation", "diagonal") %!error boxplot (1, "orientation", {}) %!error <'Whisker' input argument accepts only> boxplot (1, "whisker", "a") %!error <'Whisker' input argument accepts only> boxplot (1, "whisker", [1 3]) %!error <'OutlierTags' input argument accepts only> boxplot (3, "OutlierTags", "maybe") %!error boxplot (3, "OutlierTags", {}) %!error <'Sample_IDs' input argument accepts only> boxplot (1, "sample_IDs", 1) %!error <'BoxWidth' input argument accepts only> boxplot (1, "boxwidth", 2) %!error <'BoxWidth' input argument accepts only> boxplot (1, "boxwidth", "anything") %!error <'Widths' input argument accepts only> boxplot (5, "widths", "a") %!error <'Widths' input argument accepts only> boxplot (5, "widths", [1:4]) %!error <'Widths' input argument accepts only> boxplot (5, "widths", []) %!error <'CapWidths' input argument accepts only> boxplot (5, "capwidths", "a") %!error <'CapWidths' input argument accepts only> boxplot (5, "capwidths", [1:4]) %!error <'CapWidths' input argument accepts only> boxplot (5, "capwidths", []) %!error <'BoxStyle' input argument accepts only> boxplot (1, "Boxstyle", 1) %!error <'BoxStyle' input argument accepts only> boxplot (1, "Boxstyle", "garbage") %!error <'Positions' input argument accepts only> boxplot (1, "positions", "aa") %!error <'Labels' input argument accepts only> boxplot (3, "labels", [1 5]) %!error <'Colors' input argument accepts only> boxplot (1, "colors", {}) %!error <'Colors' input argument accepts only> boxplot (2, "colors", [1 2 3 4]) %!error boxplot (randn (10, 3), 'Sample_IDs', {"a", "b"}) %!error boxplot (rand (3, 3), [1 2]) ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! [a, b] = boxplot (rand (10, 3)); %! assert (size (a), [7, 3]); %! assert (numel (b.box), 3); %! assert (numel (b.whisker), 12); %! assert (numel (b.median), 3); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! [~, b] = boxplot (rand (10, 3), "BoxStyle", "filled", "colors", "ybc"); %! assert (numel (b.box_fill), 3); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! hold on %! [a, b] = boxplot (rand (10, 3)); %! assert (ishold, true); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect statistics-release-1.7.3/inst/canoncorr.m000066400000000000000000000064411475240274700204720ustar00rootroot00000000000000## Copyright (C) 2016-2019 by Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{A}, @var{B}, @var{r}, @var{U}, @var{V}] =} canoncorr (@var{X}, @var{Y}) ## ## Canonical correlation analysis. ## ## Given @var{X} (size @var{k}*@var{m}) and @var{Y} (@var{k}*@var{n}), returns ## projection matrices of canonical coefficients @var{A} (size @var{m}*@var{d}, ## where @var{d} is the smallest of @var{m}, @var{n}, @var{d}) and @var{B} ## (size @var{m}*@var{d}); the canonical correlations @var{r} (1*@var{d}, ## arranged in decreasing order); the canonical variables @var{U}, @var{V} ## (both @var{k}*@var{d}, with orthonormal columns); and @var{stats}, ## a structure containing results from Bartlett's chi-square and Rao's F tests ## of significance. ## ## @seealso{princomp} ## @end deftypefn function [A,B,r,U,V,stats] = canoncorr (X,Y) k = size (X, 1); # should also be size (Y, 1) m = size (X, 2); n = size (Y, 2); d = min ([k m n]); X = center (X); Y = center (Y); [Qx Rx] = qr (X, 0); [Qy Ry] = qr (Y, 0); [U S V] = svd (Qx' * Qy, "econ"); A = Rx \ U(:, 1:d); B = Ry \ V(:, 1:d); ## A, B are scaled to make the covariance matrices of the outputs U, V ## identity matrices f = sqrt (k-1); A .*= f; B .*= f; if (nargout > 2) r = max(0, min(diag(S), 1))'; endif if (nargout > 3) U = X * A; endif if (nargout > 4) V = Y * B; endif if (nargout > 5) Wilks = fliplr(cumprod(fliplr((1 - r .^ 2)))); chisq = - (k - 1 - (m + n + 1)/2) * log(Wilks); df1 = (m - (1:d) + 1) .* (n - (1:d) + 1); pChisq = 1 - chi2cdf (chisq, df1); s = sqrt((df1.^2 - 4) ./ ((m - (1:d) + 1).^2 + (n - (1:d) + 1).^2 - 5)); df2 = (k - 1 - (m + n + 1)/2) * s - df1/2 + 1; ls = Wilks .^ (1 ./ s); F = (1 ./ ls - 1) .* (df2 ./ df1); pF = 1 - fcdf (F, df1, df2); stats.Wilks = Wilks; stats.df1 = df1; stats.df2 = df2; stats.F = F; stats.pF = pF; stats.chisq = chisq; stats.pChisq = pChisq; endif endfunction %!shared X,Y,A,B,r,U,V,k %! k = 10; %! X = [1:k; sin(1:k); cos(1:k)]'; Y = [tan(1:k); tanh((1:k)/k)]'; %! [A,B,r,U,V,stats] = canoncorr (X,Y); %!assert (A, [-0.329229 0.072908; 0.074870 1.389318; -0.069302 -0.024109], 1E-6); %!assert (B, [-0.017086 -0.398402; -4.475049 -0.824538], 1E-6); %!assert (r, [0.99590 0.26754], 1E-5); %!assert (U, center(X) * A, 10*eps); %!assert (V, center(Y) * B, 10*eps); %!assert (cov(U), eye(size(U, 2)), 10*eps); %!assert (cov(V), eye(size(V, 2)), 10*eps); %! rand ("state", 1); [A,B,r] = canoncorr (rand(5, 10),rand(5, 20)); %!assert (r, ones(1, 5), 10*eps); statistics-release-1.7.3/inst/cdfcalc.m000066400000000000000000000057571475240274700200760ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{yCDF}, @var{xCDF}, @var{n}, @var{emsg}, @var{eid}] =} cdfcalc (@var{x}) ## ## Calculate an empirical cumulative distribution function. ## ## @code{[@var{yCDF}, @var{xCDF}] = cdfcalc (@var{x})} calculates an empirical ## cumulative distribution function (CDF) of the observations in the data sample ## vector @var{x}. @var{x} may be a row or column vector, and represents a ## random sample of observations from some underlying distribution. On return ## @var{xCDF} is the set of @var{x} values at which the CDF increases. ## At XCDF(i), the function increases from YCDF(i) to YCDF(i+1). ## ## @code{[@var{yCDF}, @var{xCDF}, @var{n}] = cdfcalc (@var{x})} also returns ## @var{n}, the sample size. ## ## @code{[@var{yCDF}, @var{xCDF}, @var{n}, @var{emsg}, @var{eid}] = cdfcalc ## (@var{x})} also returns an error message and error id if @var{x} is not a ## vector or if it contains no values other than NaN. ## ## @seealso{cdfplot} ## @end deftypefn function [yCDF, xCDF, n, emsg, eid] = cdfcalc (x) ## Check number of input and output argument narginchk (1,1); nargoutchk (2,5); ## Add defaults yCDF = []; xCDF = []; n = 0; ## Check that x is a vector if (! isvector (x)) warning ("cdfcalc: vector required as input."); emsg = "VectorRequired"; eid = "VectorRequired"; return endif ## Remove NaNs and check if there are remaining data to calculate ecdf x = x(! isnan (x)); n = length (x); if (n == 0) warning ("cdfcalc: not enough data."); emsg = "NotEnoughData"; eid = "NotEnoughData"; return endif ## Sort data in ascending order x = sort (x(:)); ## Get cumulative sums yCDF = (1:n)' / n; ## Remove duplicates, keep the last one keep_idx = ([diff(x(:)); 1] > 0); xCDF = x(keep_idx); yCDF = [0; yCDF(keep_idx)]; emsg = ''; eid = ''; endfunction %!test %! x = [2, 4, 3, 2, 4, 3, 2, 5, 6, 4]; %! [yCDF, xCDF, n, emsg, eid] = cdfcalc (x); %! assert (yCDF, [0, 0.3, 0.5, 0.8, 0.9, 1]'); %! assert (xCDF, [2, 3, 4, 5, 6]'); %! assert (n, 10); %!shared x %! x = [2, 4, 3, 2, 4, 3, 2, 5, 6, 4]; %!error yCDF = cdfcalc (x); %!error [yCDF, xCDF] = cdfcalc (); %!error [yCDF, xCDF] = cdfcalc (x, x); %!warning [yCDF, xCDF] = cdfcalc (ones(10,2)); statistics-release-1.7.3/inst/cdfplot.m000066400000000000000000000074071475240274700201440ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{hCDF} =} cdfplot (@var{x}) ## @deftypefnx {statistics} {[@var{hCDF}, @var{stats}] =} cdfplot (@var{x}) ## ## Display an empirical cumulative distribution function. ## ## @code{@var{hCDF} = cdfplot (@var{x})} plots an empirical cumulative ## distribution function (CDF) of the observations in the data sample vector ## @var{x}. @var{x} may be a row or column vector, and represents a random ## sample of observations from some underlying distribution. ## ## @code{cdfplot} plots F(x), the empirical (or sample) CDF versus the ## observations in @var{x}. The empirical CDF, F(x), is defined as follows: ## ## F(x) = (Number of observations <= x) / (Total number of observations) ## ## for all values in the sample vector @var{x}. NaNs are ignored. @var{hCDF} ## is the handle of the empirical CDF curve (a handle hraphics 'line' object). ## ## @code{[@var{hCDF}, @var{stats}] = cdfplot (@var{x})} also returns a structure ## with the following fields as a statistical summary. ## ## @multitable @columnfractions 0.05 0.3 0.65 ## @item @tab STATS.min @tab minimum value of @var{x} ## @item @tab STATS.max @tab maximum value of @var{x} ## @item @tab STATS.mean @tab sample mean of @var{x} ## @item @tab STATS.median @tab sample median (50th percentile) of @var{x} ## @item @tab STATS.std @tab sample standard deviation of @var{x} ## @end multitable ## ## @seealso{qqplot, cdfcalc} ## @end deftypefn function [hCDF, stats] = cdfplot (x) ## Check number of input arguments narginchk (1,1); ## Calculate sample cdf [yy, xx, ~, ~, eid] = cdfcalc (x); ## Check for errors returned from cdfcalc if (strcmpi (eid, "VectorRequired")) error ("cdfplot: vector required as input."); elseif (strcmpi (eid, "NotEnoughData")) error("cdfplot: not enough data."); endif ## Create vectors for plotting k = length (xx); n = reshape (repmat (1:k, 2, 1), 2*k, 1); xCDF = [-Inf; xx(n); Inf]; yCDF = [0; 0; yy(1+n)]; ## Plot cdf h = plot (xCDF, yCDF); grid ('on') xlabel ("x") ylabel ("F(x)") title ("CDF plot of x"); ## Return requested output arguments if (nargout > 0) hCDF = h; endif if (nargout > 1) stats.min = nanmin (x); stats.max = nanmax (x); stats.mean = mean (x, "omitnan"); stats.median = median (x, "omitnan"); stats.std = std (x, "omitnan"); endif endfunction %!demo %! x = randn(100,1); %! cdfplot (x); ## Test results %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [2, 4, 3, 2, 4, 3, 2, 5, 6, 4]; %! [hCDF, stats] = cdfplot (x); %! assert (stats.min, 2); %! assert (stats.max, 6); %! assert (stats.median, 3.5); %! assert (stats.std, 1.35400640077266, 1e-14); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = randn(100,1); %! cdfplot (x); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error cdfplot (); %!error cdfplot ([x',x']); %!error cdfplot ([NaN, NaN, NaN, NaN]); statistics-release-1.7.3/inst/chi2gof.m000066400000000000000000000413061475240274700200260ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} chi2gof (@var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{p}] =} chi2gof (@var{x}) ## @deftypefnx {statistics} {[@var{p}, @var{h}, @var{stats}] =} chi2gof (@var{x}) ## @deftypefnx {statistics} {[@dots{}] =} chi2gof (@var{x}, @var{Name}, @var{Value}, @dots{}) ## ## Chi-square goodness-of-fit test. ## ## @code{chi2gof} performs a chi-square goodness-of-fit test for discrete or ## continuous distributions. The test is performed by grouping the data into ## bins, calculating the observed and expected counts for those bins, and ## computing the chi-square test statistic ## @tex ## $$ \chi ^ 2 = \sum_{i=1}^N \left (O_i - E_i \right) ^ 2 / E_i $$ ## @end tex ## @ifnottex ## SUM((O-E).^2./E), ## @end ifnottex ## where O is the observed counts and E is the expected counts. This test ## statistic has an approximate chi-square distribution when the counts are ## sufficiently large. ## ## Bins in either tail with an expected count less than 5 are pooled with ## neighboring bins until the count in each extreme bin is at least 5. If ## bins remain in the interior with counts less than 5, @code{chi2gof} displays ## a warning. In that case, you should use fewer bins, or provide bin centers ## or binedges, to increase the expected counts in all bins. ## ## @code{@var{h} = chi2gof (@var{x})} performs a chi-square goodness-of-fit test ## that the data in the vector X are a random sample from a normal distribution ## with mean and variance estimated from @var{x}. The result is @var{h} = 0 if ## the null hypothesis (that @var{x} is a random sample from a normal ## distribution) cannot be rejected at the 5% significance level, or @var{h} = 1 ## if the nullhypothesis can be rejected at the 5% level. @code{chi2gof} uses ## by default 10 bins (@qcode{"nbins"}), and compares the test statistic to a ## chi-square distribution with @qcode{@var{nbins} - 3} degrees of freedom, to ## take into account that two parameters were estimated. ## ## @code{[@var{h}, @var{p}] = chi2gof (@var{x})} also returns the p-value @var{p}, ## which is the probability of observing the given result, or one more extreme, ## by chance if the null hypothesis is true. If there are not enough degrees of ## freedom to carry out the test, @var{p} is NaN. ## ## @code{[@var{h}, @var{p}, @var{stats}] = chi2gof (@var{x})} also returns a ## @var{stats} structure with the following fields: ## ## @multitable @columnfractions 0.05 0.3 0.65 ## @item @tab "chi2stat" @tab Chi-square statistic ## @item @tab "df" @tab Degrees of freedom ## @item @tab "binedges" @tab Vector of bin binedges after pooling ## @item @tab "O" @tab Observed count in each bin ## @item @tab "E" @tab Expected count in each bin ## @end multitable ## ## @code{[@dots{}] = chi2gof (@var{x}, @var{Name}, @var{Value}, @dots{})} ## specifies optional Name/Value pair arguments chosen from the following list. ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"nbins"} @tab The number of bins to use. Default is 10. ## @item @tab @qcode{"binctrs"} @tab A vector of bin centers. ## @item @tab @qcode{"binedges"} @tab A vector of bin binedges. ## @item @tab @qcode{"cdf"} @tab A fully specified cumulative distribution ## function or a function handle provided in a cell array whose first element is ## a function handle, and all later elements are its parameter values. The ## function must take @var{x} values as its first argument, and other parameters ## as later arguments. ## @item @tab @qcode{"expected"} @tab A vector with one element per bin ## specifying the expected counts for each bin. ## @item @tab @qcode{"nparams"} @tab The number of estimated parameters; used to ## adjust the degrees of freedom to be @qcode{@var{nbins} - 1 - @var{nparams}}, ## where @var{nbins} is the number of bins. ## @item @tab @qcode{"emin"} @tab The minimum allowed expected value for a bin; ## any bin in either tail having an expected value less than this amount is ## pooled with a neighboring bin. Use the value 0 to prevent pooling. Default ## is 5. ## @item @tab @qcode{"frequency"} @tab A vector of the same length as @var{x} ## containing the frequency of the corresponding @var{x} values. ## @item @tab @qcode{"alpha"} @tab An @var{alpha} value such that the hypothesis ## is rejected if @qcode{@var{p} < @var{alpha}}. Default is ## @qcode{@var{alpha} = 0.05}. ## @end multitable ## ## You should specify either @qcode{"cdf"} or @qcode{"expected"} parameters, but ## not both. If your @qcode{"cdf"} input contains extra parameters, these are ## accounted for automatically and there is no need to specify @qcode{"nparams"}. ## If your @qcode{"expected"} input depends on estimated parameters, you should ## use the @qcode{"nparams"} parameter to ensure that the degrees of freedom for ## the test is correct. ## ## @end deftypefn function [h, p, stats] = chi2gof (x, varargin) ## Check imput arguments if (nargin < 1) error ("chi2gof: At least one imput argument is required."); endif if (! isvector(x) || ! isreal(x)) error ("chi2gof: X must ba a vector of real numbers."); endif ## Add initial parameters nbins = []; binctrs = []; binedges = []; cdf_spec = []; expected = []; nparams = []; emin = 5; frequency = []; alpha = 0.05; ## Parse additional arguments numarg = nargin - 1; argpos = 1; while (numarg) argname = varargin{argpos}; switch (lower (argname)) case "nbins" nbins = varargin{argpos + 1}; case "ctrs" binctrs = varargin{argpos + 1}; case "edges" binedges = varargin{argpos + 1}; case "cdf" cdf_spec = varargin{argpos + 1}; case "expected" expected = varargin{argpos + 1}; case "nparams" nparams = varargin{argpos + 1}; case "emin" emin = varargin{argpos + 1}; case "frequency" frequency = varargin{argpos + 1}; case "alpha" alpha = varargin{argpos + 1}; endswitch numarg -= 2; argpos += 2; endwhile ## Check additional arguments for errors if ((! isempty (nbins) + ! isempty (binctrs) + ! isempty (binedges)) > 1) error ("chi2gof: Inconsistent Arguments."); endif if ((! isempty (cdf_spec) + ! isempty (expected)) > 1) error ("chi2gof: Conflicted Arguments."); endif if (! isempty (frequency)) if (! isvector (frequency) || numel (frequency) != numel (x)) error ("chi2gof: X and Frequency vectors mismatch."); endif if (any (frequency < 0)) error ("chi2gof: Frequency vector contains negative numbers."); endif endif if (! isscalar (emin) || emin < 0 || emin != round (emin) || ! isreal (emin)) error("chi2gof: 'emin' must be a positive integer."); endif if (! isempty (nparams)) if (! isscalar (nparams) || nparams < 0 || nparams != round (nparams) ... || ! isreal (nparams)) error ("chi2gof: Wrong number of parameters."); endif endif if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("chi2gof: Wrong value of alpha."); endif ## Make X a column vector x = x(:); ## Parse or create a frequeny vector if (isempty (frequency)) frequency = ones (size (x)); else frequency = frequency(:); endif ## Remove NaNs if any remove_NaNs = isnan (frequency) | isnan (x); if (any (remove_NaNs)) x(remove_NaNs) = []; frequency(remove_NaNs) = []; endif ## Check for bin numbers, centers, or edges and calculate bins accordingly if (! isempty (binctrs)) [Observed, binedges] = calculatebins (x, frequency, "ctrs", binctrs); elseif (! isempty (binedges)) [Observed, binedges] = calculatebins (x, frequency, "edges", binedges); else if (isempty (nbins)) if (isempty (expected)) nbins = 10; ## default number of bins else nbins = length (expected); ## determined by Expected vector endif endif [Observed, binedges] = calculatebins (x, frequency, "nbins", nbins); endif Observed = Observed(:); nbins = length (Observed); ## Calculate expected vector cdfargs = {}; if (! isempty (expected)) ## Provided as input argument if (! isvector (expected) || numel (expected) != nbins) error ("chi2gof: Expected counts vector is the wrong size."); endif if (any (expected < 0)) error ("chi2gof: Expected counts vector has negative values."); endif Expected = expected(:); else ## Calculate from the cdf if (isempty (cdf_spec)) ## Use estimated normal as default cdffunc = @normcdf; sumfreq = sum (frequency); mu = sum (x.*frequency)/sumfreq; sigma = sqrt (sum ((x.*frequency - mu) .^ 2) / (sumfreq-1)); cdfargs = {mu, sigma}; if (isempty (nparams)) nparams = 2; endif elseif (isa (cdf_spec, "function_handle")) ## Split function handle to get function name and optional parameters cstr = ostrsplit (func2str (cdf_spec), ","); ## Simple function handle, no parameters: e.g. @normcdf if (isempty (strfind (cstr, "@")) && numel (cstr) == 1) cdffunc = str2func (char (strcat ("@", cstr))); if (isempty (nparams)) nparams = numel (cdfargs); endif ## Complex function handle, no parameters: e.g. @(x) normcdf(x) elseif (! isempty (strfind (cstr, "@")) && numel (cstr) == 1) ## Remove white spaces cstr = char (cstr); cstr(strfind (cstr, " ")) = []; ## Remove input argument in parentheses while (length (strfind (cstr,"("))) cstr(index (cstr, "("):index (cstr, ")")) = []; endwhile cdffunc = str2func (cstr); if (isempty (nparams)) nparams = numel (cdfargs); endif elseif (! isempty (strfind (cstr, "@")) && numel (cstr) > 1) ## Evaluate function name in first cell cstr_f = char (cstr(1)); cstr_f(strfind (cstr_f, " ")) = []; cstr_f(index (cstr_f, "("):index (cstr_f, ")")) = []; cstr_f(index (cstr_f, "("):end) = []; cdffunc = str2func (cstr_f); ## Evaluate optional parameters in remaining cells cstr_idx = 2; while (cstr_idx <= numel (cstr)) cstr_p = char (cstr(cstr_idx)); cstr_p(strfind (cstr_p, " ")) = []; ## Check for numerical value if (isscalar (str2num (cstr_p))) cdfargs{cstr_idx - 1} = cstr_p; else ## Get function handle: e.g. mean cstr_p(index (cstr_p, "("):end) = []; cdfargs{cstr_idx - 1} = feval (str2func (cstr_p), x .* frequency); cstr_idx += 1; endif endwhile if (isempty (nparams)) nparams = numel (cdfargs); endif endif elseif (iscell (cdf_spec)) % Get function and args from cell array cdffunc = cdf_spec{1}; cdfargs = cdf_spec(2:end); if (isempty (nparams)) nparams = numel(cdfargs); endif endif if (! is_function_handle (cdffunc)) error ("chi2gof: Poorly specified cumulative distribution function."); else cdfname = func2str (cdffunc); endif ## Calculate only inner bins, since tail probabilitiyis included in the ## calculation of expected counts for the first and last bins interioredges = binedges(2:end-1); ## Compute the cumulative probabilities Fcdf = feval (cdffunc, interioredges, cdfargs{:}); if (! isvector(Fcdf) || numel (Fcdf) != (nbins - 1)) msg = sprintf("chi2gof: Wrong number of outputs from: %s\n", cdfname); error (msg); endif % Compute the expected values Expected = sum(Observed) * diff([0;Fcdf(:);1]); endif ## Avoid too small expected values if (any (Expected < emin)) [Expected, Observed, binedges] = poolbins (Expected, Observed, binedges, emin); nbins = length (Expected); end ## Compute test statistic cstat = sum(((Observed - Expected) .^ 2) ./ Expected); ## Calculate degrees of freedom if (isempty (nparams)) nparams = 0; endif df = nbins - 1 - nparams; if (df > 0) p = 1 - chi2cdf (cstat, df); else df = 0; p = NaN; endif h = cast (p <= alpha, "double"); ## Create 3rd output argument if necessary if (nargout > 2) stats.chi2stat = cstat; stats.df = df; stats.edges = binedges; stats.O = Observed'; stats.E = Expected'; endif endfunction function [Expected, Observed, binedges] = poolbins (Expected, ... Observed, binedges, emin) i = 1; j = length(Expected); while (i < j - 1 && (Expected(i) < emin || Expected(i + 1) < emin || ... Expected(j) < emin || Expected(j - 1) < emin)) if (Expected(i) < Expected(j)) Expected(i+1) = Expected(i+1) + Expected(i); Observed(i+1) = Observed(i+1) + Observed(i); i = i + 1; else Expected(j-1) = Expected(j-1) + Expected(j); Observed(j-1) = Observed(j-1) + Observed(j); j = j - 1; endif endwhile ## Keep only pooled bins Expected = Expected(i:j); Observed = Observed(i:j); binedges(j+1:end-1) = []; binedges(2:i) = []; endfunction function [Observed, binedges] = calculatebins (x, frequency, binspec, specval) lo = double (min (x(:))); hi = double (max (x(:))); ## Check binspec for bin count, bin centers, or bin edges. switch (binspec) case "nbins" nbins = specval; if (isempty (x)) lo = 0; hi = 1; endif if (lo == hi) lo = lo - floor (nbins / 2) - 0.5; hi = hi + ceil (nbins / 2) - 0.5; endif binwidth = (hi - lo) ./ nbins; binedges = lo + binwidth * (0:nbins); binedges(length (binedges)) = hi; case "ctrs" binctrs = specval(:)'; binwidth = diff (binctrs); binwidth = [binwidth binwidth(end)]; binedges = [binctrs(1)-binwidth(1)/2 binctrs+binwidth/2]; case "edges" binedges = specval(:)'; endswitch ## Update bins nbins = length (binedges) - 1; ## Calculate bin numbers if (isempty (x)) binnum = x; elseif (! isequal (binspec, "edges")) binedges = binedges + eps(binedges); [ignore, binnum] = histc (x, [-Inf binedges(2:end-1) Inf]); else [ignore, binnum] = histc (x, binedges); binnum(binnum == nbins + 1) = nbins; end ## Remove empty bins if (any (binnum == 0)) frequency(binnum == 0) = []; binnum(binnum == 0) = []; end ## Compute Observed vector binnum = binnum(:); Observed = accumarray ([ones(size(binnum)), binnum], frequency, [1, nbins]); endfunction %!demo %! x = normrnd (50, 5, 100, 1); %! [h, p, stats] = chi2gof (x) %! [h, p, stats] = chi2gof (x, "cdf", @(x)normcdf (x, mean(x), std(x))) %! [h, p, stats] = chi2gof (x, "cdf", {@normcdf, mean(x), std(x)}) %!demo %! x = rand (100,1 ); %! n = length (x); %! binedges = linspace (0, 1, 11); %! expectedCounts = n * diff (binedges); %! [h, p, stats] = chi2gof (x, "binedges", binedges, "expected", expectedCounts) %!demo %! bins = 0:5; %! obsCounts = [6 16 10 12 4 2]; %! n = sum(obsCounts); %! lambdaHat = sum(bins.*obsCounts) / n; %! expCounts = n * poisspdf(bins,lambdaHat); %! [h, p, stats] = chi2gof (bins, "binctrs", bins, "frequency", obsCounts, ... %! "expected", expCounts, "nparams",1) ## Test input validation %!error chi2gof () %!error chi2gof ([2,3;3,4]) %!error chi2gof ([1,2,3,4], "nbins", 3, "ctrs", [2,3,4]) %!error chi2gof ([1,2,3,4], "frequency", [2,3,2]) %!error chi2gof ([1,2,3,4], "frequency", [2,3,2,-2]) %!error chi2gof ([1,2,3,4], "frequency", [2,3,2,2], "nparams", i) %!error chi2gof ([1,2,3,4], "frequency", [2,3,2,2], "alpha", 1.3) %!error chi2gof ([1,2,3,4], "expected", [-3,2,2]) %!error chi2gof ([1,2,3,4], "expected", [3,2,2], "nbins", 5) %!error chi2gof ([1,2,3,4], "cdf", @normcdff) %!test %! x = [1 2 1 3 2 4 3 2 4 3 2 2]; %! [h, p, stats] = chi2gof (x); %! assert (h, 0); %! assert (p, NaN); %! assert (stats.chi2stat, 0.1205375022748029, 1e-14); %! assert (stats.df, 0); %! assert (stats.edges, [1, 2.5, 4], 1e-14); %! assert (stats.O, [7, 5], 1e-14); %! assert (stats.E, [6.399995519909668, 5.600004480090332], 1e-14); statistics-release-1.7.3/inst/chi2test.m000066400000000000000000000417621475240274700202400ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pval} =} chi2test (@var{x}) ## @deftypefnx {statistics} {[@var{pval}, @var{chisq}] =} chi2test (@var{x}) ## @deftypefnx {statistics} {[@var{pval}, @var{chisq}, @var{dF}] =} chi2test (@var{x}) ## @deftypefnx {statistics} {[@var{pval}, @var{chisq}, @var{dF}, @var{E}] =} chi2test (@var{x}) ## @deftypefnx {statistics} {[@dots{}] =} chi2test (@var{x}, @var{name}, @var{value}) ## ## Perform a chi-squared test (for independence or homogeneity). ## ## For 2-way contingency tables, @code{chi2test} performs and a chi-squared test ## for independence or homogeneity, according to the sampling scheme and related ## question. Independence means that the the two variables forming the 2-way ## table are not associated, hence you cannot predict from one another. ## Homogeneity refers to the concept of similarity, hence they all come from the ## same distribution. ## ## Both tests are computationally identical and will produce the same result. ## Nevertheless, they anwser to different questions. Consider two variables, ## one for gender and another for smoking. To test independence (whether gender ## and smoking is associated), we would randomly sample from the general ## population and break them down into categories in the table. To test ## homogeneity (whether men and women share the same smoking habits), we would ## sample individuals from within each gender, and then measure their smoking ## habits (e.g. smokers vs non-smokers). ## ## When @code{chi2test} is called without any output arguments, it will print ## the result in the terminal including p-value, chi^2 statistic, and degrees of ## freedom. Otherwise it can return the following output arguments: ## ## @multitable @columnfractions 0.05 0.1 0.85 ## @item @tab @var{pval} @tab the p-value of the relevant test. ## @item @tab @var{chisq} @tab the chi^2 statistic of the relevant test. ## @item @tab @var{dF} @tab the degrees of freedom of the relevant test. ## @item @tab @var{E} @tab the EXPECTED values of the original contigency table. ## @end multitable ## ## Unlike MATLAB, in GNU Octave @code{chi2test} also supports 3-way tables, ## which involve three categorical variables (each in a different dimension of ## @var{x}. In its simplest form, @code{[@dots{}] = chi2test (@var{x})} will ## will test for mutual independence among the three variables. Alternatively, ## when called in the form @code{[@dots{}] = chi2test (@var{x}, @var{name}, ## @var{value})}, it can perform the following tests: ## ## @multitable @columnfractions 0.2 0.1 0.7 ## @headitem @var{name} @tab @var{value} @tab Description ## @item "mutual" @tab [] @tab Mutual independence. All variables are ## independent from each other, (A, B, C). Value must be an empty matrix. ## @item "joint" @tab scalar @tab Joint independence. Two variables are jointly ## independent of the third, (AB, C). The scalar value corresponds to the ## dimension of the independent variable (i.e. 3 for C). ## @item "marginal" @tab scalar @tab Marginal independence. Two variables are ## independent if you ignore the third, (A, C). The scalar value corresponds ## to the dimension of the variable to be ignored (i.e. 2 for B). ## @item "conditional" @tab scalar @tab Conditional independence. Two variables ## are independent given the third, (AC, BC). The scalar value corresponds to ## the dimension of the variable that forms the conditional dependence ## (i.e. 3 for C). ## @item "homogeneous" @tab [] @tab Homogeneous associations. Conditional ## (partial) odds-ratios are not related on the value of the third, ## (AB, AC, BC). Value must be an empty matrix. ## @end multitable ## ## When testing for homogeneous associations in 3-way tables, the iterative ## proportional fitting procedure is used. For small samples it is better to ## use the Cochran-Mantel-Haenszel Test. K-way tables for k > 3 are supported ## only for testing mutual independence. Similar to 2-way tables, no optional ## parameters are required for k > 3 multi-way tables. ## ## @code{chi2test} produces a warning if any cell of a 2x2 table has an expected ## frequency less than 5 or if more than 20% of the cells in larger 2-way tables ## have expected frequencies less than 5 or any cell with expected frequency ## less than 1. In such cases, use @code{fishertest}. ## ## @seealso{crosstab, fishertest, mcnemar_test} ## @end deftypefn function [pval, chisq, df, E] = chi2test (x, varargin) ## Check input arguments if (nargin < 1) print_usage (); endif if (isvector (x)) error ("chi2test: X must be a matrix."); endif if (! isreal (x)) error ("chi2test: values in X must be real numbers."); endif if (any (isnan (x(:)))) error ("chi2test: X must not have missing values (NaN)."); endif ## Get size and dimensions of contigency table sz = size (x); dim = length (sz); ## Check optional arguments if (dim == 2 && nargin > 1) error ("chi2test: optional arguments are not supported for 2-way tables."); endif if (dim == 3 && mod (numel (varargin(:)), 2) != 0) error ("chi2test: optional arguments must be in pairs."); endif if (dim == 3 && nargin > 1 && ! isnumeric (varargin{2})) error (strcat (["chi2test: value must be numeric in optional argument"], ... [" name/value pair, for 3-way tables."])); endif if (dim == 3 && nargin > 1 && numel (varargin{2}) > 1) error (strcat (["chi2test: value must be empty or scalar in optional"], ... [" argument name/value pair, for 3-way tables."])); endif if (dim >= 4 && nargin > 1) error ("chi2test: optional arguments are not supported for k>3."); endif ## Calculate total sample size n = sum (x(:)); ## For 2-way contigency table if (length (sz) == 2) ## Calculate degrees of freedom df = prod (sz - 1); ## Calculate expected values E = sum (x')' * sum (x) / n; ## For 3-way contigency table elseif (length (sz) == 3) ## Check optional arguments if (nargin == 1 || strcmpi (varargin{1}, "mutual")) ## Calculate degrees of freedom df = prod (sz) - sum (sz) + 2; ## Calculate marginal table sums q1 = sum (sum (x, 2), 3); q2 = sum (sum (x, 1), 3); q3 = sum (sum (x, 1), 2); ns = sum (x(:)) ^ 2; for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = q1(d1,:,:) * q2(:,d2,:) * q3(:,:,d3) / ns; endfor endfor endfor elseif (strcmpi (varargin{1}, "joint")) ## Get dimension of independent variable (dim) c_dim = varargin{2}; ## Calculate degrees of freedom c_sz = sz; c_sz(c_dim) = []; df = (sz(c_dim) - 1) * (prod (c_sz) - 1); ## Rearrange dimensions so that independent variable goes in dim 1 dm = [1, 2, 3]; dm(c_dim) = []; x = permute (x, [c_dim, dm]); ## Calculate partial table sums q1 = sum (sum (x, 1), 1); q2 = sum (sum (x, 2), 3); n = sum (x(:)); for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = q1(:,d2,d3) * q2(d1) / n; endfor endfor endfor ## Rearrange OBSERVED and EXPECTED matrices in original dimensions x = permute (x, [c_dim, dm]); x = permute (x, [c_dim, dm]); E = permute (E, [c_dim, dm]); E = permute (E, [c_dim, dm]); elseif (strcmpi (varargin{1}, "marginal")) ## Get dimension of marginal variable (dim) c_dim = varargin{2}; ## Calculate degrees of freedom c_sz = sz; c_sz(c_dim) = []; df = prod (sz) - sum (c_sz) + 1; ## Rearrange dimensions so that marginal variable goes in dim 1 dm = [1, 2, 3]; dm(c_dim) = []; x = permute (x, [c_dim, dm]); ## Calculate partial table sums q1 = sum (sum (x, 1), 3); q2 = sum (sum (x, 1), 2); n2 = sz(c_dim) * sum (x(:)); ## Calculate expected values for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = q1(:,d2) * q2(:,:,d3) / n2; endfor endfor endfor ## Rearrange OBSERVED and EXPECTED matrices in original dimensions x = permute (x, [c_dim, dm]); E = permute (E, [c_dim, dm]); elseif (strcmpi (varargin{1}, "conditional")) ## Get dimension of conditional variable (dim) c_dim = varargin{2}; ## Calculate degrees of freedom c_sz = sz; c_sz(c_dim) = []; df = prod (c_sz - 1) * sz(c_dim); ## Rearrange dimensions so that conditional variable goes in dim 1 dm = [1, 2, 3]; dm(c_dim) = []; x = permute (x, [c_dim, dm]); ## Calculate partial table sums q1 = sum (sum (x, 3), 3); q2 = sum (sum (x, 2), 2); q3 = sum (sum (x, 2), 3); ## Calculate expected values for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = q1(d1,d2) * q2(d1,:,d3) / q3(d1); endfor endfor endfor ## Rearrange OBSERVED and EXPECTED matrices in original dimensions x = permute (x, [c_dim, dm]); x = permute (x, [c_dim, dm]); E = permute (E, [c_dim, dm]); E = permute (E, [c_dim, dm]); elseif (strcmpi (varargin{1}, "homogeneous")) ## Calculate degrees of freedom df = prod(sz - 1); ## Compute observed marginal totals for any two dimensions omt12 = sum (sum (x, 3), 3); omt13 = sum (sum (x, 2), 2); omt23 = sum (sum (x, 1), 1); ## Produce initial seed 3-way table S = ones (sz); ## Calculate initial expected marginal totals emt12 = sum (sum (S, 3), 3); emt13 = sum (sum (S, 2), 2); emt23 = sum (sum (S, 1), 1); ## Compute difference to converge within certain tolerance or iterations OEdiff = sum (omt12(:) - emt12(:)) + sum (omt13(:) - emt13(:)) + ... sum (omt23(:) - emt23(:)); iter = 1; tol = 1e-6; ## Start Iterative Proportional Fitting Procedure while (OEdiff > tol || iter > 50) ## Rows x Columns for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = S(d1,d2,d3) * omt12(d1,d2) / emt12(d1,d2); endfor endfor endfor ## Update seed and recalculate Rows x Layers expected marginal totals S = E; emt13 = sum (sum (S, 2), 2); ## Rows x Layers for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = S(d1,d2,d3) * omt13(d1,:,d3) / emt13(d1,:,d3); endfor endfor endfor ## Update seed and recalculate Columns x Layers expected marginal totals S = E; emt23 = sum (sum (S, 1), 1); ## Columns x Layers for d1 = 1:size (x, 1) for d2 = 1:size (x, 2) for d3 = 1:size (x, 3) E(d1,d2,d3) = S(d1,d2,d3) * omt23(:,d2,d3) / emt23(:,d2,d3); endfor endfor endfor ## Update seed and recalculate Rows x Layers expected marginal totals S = E; emt12 = sum (sum (S, 3), 3); ## Update difference between OBSERVED and EXPECTED tables OEdiff = sum (omt12(:) - emt12(:)) + sum (omt13(:) - emt13(:)) + ... sum (omt23(:) - emt23(:)); iter += 1; endwhile else error ("chi2test: invalid model name for testing a 3-way table."); endif ## For k-way contigency table, where k > 3 else ## Calculate degrees of freedom df = prod (sz) - sum (sz) + 2; ## Calculate squared sample size ns = sum (x(:)) ^ (dim - 1); ## Calculate marginal table sums for each available dimension for i = 1:dim qi(i) = {x}; remdim = [1:dim]; remdim(remdim == i) = []; for j = 1:length (remdim) qi(i) = sum (qi{i}, remdim(j)); endfor qi(i) = squeeze (qi{i}); endfor ## Iterate through all cells cn = numel (x); for i = 1:cn E(i) = 1; cid = i; ## Keep track of indexing for d = dim - 1:-1:1 idx(d+1) = ceil (cid / prod (sz(1:d))); if (idx(d+1) > 1) cid -= (idx(d+1) - 1) * prod (sz(1:d)); endif endfor idx(1) = cid; ## Calculate the expected value for j = 1:dim E(i) = E(i) * qi{j}(idx(j)); endfor E(i) = E(i) / ns; endfor ## Reshape to original dimensions E = reshape (E, sz); endif ## Check expected values and display warnings if ((dim == 2 && isequal (sz, [2, 2]) && any (E(:) < 5)) || ... (dim == 2 && any (sz > 2) && sum (E(:) < 5) > 0.2 * numel (E)) || ... (dim > 2 && sum (E(:) < 5) > 0.2 * numel (E))) warning ("chi2test: Expected values less than 5."); endif if (any (E(:) < 1)) warning ("chi2test: Expected values less than 1."); endif ## Calculate chi-squared and p-value cells = ((x - E) .^2) ./ E; chisq = sum (cells(:)); pval = 1 - chi2cdf (chisq, df); ## Print results if no output requested if (nargout == 0) printf ("p-val = %f with chi^2 statistic = %f and d.f. = %d.\n", ... pval, chisq, df); endif endfunction ## Input validation tests %!error chi2test (); %!error chi2test ([1, 2, 3, 4, 5]); %!error chi2test ([1, 2; 2, 1+3i]); %!error chi2test ([NaN, 6; 34, 12]); %!error ... %! p = chi2test (ones (3, 3), "mutual", []); %!error ... %! p = chi2test (ones (3, 3, 3), "testtype", 2); %!error ... %! p = chi2test (ones (3, 3, 3), "mutual"); %!error ... %! p = chi2test (ones (3, 3, 3), "joint", ["a"]); %!error ... %! p = chi2test (ones (3, 3, 3), "joint", [2, 3]); %!error ... %! p = chi2test (ones (3, 3, 3, 4), "mutual", []) ## Check warning %!warning p = chi2test (ones (2)); %!warning p = chi2test (ones (3, 2)); %!warning p = chi2test (0.4 * ones (3)); ## Output validation tests %!test %! x = [11, 3, 8; 2, 9, 14; 12, 13, 28]; %! p = chi2test (x); %! assert (p, 0.017787, 1e-6); %!test %! x = [11, 3, 8; 2, 9, 14; 12, 13, 28]; %! [p, chisq] = chi2test (x); %! assert (chisq, 11.9421, 1e-4); %!test %! x = [11, 3, 8; 2, 9, 14; 12, 13, 28]; %! [p, chisq, df] = chi2test (x); %! assert (df, 4); %!test %!shared x %! x(:,:,1) = [59, 32; 9,16]; %! x(:,:,2) = [55, 24;12,33]; %! x(:,:,3) = [107,80;17,56];%! %!assert (chi2test (x), 2.282063427117009e-11, 1e-14); %!assert (chi2test (x, "mutual", []), 2.282063427117009e-11, 1e-14); %!assert (chi2test (x, "joint", 1), 1.164834895206468e-11, 1e-14); %!assert (chi2test (x, "joint", 2), 7.771350230001417e-11, 1e-14); %!assert (chi2test (x, "joint", 3), 0.07151361728026107, 1e-14); %!assert (chi2test (x, "marginal", 1), 0, 1e-14); %!assert (chi2test (x, "marginal", 2), 6.347555814301131e-11, 1e-14); %!assert (chi2test (x, "marginal", 3), 0, 1e-14); %!assert (chi2test (x, "conditional", 1), 0.2303114201312508, 1e-14); %!assert (chi2test (x, "conditional", 2), 0.0958810684407079, 1e-14); %!assert (chi2test (x, "conditional", 3), 2.648037344954446e-11, 1e-14); %!assert (chi2test (x, "homogeneous", []), 0.4485579470993741, 1e-14); %!test %! [pval, chisq, df, E] = chi2test (x); %! assert (chisq, 64.0982, 1e-4); %! assert (df, 7); %! assert (E(:,:,1), [42.903, 39.921; 17.185, 15.991], ones (2, 2) * 1e-3); %!test %! [pval, chisq, df, E] = chi2test (x, "joint", 2); %! assert (chisq, 56.0943, 1e-4); %! assert (df, 5); %! assert (E(:,:,2), [40.922, 23.310; 38.078, 21.690], ones (2, 2) * 1e-3); %!test %! [pval, chisq, df, E] = chi2test (x, "marginal", 3); %! assert (chisq, 146.6058, 1e-4); %! assert (df, 9); %! assert (E(:,1,1), [61.642; 57.358], ones (2, 1) * 1e-3); %!test %! [pval, chisq, df, E] = chi2test (x, "conditional", 3); %! assert (chisq, 52.2509, 1e-4); %! assert (df, 3); %! assert (E(:,:,1), [53.345, 37.655; 14.655, 10.345], ones (2, 2) * 1e-3); %!test %! [pval, chisq, df, E] = chi2test (x, "homogeneous", []); %! assert (chisq, 1.6034, 1e-4); %! assert (df, 2); %! assert (E(:,:,1), [60.827, 31.382; 7.173, 16.618], ones (2, 2) * 1e-3); statistics-release-1.7.3/inst/cholcov.m000066400000000000000000000101661475240274700201420ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{T} =} cholcov (@var{sigma}) ## @deftypefnx {statistics} {[@var{T}, @var{p} =} cholcov (@var{sigma}) ## @deftypefnx {statistics} {[@dots{}] =} cholcov (@var{sigma}, @var{flag}) ## ## Cholesky-like decomposition for covariance matrix. ## ## @code{@var{T} = cholcov (@var{sigma})} computes matrix @var{T} such that ## @var{sigma} = @var{T}' @var{T}. @var{sigma} must be square, symmetric, and ## positive semi-definite. ## ## If @var{sigma} is positive definite, then @var{T} is the square, upper ## triangular Cholesky factor. If @var{sigma} is not positive definite, @var{T} ## is computed with an eigenvalue decomposition of @var{sigma}, but in this case ## @var{T} is not necessarily triangular or square. Any eigenvectors whose ## corresponding eigenvalue is close to zero (within a tolerance) are omitted. ## If any remaining eigenvalues are negative, @var{T} is empty. ## ## The tolerance is calculated as @code{10 * eps (max (abs (diag (sigma))))}. ## ## @code{[@var{T}, @var{p} = cholcov (@var{sigma})} returns in @var{p} the ## number of negative eigenvalues of @var{sigma}. If @var{p} > 0, then @var{T} ## is empty, whereas if @var{p} = 0, @var{sigma}) is positive semi-definite. ## ## If @var{sigma} is not square and symmetric, P is NaN and T is empty. ## ## @code{[@var{T}, @var{p} = cholcov (@var{sigma}, 0)} returns @var{p} = 0 if ## @var{sigma} is positive definite, in which case @var{T} is the Cholesky ## factor. If @var{sigma} is not positive definite, @var{p} is a positive ## integer and @var{T} is empty. ## ## @code{[@dots{}] = cholcov (@var{sigma}, 1)} is equivalent to ## @code{ [@dots{}] = cholcov (@var{sigma})}. ## ## @seealso{chov} ## @end deftypefn function [T, p] = cholcov (sigma, flag) ## Check number of input arguments narginchk (1,2) ## Add default flag if not givens if (nargin < 2) flag = 1; endif ## Check if sigma is a sparse matrix is_sparse = issparse (sigma); ## Check if sigma is single or double class is_type = "double"; if (isa (sigma, "single")) is_type = "single"; endif ## Test for sigma being square and symmetric [col, row] = size (sigma); ## Add tolerance Tol = 10 * eps (max (abs (diag (sigma)))); if ((row == col) && all (all (abs (sigma - sigma') < col * Tol))) ## Check if positive definite [T, p] = chol (sigma); if (p > 0) ## Check flag for factoring using eigenvalue decomposition if (flag) [V, LAMBDA] = eig (full ((sigma + sigma') / 2)); [~, EIGMAX] = max (abs (V), [], 1); neg_idx = (V(EIGMAX + (0:row:(col-1)*row)) < 0); V(:,neg_idx) = -V(:,neg_idx); LAMBDA = diag(LAMBDA); Tol = eps (max (LAMBDA)) * length (LAMBDA); t = (abs (LAMBDA) > Tol); LAMBDA = LAMBDA(t); p = sum (LAMBDA < 0); ## Check for negative eigenvalues if (p == 0) T = diag (sqrt (LAMBDA)) * V(:,t)'; else T = zeros (0, is_type); endif else T = zeros (0, is_type); endif endif else T = zeros (0, is_type); p = NaN (is_type); endif if (is_sparse) T = sparse(T); endif endfunction %!demo %! C1 = [2, 1, 1, 2; 1, 2, 1, 2; 1, 1, 2, 2; 2, 2, 2, 3] %! T = cholcov (C1) %! C2 = T'*T %!test %! C1 = [2, 1, 1, 2; 1, 2, 1, 2; 1, 1, 2, 2; 2, 2, 2, 3]; %! T = cholcov (C1); %! assert (C1, T'*T, 1e-15 * ones (size (C1))); statistics-release-1.7.3/inst/cl_multinom.m000066400000000000000000000131771475240274700210340ustar00rootroot00000000000000## Copyright (C) 2009 Levente Torok ## Copyright (C) 2023 Andreas Bertsatos ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{CL} =} cl_multinom (@var{X}, @var{N}, @var{b}) ## @deftypefnx {statistics} {@var{CL} =} cl_multinom (@var{X}, @var{N}, @var{b}, @var{method}) ## ## Confidence level of multinomial portions. ## ## @code{cl_multinom} returns confidence level of multinomial parameters ## estimated as @math{p = X / sum(X)} with predefined confidence interval ## @var{b}. Finite population is also considered. ## ## This function calculates the level of confidence at which the samples ## represent the true distribution given that there is a predefined tolerance ## (confidence interval). This is the upside down case of the typical excercises ## at which we want to get the confidence interval given the confidence level ## (and the estimated parameters of the underlying distribution). ## But once we accept (lets say at elections) that we have a standard predefined ## maximal acceptable error rate (e.g. @var{b}=0.02 ) in the estimation and we ## just want to know that how sure we can be that the measured proportions are ## the same as in the entire population (ie. the expected value and mean of the ## samples are roughly the same) we need to use this function. ## ## @subheading Arguments ## @multitable @columnfractions 0.1 0.01 0.10 0.01 0.78 ## @headitem Variable @tab @tab Type @tab @tab Description ## @item @var{X} @tab @tab int vector @tab @tab sample frequencies bins. ## @item @var{N} @tab @tab int scalar @tab @tab Population size that was sampled ## by @var{X}. If @qcode{N < sum (@var{X})}, infinite number assumed. ## @item @var{b} @tab @tab real vector @tab @tab confidence interval. If vector, ## it should be the size of @var{X} containing confence interval for each cells. ## If scalar, each cell will have the same value of b unless it is zero or -1. ## If value is 0, @var{b} = 0.02 is assumed which is standard choice at ## elections otherwise it is calculated in a way that one sample in a cell ## alteration defines the confidence interval. ## @item @var{method} @tab @tab string @tab @tab An optional argument ## for defining the calculation method. Available choices are ## @qcode{"bromaghin"} (default), @qcode{"cochran"}, and @qcode{agresti_cull}. ## @end multitable ## ## Note! The @qcode{agresti_cull} method is not exactly the solution at ## reference given below but an adjustment of the solutions above. ## ## @subheading Returns ## Confidence level. ## ## @subheading Example ## CL = cl_multinom ([27; 43; 19; 11], 10000, 0.05) ## returns 0.69 confidence level. ## ## @subheading References ## @enumerate ## @item ## "bromaghin" calculation type (default) is based on the article: ## ## Jeffrey F. Bromaghin, "Sample Size Determination for Interval Estimation ## of Multinomial Probabilities", The American Statistician vol 47, 1993, ## pp 203-206. ## ## @item ## "cochran" calculation type is based on article: ## ## Robert T. Tortora, "A Note on Sample Size Estimation for Multinomial ## Populations", The American Statistician, , Vol 32. 1978, pp 100-102. ## ## @item ## "agresti_cull" calculation type is based on article: ## ## A. Agresti and B.A. Coull, "Approximate is better than 'exact' for ## interval estimation of binomial portions", The American Statistician, ## Vol. 52, 1998, pp 119-126 ## @end enumerate ## ## @end deftypefn function CL = cl_multinom (X, N, b = 0.05, method = "bromaghin") if (nargin < 2 || nargin > 4) print_usage; elseif (! ischar (method)) error ("cl_multinom: argument method must be a string."); endif k = rows (X); nn = sum (X); p = X / nn; if (isscalar (b)) if (b==0) b=0.02; endif b = ones (rows (X), 1 ) * b; if (b<0) b = 1 ./ max (X, 1); endif endif bb = b .* b; if (N == nn) CL = 1; return; endif if (N < nn) fpc = 1; else fpc = (N - 1) / (N - nn); # finite population correction tag endif beta = p .* (1 - p); switch lower (method) case "cochran" t = sqrt (fpc * nn * bb ./ beta); alpha = (1 - normcdf (t)) * 2; case "bromaghin" t = sqrt (fpc * (nn * 2 * bb ) ./ ... (beta - 2 * bb + sqrt (beta .* beta - bb .* (4 * beta - 1)))); alpha = (1 - normcdf (t)) * 2; case "agresti_cull" ts = fpc * nn * bb ./ beta ; if (k <= 2) alpha = 1 - chi2cdf (ts, k - 1); # adjusted Wilson interval else alpha = 1 - chi2cdf (ts / k, 1); # Goodman interval with Bonferroni arg. endif otherwise error ("cl_multinom: unknown calculation type '%s'.", method); endswitch CL = 1 - max( alpha ); endfunction %!demo %! CL = cl_multinom ([27; 43; 19; 11], 10000, 0.05) ## Test input validation %!error cl_multinom (); %!error cl_multinom (1, 2, 3, 4, 5); %!error ... %! cl_multinom (1, 2, 3, 4); %!error ... %! cl_multinom (1, 2, 3, "some string"); statistics-release-1.7.3/inst/cluster.m000066400000000000000000000147631475240274700201750ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{T} =} cluster (@var{Z}, "Cutoff", @var{C}) ## @deftypefnx {statistics} {@var{T} =} cluster (@var{Z}, "Cutoff", @var{C}, "Depth", @var{D}) ## @deftypefnx {statistics} {@var{T} =} cluster (@var{Z}, "Cutoff", @var{C}, "Criterion", @var{criterion}) ## @deftypefnx {statistics} {@var{T} =} cluster (@var{Z}, "MaxClust", @var{N}) ## ## Define clusters from an agglomerative hierarchical cluster tree. ## ## Given a hierarchical cluster tree @var{Z} generated by the @code{linkage} ## function, @code{cluster} defines clusters, using a threshold value @var{C} to ## identify new clusters ('Cutoff') or according to a maximum number of desired ## clusters @var{N} ('MaxClust'). ## ## @var{criterion} is used to choose the criterion for defining clusters, which ## can be either "inconsistent" (default) or "distance". When using ## "inconsistent", @code{cluster} compares the threshold value @var{C} to the ## inconsistency coefficient of each link; when using "distance", @code{cluster} ## compares the threshold value @var{C} to the height of each link. ## @var{D} is the depth used to evaluate the inconsistency coefficient, its ## default value is 2. ## ## @code{cluster} uses "distance" as a criterion for defining new clusters when ## it is used the 'MaxClust' method. ## ## @seealso{clusterdata, dendrogram, inconsistent, kmeans, linkage, pdist} ## @end deftypefn function T = cluster (Z, opt, varargin) switch (lower (opt)) ## check the input case "cutoff" if (nargin < 3) print_usage (); else C = varargin{1}; D = 2; criterion = "inconsistent"; if (nargin > 3) pair_index = 2; while (pair_index < (nargin - 2)) switch (lower (varargin{pair_index})) case "depth" D = varargin{pair_index + 1}; case "criterion" criterion = varargin{pair_index + 1}; otherwise error ("cluster: unknown property %s", varargin{pair_index}); endswitch pair_index += 2; endwhile endif endif if ((! (isscalar (C) || isvector (C))) || (C < 0)) error ... (["cluster: C must be a positive scalar or a vector of positive"... "numbers"]); endif case "maxclust" if (nargin != 3) print_usage (); else N = varargin{1}; C = []; endif if ((! (isscalar (N) || isvector (N))) || (N < 0)) error ... (["cluster: N must be a positive number or a vector of positive"... "numbers"]); endif otherwise error ("cluster: unknown option %s", opt); endswitch if ((columns (Z) != 3) || (! isnumeric (Z)) ... (! (max (Z(end, 1:2)) == rows (Z) * 2))) error ("cluster: Z must be a matrix generated by the linkage function"); endif ## number of observations n = rows (Z) + 1; ## vector of values used by the threshold check vThresholds = []; ## starting number of clusters nClusters = 1; ## the return value is the matrix T, constituted by one or more vector vT T = []; vT = zeros (1, n); ## main logic ## a few checks and computations before launching the recursive function switch (lower (opt)) case "cutoff" switch (lower (criterion)) case "inconsistent" vThresholds = inconsistent (Z, D)(:, 4); case "distance" vThresholds = Z(:, 3); otherwise error ("cluster: unkown criterion %s", criterion); endswitch case "maxclust" ## the MaxClust case can be regarded as a Cutoff case with distance ## criterion, where the threshold is set to the height of the highest node ## that allows us to have N different clusters vThresholds = Z(:, 3); ## let's build a vector with the apt threshold values for k = 1:length (N); if (N(k) > n) C(end+1) = 0; elseif (N(k) < 2) C(end+1) = Z(end, 3) + 1; else C(end+1) = Z((end + 2 - N(k)), 3); endif endfor endswitch for c_index = 1:length (C) cluster_cutoff_recursive (rows (Z), nClusters, c_index); T = [T; vT]; endfor T = T'; # return value ## recursive function ## for each link check if the cutoff criteria (a threshold value) are met, ## then call recursively this function for every node below that; ## when we find a leaf, we add the index of its cluster to the return value function cluster_cutoff_recursive (index, cluster_number, c_index) vClusterNumber = [cluster_number, cluster_number]; ## check the threshold value if (vThresholds(index) >= C(c_index)) ## create a new cluster nClusters++; vClusterNumber(2) = nClusters; endif; ## go on, down the tree for j = 1:2 if (Z(index,j) > n) new_index = Z(index,j) - n; cluster_cutoff_recursive (new_index, vClusterNumber(j), c_index); else ## if the next node is a leaf, add the index of its cluster to the ## result at the correct position, i.e. the leaf number; ## if leaf 14 belongs to cluster 3: ## vT(14) = 3; vT(Z(index,j)) = vClusterNumber(j); endif endfor endfunction endfunction ## Test input validation %!error cluster () %!error cluster ([1 1], "Cutoff", 1) %!error cluster ([1 2 1], "Bogus", 1) %!error cluster ([1 2 1], "Cutoff", -1) %!error cluster ([1 2 1], "Cutoff", 1, "Bogus", 1) ## Test output %!test % X = [(randn (10, 2) * 0.25) + 1; (randn (10, 2) * 0.25) - 1]; % Z = linkage(X, "ward"); % T = [ones (10, 1); 2 * ones (10, 1)]; % assert (cluster (Z, "MaxClust", 2), T); statistics-release-1.7.3/inst/clusterdata.m000066400000000000000000000105201475240274700210120ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{T} =} clusterdata (@var{X}, @var{cutoff}) ## @deftypefnx {statistics} {@var{T} =} clusterdata (@var{X}, @var{Name}, @var{Value}) ## ## Wrapper function for @code{linkage} and @code{cluster}. ## ## If @var{cutoff} is used, then @code{clusterdata} calls @code{linkage} and ## @code{cluster} with default value, using @var{cutoff} as a threshold value ## for @code{cluster}. If @var{cutoff} is an integer and greater or equal to 2, ## then @var{cutoff} is interpreted as the maximum number of cluster desired ## and the "MaxClust" option is used for @code{cluster}. ## ## If @var{cutoff} is not used, then @code{clusterdata} expects a list of pair ## arguments. Then you must specify either the "Cutoff" or "MaxClust" option ## for @code{cluster}. The method and metric used by @code{linkage}, are ## defined through the "linkage" and "distance" arguments. ## ## @seealso{cluster, dendrogram, inconsistent, kmeans, linkage, pdist} ## @end deftypefn function T = clusterdata (X, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("clusterdata: function called with too few input arguments."); endif linkage_criterion = "single"; distance_method = "euclidean"; savememory = "off"; clustering_method = []; criterion = "inconsistent"; D = 2; if (isnumeric (varargin{1})) # clusterdata (X, cutoff) if (isinteger (varargin{1}) && (varargin{1} >= 2)) clustering_method = "MaxClust"; else clustering_method = "Cutoff"; endif C = varargin{1}; else # clusterdata (Name, Value) pair_index = 1; while (pair_index < (nargin - 1)) switch (lower (varargin{pair_index})) case "criterion" criterion = varargin{pair_index + 1}; case "cutoff" clustering_method = "Cutoff"; C = varargin{pair_index + 1}; case "depth" D = varargin{pair_index + 1}; case "distance" distance_method = varargin{pair_index + 1}; case "linkage" linkage_criterion = varargin{pair_index + 1}; case "maxclust" clustering_method = "MaxClust"; C = varargin{pair_index + 1}; case "savememory" savememory = varargin{pair_index + 1}; otherwise error ("clusterdata: unknown property %s", varargin{pair_index}); endswitch pair_index += 2; endwhile endif if (isempty (clustering_method)) error (strcat (["clusterdata: you must specify either 'MaxClust'"], ... [" or 'Cutoff' when using name-value arguments."])); endif ## main body Z = linkage (X, linkage_criterion, distance_method, "savememory"); if (strcmp (lower (clustering_method), "cutoff")) T = cluster (Z, clustering_method, C, "Criterion", criterion, "Depth", D); else T = cluster (Z, clustering_method, C); endif endfunction %!demo %! randn ("seed", 1) # for reproducibility %! r1 = randn (10, 2) * 0.25 + 1; %! randn ("seed", 5) # for reproducibility %! r2 = randn (20, 2) * 0.5 - 1; %! X = [r1; r2]; %! %! wnl = warning ("off", "Octave:linkage_savemem", "local"); %! T = clusterdata (X, "linkage", "ward", "MaxClust", 2); %! scatter (X(:,1), X(:,2), 36, T, "filled"); ## Test input validation %!error ... %! clusterdata () %!error ... %! clusterdata (1) %!error clusterdata ([1 1], "Bogus", 1) %!error clusterdata ([1 1], "Depth", 1) statistics-release-1.7.3/inst/cmdscale.m000066400000000000000000000132121475240274700202530ustar00rootroot00000000000000## Copyright (C) 2014 JD Walsh ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Y} =} cmdscale (@var{D}) ## @deftypefnx {statistics} {[@var{Y}, @var{e}] =} cmdscale (@var{D}) ## ## Classical multidimensional scaling of a matrix. ## ## Takes an @var{n} by @var{n} distance (or difference, similarity, or ## dissimilarity) matrix @var{D}. Returns @var{Y}, a matrix of @var{n} points ## with coordinates in @var{p} dimensional space which approximate those ## distances (or differences, similarities, or dissimilarities). Also returns ## the eigenvalues @var{e} of ## @code{@var{B} = -1/2 * @var{J} * (@var{D}.^2) * @var{J}}, where ## @code{J = eye(@var{n}) - ones(@var{n},@var{n})/@var{n}}. @var{p}, the number ## of columns of @var{Y}, is equal to the number of positive real eigenvalues of ## @var{B}. ## ## @var{D} can be a full or sparse matrix or a vector of length ## @code{@var{n}*(@var{n}-1)/2} containing the upper triangular elements (like ## the output of the @code{pdist} function). It must be symmetric with ## non-negative entries whose values are further restricted by the type of ## matrix being represented: ## ## * If @var{D} is either a distance, dissimilarity, or difference matrix, then ## it must have zero entries along the main diagonal. In this case the points ## @var{Y} equal or approximate the distances given by @var{D}. ## ## * If @var{D} is a similarity matrix, the elements must all be less than or ## equal to one, with ones along the the main diagonal. In this case the points ## @var{Y} equal or approximate the distances given by ## @code{@var{D} = sqrt(ones(@var{n},@var{n})-@var{D})}. ## ## @var{D} is a Euclidean matrix if and only if @var{B} is positive ## semi-definite. When this is the case, then @var{Y} is an exact representation ## of the distances given in @var{D}. If @var{D} is non-Euclidean, @var{Y} only ## approximates the distance given in @var{D}. The approximation used by ## @code{cmdscale} minimizes the statistical loss function known as ## @var{strain}. ## ## The returned @var{Y} is an @var{n} by @var{p} matrix showing possible ## coordinates of the points in @var{p} dimensional space ## (@code{@var{p} < @var{n}}). The columns are correspond to the positive ## eigenvalues of @var{B} in descending order. A translation, rotation, or ## reflection of the coordinates given by @var{Y} will satisfy the same distance ## matrix up to the limits of machine precision. ## ## For any @code{@var{k} <= @var{p}}, if the largest @var{k} positive ## eigenvalues of @var{B} are significantly greater in absolute magnitude than ## its other eigenvalues, the first @var{k} columns of @var{Y} provide a ## @var{k}-dimensional reduction of @var{Y} which approximates the distances ## given by @var{D}. The optional return @var{e} can be used to consider various ## values of @var{k}, or to evaluate the accuracy of specific dimension ## reductions (e.g., @code{@var{k} = 2}). ## ## Reference: Ingwer Borg and Patrick J.F. Groenen (2005), Modern ## Multidimensional Scaling, Second Edition, Springer, ISBN: 978-0-387-25150-9 ## (Print) 978-0-387-28981-6 (Online) ## ## @seealso{pdist} ## @end deftypefn function [Y, e] = cmdscale (D) % Check for matrix input if ((nargin ~= 1) || ... (~any(strcmp ({'matrix' 'scalar' 'range'}, typeinfo(D))))) usage ('cmdscale: input must be vector or matrix; see help'); endif % If vector, convert to matrix; otherwise, check for square symmetric input if (isvector (D)) D = squareform (D); elseif ((~issquare (D)) || (norm (D - D', 1) > 0)) usage ('cmdscale: matrix input must be square symmetric; see help'); endif n = size (D,1); % Check for valid format (see help above); If similarity matrix, convert if (any (any (D < 0))) usage ('cmdscale: entries must be nonnegative; see help'); elseif (trace (D) ~= 0) if ((~all (diag (D) == 1)) || (~all (D <= 1))) usage ('cmdscale: input must be distance vector or matrix; see help'); endif D = sqrt (ones (n,n) - D); endif % Build centering matrix, perform double centering, extract eigenpairs J = eye (n) - ones (n,n) / n; B = -1 / 2 * J * (D .^ 2) * J; [Q, e] = eig (B); e = diag (e); etmp = e; e = sort(e, 'descend'); % Remove complex eigenpairs (only possible due to machine approximation) if (iscomplex (etmp)) for i = 1 : size (etmp,1) cmp(i) = (isreal (etmp(i))); endfor etmp = etmp(cmp); Q = Q(:,cmp); endif % Order eigenpairs [etmp, ord] = sort (etmp, 'descend'); Q = Q(:,ord); % Remove negative eigenpairs cmp = (etmp > 0); etmp = etmp(cmp); Q = Q(:,cmp); % Test for n-dimensional results if (size(etmp,1) == n) etmp = etmp(1:n-1); Q = Q(:, 1:n-1); endif % Build output matrix Y Y = Q * diag (sqrt (etmp)); endfunction %!shared m, n, X, D %! m = randi(100) + 1; n = randi(100) + 1; X = rand(m, n); D = pdist(X); %!assert(norm(pdist(cmdscale(D))), norm(D), sqrt(eps)) %!assert(norm(pdist(cmdscale(squareform(D)))), norm(D), sqrt(eps)) statistics-release-1.7.3/inst/combnk.m000066400000000000000000000050051475240274700177520ustar00rootroot00000000000000## Copyright (C) 2010 Soren Hauberg ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{c} =} combnk (@var{data}, @var{k}) ## ## Return all combinations of @var{k} elements in @var{data}. ## ## @end deftypefn function retval = combnk (data, k) ## Check input if (nargin != 2) print_usage; elseif (! isvector (data)) error ("combnk: first input argument must be a vector"); elseif (!isreal (k) || k != round (k) || k < 0) error ("combnk: second input argument must be a non-negative integer"); endif ## Simple checks n = numel (data); if (k == 0 || k > n) retval = resize (data, 0, k); elseif (k == n) retval = data (:).'; else retval = __combnk__ (data, k); endif ## For some odd reason Matlab seems to treat strings differently compared to ## other data-types... if (ischar (data)) retval = flipud (retval); endif endfunction function retval = __combnk__ (data, k) ## Recursion stopping criteria if (k == 1) retval = data (:); else ## Process data n = numel (data); if (iscell (data)) retval = {}; else retval = []; endif for j = 1:n C = __combnk__ (data ((j+1):end), k-1); C = cat (2, repmat (data (j), rows (C), 1), C); if (! isempty (C)) if (isempty (retval)) retval = C; else retval = [retval; C]; endif endif endfor endif endfunction %!demo %! c = combnk (1:5, 2); %! disp ("All pairs of integers between 1 and 5:"); %! disp (c); %!test %! c = combnk (1:3, 2); %! assert (c, [1, 2; 1, 3; 2, 3]); %!test %! c = combnk (1:3, 6); %! assert (isempty (c)); %!test %! c = combnk ({1, 2, 3}, 2); %! assert (c, {1, 2; 1, 3; 2, 3}); %!test %! c = combnk ("hello", 2); %! assert (c, ["lo"; "lo"; "ll"; "eo"; "el"; "el"; "ho"; "hl"; "hl"; "he"]); statistics-release-1.7.3/inst/confusionchart.m000066400000000000000000000250031475240274700215260ustar00rootroot00000000000000## Copyright (C) 2020-2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} confusionchart (@var{trueLabels}, @var{predictedLabels}) ## @deftypefnx {statistics} {} confusionchart (@var{m}) ## @deftypefnx {statistics} {} confusionchart (@var{m}, @var{classLabels}) ## @deftypefnx {statistics} {} confusionchart (@var{parent}, @dots{}) ## @deftypefnx {statistics} {} confusionchart (@dots{}, @var{prop}, @var{val}, @dots{}) ## @deftypefnx {statistics} {@var{cm} =} confusionchart (@dots{}) ## ## Display a chart of a confusion matrix. ## ## The two vectors of values @var{trueLabels} and @var{predictedLabels}, which ## are used to compute the confusion matrix, must be defined with the same ## format as the inputs of @code{confusionmat}. ## Otherwise a confusion matrix @var{m} as computed by @code{confusionmat} can ## be given. ## ## @var{classLabels} is an array of labels, i.e. the list of the class names. ## ## If the first argument is a handle to a @code{figure} or to a @code{uipanel}, ## then the confusion matrix chart is displayed inside that object. ## ## Optional property/value pairs are passed directly to the underlying objects, ## e.g. @qcode{"xlabel"}, @qcode{"ylabel"}, @qcode{"title"}, @qcode{"fontname"}, ## @qcode{"fontsize"} etc. ## ## The optional return value @var{cm} is a @code{ConfusionMatrixChart} object. ## Specific properties of a @code{ConfusionMatrixChart} object are: ## @itemize @bullet ## @item @qcode{"DiagonalColor"} ## The color of the patches on the diagonal, default is [0.0, 0.4471, 0.7412]. ## ## @item @qcode{"OffDiagonalColor"} ## The color of the patches off the diagonal, default is [0.851, 0.3255, 0.098]. ## ## @item @qcode{"GridVisible"} ## Available values: @qcode{on} (default), @qcode{off}. ## ## @item @qcode{"Normalization"} ## Available values: @qcode{absolute} (default), @qcode{column-normalized}, ## @qcode{row-normalized}, @qcode{total-normalized}. ## ## @item @qcode{"ColumnSummary"} ## Available values: @qcode{off} (default), @qcode{absolute}, ## @qcode{column-normalized},@qcode{total-normalized}. ## ## @item @qcode{"RowSummary"} ## Available values: @qcode{off} (default), @qcode{absolute}, ## @qcode{row-normalized}, @qcode{total-normalized}. ## @end itemize ## ## Run @code{demo confusionchart} to see some examples. ## ## @seealso{confusionmat, sortClasses} ## @end deftypefn function cm = confusionchart (varargin) ## check the input parameters if (nargin < 1) print_usage (); endif p_i = 1; if (ishghandle (varargin{p_i})) ## parameter is a parent figure handle_type = get (varargin{p_i}, "type"); if (strcmp (handle_type, "figure")) h = figure (varargin{p_i}); hax = axes ("parent", h); elseif (strcmp (handle_type, "uipanel")) h = varargin{p_i}; hax = axes ("parent", varargin{p_i}); else ## MATLAB compatibility: on MATLAB are also available Tab objects, ## TiledChartLayout objects, GridLayout objects error ("confusionchart: invalid handle to parent object"); endif p_i++; else h = figure (); hax = axes ("parent", h); endif if (ismatrix (varargin{p_i}) && rows (varargin{p_i}) == ... columns (varargin{p_i})) ## parameter is a confusion matrix conmat = varargin{p_i}; p_i++; if (p_i <= nargin && ((isvector (varargin{p_i}) && ... length (varargin{p_i}) == rows (conmat)) || ... (ischar ( varargin{p_i}) && rows (varargin{p_i}) == rows (conmat)) ... || iscellstr (varargin{p_i}))) ## parameter is an array of labels labarr = varargin{p_i}; if (isrow (labarr)) labarr = vec (labarr); endif p_i++; else labarr = [1 : (rows (conmat))]'; endif elseif (isvector (varargin{p_i})) ## parameter must be a group for confusionmat [conmat, labarr] = confusionmat (varargin{p_i}, varargin{p_i + 1}); p_i = p_i + 2; else close (h); error ("confusionchart: invalid argument"); endif ## remaining parameters are stored i = p_i; args = {}; while (i <= nargin) args{end + 1} = varargin{i++}; endwhile ## prepare the labels if (! iscellstr (labarr)) if (! ischar (labarr)) labarr = num2str (labarr); endif labarr = cellstr (labarr); endif ## MATLAB compatibility: labels are sorted [labarr, I] = sort (labarr); conmat = conmat(I, :); conmat = conmat(:, I); cm = ConfusionMatrixChart (hax, conmat, labarr, args); endfunction ## Demonstration using the confusion matrix example from ## R.Bonnin, "Machine Learning for Developers", pp. 55-56 %!demo %! ## Setting the chart properties %! Yt = [8 5 6 8 5 3 1 6 4 2 5 3 1 4]'; %! Yp = [8 5 6 8 5 2 3 4 4 5 5 7 2 6]'; %! confusionchart (Yt, Yp, "Title", ... %! "Demonstration with summaries","Normalization",... %! "absolute","ColumnSummary", "column-normalized","RowSummary",... %! "row-normalized") ## example: confusion matrix and class labels %!demo %! ## Cellstr as inputs %! Yt = {"Positive", "Positive", "Positive", "Negative", "Negative"}; %! Yp = {"Positive", "Positive", "Negative", "Negative", "Negative"}; %! m = confusionmat (Yt, Yp); %! confusionchart (m, {"Positive", "Negative"}); ## example: editing the properties of an existing ConfusionMatrixChart object %!demo %! ## Editing the object properties %! Yt = {"Positive", "Positive", "Positive", "Negative", "Negative"}; %! Yp = {"Positive", "Positive", "Negative", "Negative", "Negative"}; %! cm = confusionchart (Yt, Yp); %! cm.Title = "This is an example with a green diagonal"; %! cm.DiagonalColor = [0.4660, 0.6740, 0.1880]; ## example: drawing the chart inside a uipanel %!demo %! ## Confusion chart in a uipanel %! h = uipanel (); %! Yt = {"Positive", "Positive", "Positive", "Negative", "Negative"}; %! Yp = {"Positive", "Positive", "Negative", "Negative", "Negative"}; %! cm = confusionchart (h, Yt, Yp); ## example: sortClasses %!demo %! ## Sorting classes %! Yt = [8 5 6 8 5 3 1 6 4 2 5 3 1 4]'; %! Yp = [8 5 6 8 5 2 3 4 4 5 5 7 2 6]'; %! cm = confusionchart (Yt, Yp, "Title", ... %! "Classes are sorted in ascending order"); %! cm = confusionchart (Yt, Yp, "Title", ... %! "Classes are sorted according to clusters"); %! sortClasses (cm, "cluster"); ## Test input validation ## Get current figure visibility so it can be restored after tests %!shared visibility_setting %! visibility_setting = get (0, "DefaultFigureVisible"); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ()", "Invalid call"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 1; 2 2; 3 3])", "invalid argument"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'xxx', 1)", "invalid property"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'XLabel', 1)", "XLabel .* string"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'YLabel', [1 0])", ... %! ".* YLabel .* string"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'Title', .5)", ".* Title .* string"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'FontName', [])", ... %! ".* FontName .* string"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'FontSize', 'b')", ... %! ".* FontSize .* numeric"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'DiagonalColor', 'h')", ... %! ".* DiagonalColor .* color"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'OffDiagonalColor', [])", ... %! ".* OffDiagonalColor .* color"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'Normalization', '')", ... %! ".* invalid .* Normalization"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'ColumnSummary', [])", ... %! ".* invalid .* ColumnSummary"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'RowSummary', 1)", ... %! ".* invalid .* RowSummary"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'GridVisible', .1)", ... %! ".* invalid .* GridVisible"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'HandleVisibility', .1)", ... %! ".* invalid .* HandleVisibility"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'OuterPosition', .1)", ... %! ".* invalid .* OuterPosition"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'Position', .1)", ... %! ".* invalid .* Position"); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! fail ("confusionchart ([1 2], [0 1], 'Units', .1)", ".* invalid .* Units"); %! set (0, "DefaultFigureVisible", visibility_setting); statistics-release-1.7.3/inst/confusionmat.m000066400000000000000000000153521475240274700212140ustar00rootroot00000000000000## Copyright (C) 2020 Stefano Guidoni ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{C} =} confusionmat (@var{group}, @var{grouphat}) ## @deftypefnx {statistics} {@var{C} =} confusionmat (@var{group}, @var{grouphat}, "Order", @var{grouporder}) ## @deftypefnx {statistics} {[@var{C}, @var{order}] =} confusionmat (@var{group}, @var{grouphat}) ## ## Compute a confusion matrix for classification problems ## ## @code{confusionmat} returns the confusion matrix @var{C} for the group of ## actual values @var{group} and the group of predicted values @var{grouphat}. ## The row indices of the confusion matrix represent actual values, while the ## column indices represent predicted values. The indices are the same for both ## actual and predicted values, so the confusion matrix is a square matrix. ## Each element of the matrix represents the number of matches between a given ## actual value (row index) and a given predicted value (column index), hence ## correct matches lie on the main diagonal of the matrix. ## The order of the rows and columns is returned in @var{order}. ## ## @var{group} and @var{grouphat} must have the same number of observations ## and the same data type. ## Valid data types are numeric vectors, logical vectors, character arrays, ## string arrays (not implemented yet), cell arrays of strings. ## ## The order of the rows and columns can be specified by setting the ## @var{grouporder} variable. The data type of @var{grouporder} must be the ## same of @var{group} and @var{grouphat}. ## ## MATLAB compatibility: Octave misses string arrays and categorical vectors. ## ## @seealso{crosstab} ## @end deftypefn function [C, order] = confusionmat (group, grouphat, opt = "Order", grouporder) ## check the input parameters if ((nargin < 2) || (nargin > 4)) print_usage(); endif y_true = group; y_pred = grouphat; if (class (y_true) != class (y_pred)) error ("confusionmat: group and grouphat must be of the same data type"); endif if (length (y_true) != length (y_pred)) error ("confusionmat: group and grouphat must be of the same length"); endif if ((nargin > 3) && strcmp (opt, "Order")) unique_tokens = grouporder; if class( y_true ) != class( unique_tokens ) error ("confusionmat: group and grouporder must be of the same data type"); endif endif if (isvector (y_true)) if (isrow (y_true)) y_true = vec(y_true); endif else error ("confusionmat: group must be a vector or array"); endif if (isvector (y_pred)) if (isrow (y_pred)) y_pred = vec(y_pred); endif else error ("confusionmat: grouphat must be a vector or array"); endif if (exist ( "unique_tokens", "var")) if (isvector (unique_tokens)) if (isrow (unique_tokens)) unique_tokens = vec(unique_tokens); endif else error ("confusionmat: grouporder must be a vector or array"); endif endif ## compute the confusion matrix if (isa (y_true, "numeric") || isa (y_true, "logical")) ## numeric or boolean vector ## MATLAB compatibility: ## remove NaN values from grouphat nan_indices = find (isnan (y_pred)); y_pred(nan_indices) = []; ## MATLAB compatibility: ## numeric and boolean values ## are sorted in ascending order if (! exist ("unique_tokens", "var")) unique_tokens = union (y_true, y_pred); endif y_true(nan_indices) = []; C_size = length (unique_tokens); C = zeros (C_size); for i = 1:length (y_true) row_index = find (unique_tokens == y_true(i)); col_index = find (unique_tokens == y_pred(i)); C(row_index, col_index)++; endfor elseif (iscellstr (y_true)) ## string cells ## MATLAB compatibility: ## remove empty values from grouphat empty_indices = []; for i = 1:length (y_pred) if (isempty (y_pred{i})) empty_indices = [empty_indices; i]; endif endfor y_pred(empty_indices) = []; ## MATLAB compatibility: ## string values are sorted according to their ## first appearance in group and grouphat if (! exist ("unique_tokens", "var")) all_tokens = vertcat (y_true, y_pred); unique_tokens = [all_tokens(1)]; for i = 2:length( all_tokens ) if (! any (strcmp (all_tokens(i), unique_tokens))) unique_tokens = [unique_tokens; all_tokens(i)]; endif endfor endif y_true(empty_indices) = []; C_size = length (unique_tokens); C = zeros (C_size); for i = 1:length (y_true) row_index = find (strcmp (y_true{i}, unique_tokens)); col_index = find (strcmp (y_pred{i}, unique_tokens)); C(row_index, col_index)++; endfor elseif (ischar (y_true)) ## character array ## MATLAB compatibility: ## character values are sorted according to their ## first appearance in group and grouphat if (! exist ("unique_tokens", "var")) all_tokens = vertcat (y_true, y_pred); unique_tokens = [all_tokens(1)]; for i = 2:length (all_tokens) if (! any (find (unique_tokens == all_tokens(i)))) unique_tokens = [unique_tokens; all_tokens(i)]; endif endfor endif C_size = length ( unique_tokens ); C = zeros ( C_size ); for i = 1:length( y_true) row_index = find (unique_tokens == y_true(i)); col_index = find (unique_tokens == y_pred(i)); C(row_index, col_index)++; endfor elseif (isstring (y_true)) ## string array ## FIXME: not implemented yet error ("confusionmat: string array not implemented yet"); else error ("confusionmat: invalid data type"); endif order = unique_tokens; endfunction ## Test the confusion matrix example from ## R.Bonnin, "Machine Learning for Developers", pp. 55-56 %!test %! Yt = [8 5 6 8 5 3 1 6 4 2 5 3 1 4]'; %! Yp = [8 5 6 8 5 2 3 4 4 5 5 7 2 6]'; %! C = [0 1 1 0 0 0 0 0; 0 0 0 0 1 0 0 0; 0 1 0 0 0 0 1 0; 0 0 0 1 0 1 0 0; ... %! 0 0 0 0 3 0 0 0; 0 0 0 1 0 1 0 0; 0 0 0 0 0 0 0 0; 0 0 0 0 0 0 0 2]; %! assert (confusionmat (Yt, Yp), C) statistics-release-1.7.3/inst/cophenet.m000066400000000000000000000111341475240274700203060ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{c}, @var{d}] =} cophenet (@var{Z}, @var{y}) ## ## Compute the cophenetic correlation coefficient. ## ## The cophenetic correlation coefficient @var{C} of a hierarchical cluster tree ## @var{Z} is the linear correlation coefficient between the cophenetic ## distances @var{d} and the euclidean distances @var{y}. ## @tex ## \def\frac#1#2{{\begingroup#1\endgroup\over#2}} ## $$ c = \frac {\sum_{i < j}(Y_{ij}-{\bar {y}})(Z_{ij}-{\bar{z}})} ## {\sqrt{\sum_{i < j}(Y_{ij}-{\bar {y}})^2(Z_{ij}-{\bar{z}})^2}} $$ ## @end tex ## ## It is a measure of the similarity between the distance of the leaves, as seen ## in the tree, and the distance of the original data points, which were used to ## build the tree. When this similarity is greater, that is the coefficient is ## closer to 1, the tree renders an accurate representation of the distances ## between the original data points. ## ## @var{Z} is a hierarchical cluster tree, as the output of @code{linkage}. ## @var{y} is a vector of euclidean distances, as the output of @code{pdist}. ## ## The optional output @var{d} is a vector of cophenetic distances, in the same ## lower triangular format as @var{y}. The cophenetic distance between two data ## points is the height of the lowest common node of the tree. ## ## @seealso{cluster, dendrogram, inconsistent, linkage, pdist, squareform} ## @end deftypefn function [c, d] = cophenet (Z, y) ## Check input arguments if (nargin < 2) error ("cophenet: function called with too few input arguments."); endif ## Z must be a tree [m w] = size (Z); if ((w != 3) || (! isnumeric (Z)) || (! (max (Z(end,1:2)) == m * 2))) error ("cophenet: Z must be a matrix as generated by the linkage function."); endif ## Data set size n = m + 1; ## Y must be a vector of distances if ((! isnumeric (y)) || (length (y) != (n - 1) * n / 2)) error ("cophenet: Y must be a vector of euclidean distances."); endif ## Compute the cophenetic distances d d = zeros (1, length (y)); N = sparse ((m - 1), m); # to keep track of the leaves from each branch for i = 1 : m l_n = Z(i, 1); r_n = Z(i, 2); if (l_n > n) l_v = nonzeros (N(l_n - n, :)); # the list of leaves from the left branch else l_v = l_n; endif if (r_n > n) r_v = nonzeros (N(r_n - n, :)); # the list of leaves from the right branch else r_v = r_n; endif j_max = length (l_v); k_max = length (r_v); ## Keep track of the leaves in each sub-branch, i.e. node; ## this does not matter for the last node, which includes all leaves if (i < m) N(i, 1 : (j_max + k_max)) = [l_v' r_v']; endif for j = 1 : j_max for k = 1: k_max ## d is in the same format as y if (l_v(j) < r_v(k)) index = (l_v(j) - 1) * m - sum (1 : (l_v(j) - 2)) + (r_v(k) - l_v(j)); else index = (r_v(k) - 1) * m - sum (1 : (r_v(k) - 2)) + (l_v(j) - r_v(k)); endif d(index) = Z(i, 3); endfor endfor endfor ## Compute the cophenetic correlation c y_mean = mean (y); z_mean = mean (d); Y_sigma = y - y_mean; Z_sigma = d - z_mean; c = sum (Z_sigma .* Y_sigma) / sqrt (sum (Y_sigma .^ 2) * sum (Z_sigma .^ 2)); endfunction %!demo %! randn ("seed", 5) # for reproducibility %! X = randn (10,2); %! y = pdist (X); %! Z = linkage (y, "average"); %! cophenet (Z, y) ## Test input validation %!error cophenet () %!error cophenet (1) %!error ... %! cophenet (ones (2,2), 1) %!error ... %! cophenet ([1 2 1], "a") %!error ... %! cophenet ([1 2 1], [1 2]) statistics-release-1.7.3/inst/correlation_test.m000066400000000000000000000213741475240274700220700ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} correlation_test (@var{x}, @var{y}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} correlation_test (@var{y}, @var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{stats}] =} correlation_test (@var{y}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} correlation_test (@var{y}, @var{x}, @var{Name}, @var{Value}) ## ## Perform a correlation coefficient test whether two samples @var{x} and ## @var{y} come from uncorrelated populations. ## ## @code{@var{h} = correlation_test (@var{y}, @var{x})} tests the null ## hypothesis that the two samples @var{x} and @var{y} come from uncorrelated ## populations. The result is @var{h} = 0 if the null hypothesis cannot be ### rejected at the 5% significance level, or @var{h} = 1 if the null hypothesis ## can be rejected at the 5% level. @var{y} and @var{x} must be vectors of ## equal length with finite real numbers. ## ## The p-value of the test is returned in @var{pval}. @var{stats} is a ## structure with the following fields: ## @multitable @columnfractions 0.05 0.2 0.05 0.70 ## @headitem @tab Field @tab @tab Value ## @item @tab @qcode{method} @tab @tab the type of correlation coefficient used ## for the test ## @item @tab @qcode{df} @tab @tab the degrees of freedom (where applicable) ## @item @tab @qcode{corrcoef} @tab @tab the correlation coefficient ## @item @tab @qcode{stat} @tab @tab the test's statistic ## @item @tab @qcode{dist} @tab @tab the respective distribution for the test ## @item @tab @qcode{alt} @tab @tab the alternative hypothesis for the test ## @end multitable ## ## ## @code{[@dots{}] = correlation_test (@dots{}, @var{name}, @var{value})} ## specifies one or more of the following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"alpha"} @tab the significance level. Default is 0.05. ## ## @item @tab @qcode{"tail"} @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.1 0.25 0.65 ## @item @tab @qcode{"both"} @tab @math{corrcoef} is not 0 (two-tailed, default) ## @item @tab @qcode{"left"} @tab @math{corrcoef} is less than 0 (left-tailed) ## @item @tab @qcode{"right"} @tab @math{corrcoef} is greater than 0 ## (right-tailed) ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{"method"} @tab a string specifying the correlation ## coefficient used for the test ## @end multitable ## @multitable @columnfractions 0.1 0.25 0.65 ## @item @tab @qcode{"pearson"} @tab Pearson's product moment correlation ## (Default) ## @item @tab @qcode{"kendall"} @tab Kendall's rank correlation tau ## @item @tab @qcode{"spearman"} @tab Spearman's rank correlation rho ## @end multitable ## ## @seealso{regression_ftest, regression_ttest} ## @end deftypefn function [h, pval, stats] = correlation_test (x, y, varargin) if (nargin < 2) print_usage (); endif if (! isvector (x) || ! isvector (y) || length (x) != length (y)) error ("correlation_test: X and Y must be vectors of equal length."); endif ## Force to column vectors x = x(:); y = y(:); ## Check for finite real numbers in X and Y if (! all (isfinite (x)) || ! isreal (x)) error ("correlation_test: X must contain finite real numbers."); endif if (! all (isfinite (y(:))) || ! isreal (y)) error ("correlation_test: Y must contain finite real numbers."); endif ## Set default arguments alpha = 0.05; tail = "both"; method = "pearson"; ## Check additional options i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; ## Check for valid alpha if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("correlation_test: invalid value for alpha."); endif case "tail" i = i + 1; tail = varargin{i}; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("correlation_test: invalid value for tail."); endif case "method" i = i + 1; method = varargin{i}; if (! any (strcmpi (method, {"pearson", "kendall", "spearman"}))) error ("correlation_test: invalid value for method."); endif otherwise error ("correlation_test: invalid Name argument."); endswitch i = i + 1; endwhile n = length (x); if (strcmpi (method, "pearson")) r = corr (x, y); stats.method = "Pearson's product moment correlation"; stats.df = n - 2; stats.corrcoef = r; stats.stat = sqrt (stats.df) .* r / sqrt (1 - r.^2); stats.dist = "Student's t"; cdf = tcdf (stats.stat, stats.df); elseif (strcmpi (method, "kendall")) tau = kendall (x, y); stats.method = "Kendall's rank correlation tau"; stats.df = []; stats.corrcoef = tau; stats.stat = tau / sqrt ((2 * (2*n+5)) / (9*n*(n-1))); stats.dist = "standard normal"; cdf = stdnormal_cdf (stats.stat); else # spearman rho = spearman (x, y); stats.method = "Spearman's rank correlation rho"; stats.df = []; stats.corrcoef = rho; stats.stat = sqrt (n-1) * (rho - 6/(n^3-n)); stats.dist = "standard normal"; cdf = stdnormal_cdf (stats.stat); endif ## Based on the "tail" argument determine the P-value switch lower (tail) case "both" pval = 2 * min (cdf, 1 - cdf); case "right" pval = 1 - cdf; case "left" pval = cdf; endswitch stats.alt = tail; ## Determine the test outcome h = double (pval < alpha); endfunction ## Test input validation %!error correlation_test (); %!error correlation_test (1); %!error ... %! correlation_test ([1 2 NaN]', [2 3 4]'); %!error ... %! correlation_test ([1 2 Inf]', [2 3 4]'); %!error ... %! correlation_test ([1 2 3+i]', [2 3 4]'); %!error ... %! correlation_test ([1 2 3]', [2 3 NaN]'); %!error ... %! correlation_test ([1 2 3]', [2 3 Inf]'); %!error ... %! correlation_test ([1 2 3]', [3 4 3+i]'); %!error ... %! correlation_test ([1 2 3]', [3 4 4 5]'); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "alpha", 0); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "alpha", 1.2); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "alpha", [.02 .1]); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "alpha", "a"); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "some", 0.05); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "tail", "val"); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "alpha", 0.01, "tail", "val"); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "method", 0.01); %!error ... %! correlation_test ([1 2 3]', [2 3 4]', "method", "some"); %!test %! x = [6 7 7 9 10 12 13 14 15 17]; %! y = [19 22 27 25 30 28 30 29 25 32]; %! [h, pval, stats] = correlation_test (x, y); %! assert (stats.corrcoef, corr (x', y'), 1e-14); %! assert (pval, 0.0223, 1e-4); %!test %! x = [6 7 7 9 10 12 13 14 15 17]'; %! y = [19 22 27 25 30 28 30 29 25 32]'; %! [h, pval, stats] = correlation_test (x, y); %! assert (stats.corrcoef, corr (x, y), 1e-14); %! assert (pval, 0.0223, 1e-4); statistics-release-1.7.3/inst/crosstab.m000066400000000000000000000107641475240274700203310ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{t} =} crosstab (@var{x1}, @var{x2}) ## @deftypefnx {statistics} {@var{t} =} crosstab (@var{x1}, @dots{}, @var{xn}) ## @deftypefnx {statistics} {[@var{t}, @var{chisq}, @var{p}, @var{labels}] =} crosstab (@dots{}) ## ## Create a cross-tabulation (contingency table) @var{t} from data vectors. ## ## The inputs @var{x1}, @var{x2}, ... @var{xn} must be vectors of equal length ## with a data type of numeric, logical, char, or string (cell array). ## ## As additional return values @code{crosstab} returns the chi-square statistics ## @var{chisq}, its p-value @var{p} and a cell array @var{labels}, containing ## the labels of each input argument. ## ## @seealso{grp2idx, tabulate} ## @end deftypefn function [t, chisq, p, labels] = crosstab (varargin) ## check input if (nargin < 2) print_usage (); endif v_length = size (varargin{1}, 1); ## main - begin v_reshape = []; # vector of the dimensions of t X = []; # matrix of the indexed input values labels = {}; # cell array of labels for i = 1 : nargin ## If char array, convert to numerical vector try [vector, gnames] = grp2idx (varargin{i}); catch error ("crosstab: x1, x2 ... xn must be vectors."); end_try_catch if ((! isvector (vector)) || (v_length != length (vector))) error ("crosstab: x1, x2 ... xn must be vectors of the same length."); endif X = [X, vector]; for h = 1 : length (gnames) labels{h, i} = gnames{h, 1}; endfor v_reshape(i) = length (unique (vector)); endfor v = unique (X(:, nargin)); t = []; ## core logic, this employs a recursive function "crosstab_recursive" ## given (x1, x2, x3, ... xn) as inputs ## t(i,j,k,...) = sum (x1(:) == v1(i) & x2(:) == v2(j) & ...) for i = 1 : length (v) t = [t, (crosstab_recursive (nargin - 1,... (X(:, nargin) == v(i) | isnan (v(i)) * isnan (X(:, nargin)))))]; endfor t = reshape(t, v_reshape); # build the nargin-dimensional matrix ## additional statistics if (length (v_reshape) > 1) [p, chisq] = chi2test (t); endif ## main - end ## function: crosstab_recursive ## while there are input vectors, let's do iterations over them function t_partial = crosstab_recursive (x_idx, t_parent) y = X(:, x_idx); w = unique (y); t_partial = []; if (x_idx == 1) ## we have reached the last vector, ## let the computation begin for j = 1 : length (w) t_partial = [t_partial, ... sum(t_parent & (y == w(j) | isnan (w(j)) * isnan (y)));]; endfor else ## if there are more vectors, ## just add data and pass it through to the next iteration for j = 1 : length (w) t_partial = [t_partial, ... (crosstab_recursive (x_idx - 1, ... (t_parent & (y == w(j) | isnan (w(j)) * isnan (y)))))]; endfor endif endfunction endfunction ## Test input validation %!error crosstab () %!error crosstab (1) %!error crosstab (ones (2), [1 1]) %!error crosstab ([1 1], ones (2)) %!error crosstab ([1], [1 2]) %!error crosstab ([1 2], [1]) %!test %! load carbig %! [table, chisq, p, labels] = crosstab (cyl4, when, org); %! assert (table(2,3,1), 38); %! assert (labels{3,3}, "Japan"); %!test %! load carbig %! [table, chisq, p, labels] = crosstab (cyl4, when, org); %! assert (table(2,3,2), 17); %! assert (labels{1,3}, "USA"); statistics-release-1.7.3/inst/crossval.m000066400000000000000000000141421475240274700203370ustar00rootroot00000000000000## Copyright (C) 2014 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{results} =} crossval (@var{f}, @var{X}, @var{y}) ## @deftypefnx {statistics} {@var{results} =} crossval (@var{f}, @var{X}, @var{y}, @var{name}, @var{value}) ## ## Perform cross validation on given data. ## ## @var{f} should be a function that takes 4 inputs @var{xtrain}, @var{ytrain}, ## @var{xtest}, @var{ytest}, fits a model based on @var{xtrain}, @var{ytrain}, ## applies the fitted model to @var{xtest}, and returns a goodness of fit ## measure based on comparing the predicted and actual @var{ytest}. ## @code{crossval} returns an array containing the values returned by @var{f} ## for every cross-validation fold or resampling applied to the given data. ## ## @var{X} should be an @var{n} by @var{m} matrix of predictor values ## ## @var{y} should be an @var{n} by @var{1} vector of predicand values ## ## Optional arguments may include name-value pairs as follows: ## ## @table @asis ## @item @qcode{"KFold"} ## Divide set into @var{k} equal-size subsets, using each one successively ## for validation. ## ## @item @qcode{"HoldOut"} ## Divide set into two subsets, training and validation. If the value ## @var{k} is a fraction, that is the fraction of values put in the ## validation subset (by default @var{k}=0.1); if it is a positive integer, ## that is the number of values in the validation subset. ## ## @item @qcode{"LeaveOut"} ## Leave-one-out partition (each element is placed in its own subset). ## The value is ignored. ## ## @item @qcode{"Partition"} ## The value should be a @var{cvpartition} object. ## ## @item @qcode{"Given"} ## The value should be an @var{n} by @var{1} vector specifying in which ## partition to put each element. ## ## @item @qcode{"stratify"} ## The value should be an @var{n} by @var{1} vector containing class ## designations for the elements, in which case the @qcode{"KFold"} and ## @qcode{"HoldOut"} partitionings attempt to ensure each partition ## represents the classes proportionately. ## ## @item @qcode{"mcreps"} ## The value should be a positive integer specifying the number of times ## to resample based on different partitionings. Currently only works with ## the partition type @qcode{"HoldOut"}. ## ## @end table ## ## Only one of @qcode{"KFold"}, @qcode{"HoldOut"}, @qcode{"LeaveOut"}, ## @qcode{"Given"}, @qcode{"Partition"} should be specified. If none is ## specified, the default is @qcode{"KFold"} with @var{k} = 10. ## ## @seealso{cvpartition} ## @end deftypefn function results = crossval (f, X, y, varargin) [n, m] = size (X); if (numel (y) != n) error ("X, y sizes incompatible") endif ## extract optional parameter-value argument pairs if (numel (varargin) > 1) vargs = varargin; nargs = numel (vargs); values = vargs(2:2:nargs); names = vargs(1:2:nargs)(1:numel(values)); validnames = {"KFold", "HoldOut", "LeaveOut", "Partition", ... "Given", "stratify", "mcreps"}; for i = 1:numel (names) names(i) = validatestring (names(i){:}, validnames); end for i = 1:numel(validnames) name = validnames(i){:}; name_pos = strmatch (name, names); if (! isempty (name_pos)) eval ([name " = values(name_pos){:};"]) endif endfor endif ## construct CV partition if exist ("Partition", "var") P = Partition; elseif exist ("Given", "var") P = cvpartition (Given, "Given"); elseif exist ("KFold", "var") if (! exist ("stratify", "var")) stratify = n; endif P = cvpartition (stratify, "KFold", KFold); elseif (exist ("HoldOut", "var")) if (! exist ("stratify", "var")) stratify = n; endif P = cvpartition (stratify, "HoldOut", HoldOut); if (! exist ("mcreps", "var") || isempty (mcreps)) mcreps = 1; endif elseif (exist ("LeaveOut", "var")) P = cvpartition (n, "LeaveOut"); else #KFold if (! exist ("stratify", "var")) stratify = n; endif P = cvpartition (stratify, "KFold"); endif nr = get (P, "NumTestSets"); # number of test sets to do cross validation on nreps = 1; if (strcmp (get (P, "Type"), "holdout") && exist ("mcreps", "var") && mcreps > 1) nreps = mcreps; endif results = nan (nreps, nr); for rep = 1:nreps if (rep > 1) P = repartition (P); endif for i = 1:nr inds_train = training (P, i); inds_test = test (P, i); result = f (X(inds_train, :), y(inds_train), X(inds_test, :), y(inds_test)); results(rep, i) = result; endfor endfor endfunction %!test %! load fisheriris %! y = meas(:, 1); %! X = [ones(size(y)) meas(:, 2:4)]; %! f = @(X1, y1, X2, y2) meansq (y2 - X2*regress(y1, X1)); %! results0 = crossval (f, X, y); %! results1 = crossval (f, X, y, 'KFold', 10); %! folds = 5; %! results2 = crossval (f, X, y, 'KFold', folds); %! results3 = crossval (f, X, y, 'Partition', cvpartition (numel (y), 'KFold', folds)); %! results4 = crossval (f, X, y, 'LeaveOut', 1); %! mcreps = 2; n_holdout = 20; %! results5 = crossval (f, X, y, 'HoldOut', n_holdout, 'mcreps', mcreps); %! %! ## ensure equal representation of iris species in the training set -- tends %! ## to slightly reduce cross-validation mean square error %! results6 = crossval (f, X, y, 'KFold', 5, 'stratify', grp2idx(species)); %! %! assert (results0, results1, 2e-15); %! assert (results2, results3, 5e-17); %! assert (size(results4), [1 numel(y)]); %! assert (mean(results4), 0.1018, 1e-4); %! assert (size(results5), [mcreps 1]); statistics-release-1.7.3/inst/datasample.m000066400000000000000000000164271475240274700206260ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} datasample (@var{data}, @var{k}) ## @deftypefnx {statistics} {@var{y} =} datasample (@var{data}, @var{k}, @var{dim}) ## @deftypefnx {statistics} {@var{y} =} datasample (@dots{}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{y} @var{idcs}] =} datasample (@dots{}) ## ## Randomly sample data. ## ## Return @var{k} observations randomly sampled from @var{data}. @var{data} can ## be a vector or a matrix of any data. When @var{data} is a matrix or a ## n-dimensional array, the samples are the subarrays of size n - 1, taken along ## the dimension @var{dim}. The default value for @var{dim} is 1, that is the ## row vectors when sampling a matrix. ## ## Output @var{y} is the returned sampled data. Optional output @var{idcs} is ## the vector of the indices to build @var{y} from @var{data}. ## ## Additional options are set through pairs of parameter name and value. ## Available parameters are: ## ## @table @code ## @item @qcode{Replace} ## a logical value that can be @code{true} (default) or @code{false}: when set ## to @code{true}, @code{datasample} returns data sampled with replacement. ## ## @item @qcode{Weigths} ## a vector of positive numbers that sets the probability of each element. It ## must have the same size as @var{data} along dimension @var{dim}. ## ## @end table ## ## ## @end deftypefn ## ## @seealso{rand, randi, randperm, randsample} function [y, idcs] = datasample (data, k, varargin) ## check input if ( nargin < 2 ) print_usage (); endif ## data: some data, any type, any format but cell ## MATLAB compatibility: there are no "table" or "dataset array" types in ## Octave if (iscell (data)) error ("datasample: data must be a vector or matrix"); endif ## k, a positive integer if ((! isnumeric (k) || ! isscalar (k)) || (! (floor (k) == k)) || (k <= 0)) error ("datasample: k must be a positive integer scalar"); endif dim = 1; replace = true; weights = []; if ( nargin > 2 ) pair_index = 1; if (! ischar (varargin{1})) ## it must be dim dim = varargin{1}; ## the (Name, Value) pairs start further pair_index += 1; ## dim, another positive integer if ((! isscalar (dim)) || (! (floor (dim) == dim)) || (dim <= 0)) error ("datasample: DIM must be a positive integer scalar"); endif endif ## (Name, Value) pairs while (pair_index < (nargin - 2)) switch (lower (varargin{pair_index})) case "replace" if (! islogical (varargin{pair_index + 1})) error ("datasample: expected a logical value for 'Replace'"); endif replace = varargin{pair_index + 1}; case "weights" if ((! isnumeric (varargin{pair_index + 1})) || (! isvector (varargin{pair_index + 1})) || (any (varargin{pair_index + 1} < 0))) error (["datasample: the sampling weights must be defined as a " ... "vector of positive values"]); endif weights = varargin{pair_index + 1}; otherwise error ("datasample: unknown property %s", varargin{pair_index}); endswitch pair_index += 2; endwhile endif ## get the size of the population to sample if (isvector (data)) imax = length (data); else imax = size (data, dim); endif if (isempty (weights)) ## all elements have the same probability of being chosen ## this is easy ## with or without replacement if (replace) idcs = randi (imax, k, 1); else idcs = randperm (imax, k); endif else ## first check if the weights vector is right if (imax != length (weights)) error (["datasample: the size of the vector of sampling weights must"... " be equal to the size of the sampled data"]); endif if (replace) ## easy case: ## normalize the weights, weights_n = cumsum (weights ./ sum (weights)); weights_n(end) = 1; # just to be sure ## then choose k numbers uniformly between 0 and 1 samples = rand (k, 1); ## we have subdivided the space between 0 and 1 accordingly to the ## weights vector: we have just to map back the random numbers to the ## indices of the orginal dataset for iter = 1 : k idcs(iter) = find (weights_n >= samples(iter), 1); endfor else ## complex case ## choose k numbers uniformly between 0 and 1 samples = rand (k, 1); for iter = 1 : k ## normalize the weights weights_n = cumsum (weights ./ sum (weights)); weights_n(end) = 1; # just to be sure idcs(iter) = find (weights_n >= samples(iter), 1); ## remove the element from the set, i. e. set its probability to zero weights(idcs(iter)) = 0; endfor endif endif ## let's get the actual data from the original set if (isvector (data)) ## data is a vector y = data(idcs); else vS = size (data); if (length (vS) == 2) ## data is a 2-dimensional matrix if (dim == 1) y = data(idcs, :); else y = data(:, idcs); endif else ## data is an n-dimensional matrix s = "y = data("; for iter = 1 : length (vS) if (iter == dim) s = [s "idcs,"]; else s = [s ":,"]; endif endfor s = [s ":);"]; eval (s); endif endif endfunction ## some tests %!error datasample(); %!error datasample(1); %!error datasample({1, 2, 3}, 1); %!error datasample([1 2], -1); %!error datasample([1 2], 1.5); %!error datasample([1 2], [1 1]); %!error datasample([1 2], 'g', [1 1]); %!error datasample([1 2], 1, -1); %!error datasample([1 2], 1, 1.5); %!error datasample([1 2], 1, [1 1]); %!error datasample([1 2], 1, 1, "Replace", -2); %!error datasample([1 2], 1, 1, "Weights", "abc"); %!error datasample([1 2], 1, 1, "Weights", [1 -2 3]); %!error datasample([1 2], 1, 1, "Weights", ones (2)); %!error datasample([1 2], 1, 1, "Weights", [1 2 3]); %!test %! dat = randn (10, 4); %! assert (size (datasample (dat, 3, 1)), [3 4]); %!test %! dat = randn (10, 4); %! assert (size (datasample (dat, 3, 2)), [10 3]); statistics-release-1.7.3/inst/datasets/000077500000000000000000000000001475240274700201335ustar00rootroot00000000000000statistics-release-1.7.3/inst/datasets/acetylene.mat000066400000000000000000000045241475240274700226140ustar00rootroot00000000000000Octave-1-L Descriptionÿ sq_stringþÿÿÿi= M xxxy SKC RMT= u 123: ouh eah l ::: une freA t C rum eq c i RRCo cgi ruAe p eaon eic eamt l atnv :,a nrey e cete Tl cdrl t ar . etie r oocs ,E :,cn e rfti n ae g o Tg Dn r tHtn .i . D e e2i n ,Sa s m mo Te tt s ptef ae Raa i eo mr .t o r (n ui s= n ans- rn St= t-eh ag ni d uhce , ec a reop P ei t epnt "r ,a a tda No n (asn eg ", w dn)e wr R i ee e iv t g t As d. h r( o cs g2 em e, e9 c eo a t o sl c yv Rn r e e l. eo r c t e5 g. e er y n7 r1 l na l e e a tt e ( s( t ii n P1 s1 e go e r9 i9 d r) o6 o7 a ( c1 n5 p d % e) ) r e ) s, i, e ) s n d p p i Up Pp c s. r. t e4 a3 o s3 c- r - t2 s H4 i0 y9 c. d. e r , o " g e n D i l u t i o n , " x1ÿmatrixþÿÿÿP”@P”@P”@P”@P”@P”@À’@À’@À’@À’@À’@À’@0‘@0‘@0‘@0‘@x2ÿmatrixþÿÿÿ@"@&@+@1@7@333333@@&@+@1@7@333333@@&@1@x3ÿmatrixþÿÿÿú~j¼t“ˆ?ú~j¼t“ˆ?Zd;ßO‡?9´Èv¾ŸŠ?ÙÎ÷S㥋?ú~j¼t“ˆ?{®Gáz¤?Ûù~j¼t£?ü©ñÒMb ?9´Èv¾Ÿš?œÄ °rh¡?Ë¡E¶óý¤?/Ý$µ?J +‡¹?Zd;ßO·?j¼t“¶?yÿmatrixþÿÿÿ€H@š™™™™I@@I@@H@ÀG@@F@<@€?@@A@€A@C@@C@.@1@€4@€=@statistics-release-1.7.3/inst/datasets/arrhythmia.mat000066400000000000000000037416671475240274700230360ustar00rootroot00000000000000Octave-1-L Descriptionÿ sq_stringþÿÿÿEChSY Nate ortei mdp s121ii:V -6na/at=1 ac/rh 5=l aNen ara o=uarcmc ntrhelavcthisaralryv srraitefshisbh.o yosumiratuitic thsfeasntm is .arice dumialdaaceb a rtisu scea. t sa eoe escfdf seoru r sdo/2a o emm7n f d l9g t/ i a ahdin r seang r tp h nUauf y uCstr t mIe o h e tvm m rmsa i ia/r1 a ccAi hrat virbo anhl reye1 i ts6 alh.: bem lai era sn i wn ig t hr e vp ao ls ui et so r 0y : a n d 1VarNamesÿcellþÿÿÿÿ sq_stringþÿÿÿ Age, yearsÿ sq_stringþÿÿÿSex (0=male, 1=female)ÿ sq_stringþÿÿÿ Height, cmÿ sq_stringþÿÿÿ Weight, kgÿ sq_stringþÿÿÿ QRS durationÿ sq_stringþÿÿÿ P-R intervalÿ sq_stringþÿÿÿ Q-T intervalÿ sq_stringþÿÿÿ T intervalÿ sq_stringþÿÿÿ P intervalÿ sq_stringþÿÿÿ QRS angleÿ sq_stringþÿÿÿT angleÿ sq_stringþÿÿÿP angleÿ sq_stringþÿÿÿ QRST angleÿ sq_stringþÿÿÿJ angleÿ sq_stringþÿÿÿHeart rate per minuteÿ sq_stringþÿÿÿDI Q wave widthÿ sq_stringþÿÿÿDI R wave widthÿ sq_stringþÿÿÿDI S wave widthÿ sq_stringþÿÿÿDI R' wave widthÿ sq_stringþÿÿÿDI S' wave widthÿ sq_stringþÿÿÿDI N of intrinsic deflectionsÿ sq_stringþÿÿÿDI Existence of ragged R waveÿ sq_stringþÿÿÿ-DI Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿDI Existence of ragged P waveÿ sq_stringþÿÿÿ-DI Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿDI Existence of ragged T waveÿ sq_stringþÿÿÿ-DI Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿDII Q wave widthÿ sq_stringþÿÿÿDII R wave widthÿ sq_stringþÿÿÿDII S wave widthÿ sq_stringþÿÿÿDII R' wave widthÿ sq_stringþÿÿÿDII S' wave widthÿ sq_stringþÿÿÿDII N of intrinsic deflectionsÿ sq_stringþÿÿÿDII Existence of ragged R waveÿ sq_stringþÿÿÿ.DII Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿDII Existence of ragged P waveÿ sq_stringþÿÿÿ.DII Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿDII Existence of ragged T waveÿ sq_stringþÿÿÿ.DII Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿDIII Q wave widthÿ sq_stringþÿÿÿDIII R wave widthÿ sq_stringþÿÿÿDIII S wave widthÿ sq_stringþÿÿÿDIII R' wave widthÿ sq_stringþÿÿÿDIII S' wave widthÿ sq_stringþÿÿÿDIII N of intrinsic deflectionsÿ sq_stringþÿÿÿDIII Existence of ragged R waveÿ sq_stringþÿÿÿ/DIII Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿDIII Existence of ragged P waveÿ sq_stringþÿÿÿ/DIII Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿDIII Existence of ragged T waveÿ sq_stringþÿÿÿ/DIII Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿAVR Q wave widthÿ sq_stringþÿÿÿAVR R wave widthÿ sq_stringþÿÿÿAVR S wave widthÿ sq_stringþÿÿÿAVR R' wave widthÿ sq_stringþÿÿÿAVR S' wave widthÿ sq_stringþÿÿÿAVR N of intrinsic deflectionsÿ sq_stringþÿÿÿAVR Existence of ragged R waveÿ sq_stringþÿÿÿ.AVR Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿAVR Existence of ragged P waveÿ sq_stringþÿÿÿ.AVR Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿAVR Existence of ragged T waveÿ sq_stringþÿÿÿ.AVR Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿAVL Q wave widthÿ sq_stringþÿÿÿAVL R wave widthÿ sq_stringþÿÿÿAVL S wave widthÿ sq_stringþÿÿÿAVL R' wave widthÿ sq_stringþÿÿÿAVL S' wave widthÿ sq_stringþÿÿÿAVL N of intrinsic deflectionsÿ sq_stringþÿÿÿAVL Existence of ragged R waveÿ sq_stringþÿÿÿ.AVL Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿAVL Existence of ragged P waveÿ sq_stringþÿÿÿ.AVL Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿAVL Existence of ragged T waveÿ sq_stringþÿÿÿ.AVL Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿAVF Q wave widthÿ sq_stringþÿÿÿAVF R wave widthÿ sq_stringþÿÿÿAVF S wave widthÿ sq_stringþÿÿÿAVF R' wave widthÿ sq_stringþÿÿÿAVF S' wave widthÿ sq_stringþÿÿÿAVF N of intrinsic deflectionsÿ sq_stringþÿÿÿAVF Existence of ragged R waveÿ sq_stringþÿÿÿ.AVF Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿAVF Existence of ragged P waveÿ sq_stringþÿÿÿ.AVF Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿAVF Existence of ragged T waveÿ sq_stringþÿÿÿ.AVF Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿV1 Q wave widthÿ sq_stringþÿÿÿV1 R wave widthÿ sq_stringþÿÿÿV1 S wave widthÿ sq_stringþÿÿÿV1 R' wave widthÿ sq_stringþÿÿÿV1 S' wave widthÿ sq_stringþÿÿÿV1 N of intrinsic deflectionsÿ sq_stringþÿÿÿV1 Existence of ragged R waveÿ sq_stringþÿÿÿ-V1 Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿV1 Existence of ragged P waveÿ sq_stringþÿÿÿ-V1 Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿV1 Existence of ragged T waveÿ sq_stringþÿÿÿ-V1 Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿV2 Q wave widthÿ sq_stringþÿÿÿV2 R wave widthÿ sq_stringþÿÿÿV2 S wave widthÿ sq_stringþÿÿÿV2 R' wave widthÿ sq_stringþÿÿÿV2 S' wave widthÿ sq_stringþÿÿÿV2 N of intrinsic deflectionsÿ sq_stringþÿÿÿV2 Existence of ragged R waveÿ sq_stringþÿÿÿ-V2 Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿV2 Existence of ragged P waveÿ sq_stringþÿÿÿ-V2 Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿV2 Existence of ragged T waveÿ sq_stringþÿÿÿ-V2 Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿV3 Q wave widthÿ sq_stringþÿÿÿV3 R wave widthÿ sq_stringþÿÿÿV3 S wave widthÿ sq_stringþÿÿÿV3 R' wave widthÿ sq_stringþÿÿÿV3 S' wave widthÿ sq_stringþÿÿÿV3 N of intrinsic deflectionsÿ sq_stringþÿÿÿV3 Existence of ragged R waveÿ sq_stringþÿÿÿ-V3 Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿV3 Existence of ragged P waveÿ sq_stringþÿÿÿ-V3 Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿV3 Existence of ragged T waveÿ sq_stringþÿÿÿ-V3 Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿV4 Q wave widthÿ sq_stringþÿÿÿV4 R wave widthÿ sq_stringþÿÿÿV4 S wave widthÿ sq_stringþÿÿÿV4 R' wave widthÿ sq_stringþÿÿÿV4 S' wave widthÿ sq_stringþÿÿÿV4 N of intrinsic deflectionsÿ sq_stringþÿÿÿV4 Existence of ragged R waveÿ sq_stringþÿÿÿ-V4 Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿV4 Existence of ragged P waveÿ sq_stringþÿÿÿ-V4 Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿV4 Existence of ragged T waveÿ sq_stringþÿÿÿ-V4 Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿV5 Q wave widthÿ sq_stringþÿÿÿV5 R wave widthÿ sq_stringþÿÿÿV5 S wave widthÿ sq_stringþÿÿÿV5 R' wave widthÿ sq_stringþÿÿÿV5 S' wave widthÿ sq_stringþÿÿÿV5 N of intrinsic deflectionsÿ sq_stringþÿÿÿV5 Existence of ragged R waveÿ sq_stringþÿÿÿ-V5 Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿV5 Existence of ragged P waveÿ sq_stringþÿÿÿ-V5 Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿV5 Existence of ragged T waveÿ sq_stringþÿÿÿ-V5 Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿV6 Q wave widthÿ sq_stringþÿÿÿV6 R wave widthÿ sq_stringþÿÿÿV6 S wave widthÿ sq_stringþÿÿÿV6 R' wave widthÿ sq_stringþÿÿÿV6 S' wave widthÿ sq_stringþÿÿÿV6 N of intrinsic deflectionsÿ sq_stringþÿÿÿV6 Existence of ragged R waveÿ sq_stringþÿÿÿ-V6 Existence of diphasic derivation of R waveÿ sq_stringþÿÿÿV6 Existence of ragged P waveÿ sq_stringþÿÿÿ-V6 Existence of diphasic derivation of P waveÿ sq_stringþÿÿÿV6 Existence of ragged T waveÿ sq_stringþÿÿÿ-V6 Existence of diphasic derivation of T waveÿ sq_stringþÿÿÿDI JJ wave amplitudeÿ sq_stringþÿÿÿDI Q wave amplitudeÿ sq_stringþÿÿÿDI R wave amplitudeÿ sq_stringþÿÿÿDI S wave amplitudeÿ sq_stringþÿÿÿDI R' wave amplitudeÿ sq_stringþÿÿÿDI S' wave amplitudeÿ sq_stringþÿÿÿDI P wave amplitudeÿ sq_stringþÿÿÿDI T wave amplitudeÿ sq_stringþÿÿÿDI QRSAÿ sq_stringþÿÿÿDI QRSTAÿ sq_stringþÿÿÿDII JJ wave amplitudeÿ sq_stringþÿÿÿDII Q wave amplitudeÿ sq_stringþÿÿÿDII R wave amplitudeÿ sq_stringþÿÿÿDII S wave amplitudeÿ sq_stringþÿÿÿDII R' wave amplitudeÿ sq_stringþÿÿÿDII S' wave amplitudeÿ sq_stringþÿÿÿDII P wave amplitudeÿ sq_stringþÿÿÿDII T wave amplitudeÿ sq_stringþÿÿÿDII QRSAÿ sq_stringþÿÿÿ DII QRSTAÿ sq_stringþÿÿÿDIII JJ wave amplitudeÿ sq_stringþÿÿÿDIII Q wave amplitudeÿ sq_stringþÿÿÿDIII R wave amplitudeÿ sq_stringþÿÿÿDIII S wave amplitudeÿ sq_stringþÿÿÿDIII R' wave amplitudeÿ sq_stringþÿÿÿDIII S' wave amplitudeÿ sq_stringþÿÿÿDIII P wave amplitudeÿ sq_stringþÿÿÿDIII T wave amplitudeÿ sq_stringþÿÿÿ DIII QRSAÿ sq_stringþÿÿÿ DIII QRSTAÿ sq_stringþÿÿÿAVR JJ wave amplitudeÿ sq_stringþÿÿÿAVR Q wave amplitudeÿ sq_stringþÿÿÿAVR R wave amplitudeÿ sq_stringþÿÿÿAVR S wave amplitudeÿ sq_stringþÿÿÿAVR R' wave amplitudeÿ sq_stringþÿÿÿAVR S' wave amplitudeÿ sq_stringþÿÿÿAVR P wave amplitudeÿ sq_stringþÿÿÿAVR T wave amplitudeÿ sq_stringþÿÿÿAVR QRSAÿ sq_stringþÿÿÿ AVR QRSTAÿ sq_stringþÿÿÿAVL JJ wave amplitudeÿ sq_stringþÿÿÿAVL Q wave amplitudeÿ sq_stringþÿÿÿAVL R wave amplitudeÿ sq_stringþÿÿÿAVL S wave amplitudeÿ sq_stringþÿÿÿAVL R' wave amplitudeÿ sq_stringþÿÿÿAVL S' wave amplitudeÿ sq_stringþÿÿÿAVL P wave amplitudeÿ sq_stringþÿÿÿAVL T wave amplitudeÿ sq_stringþÿÿÿAVL QRSAÿ sq_stringþÿÿÿ AVL QRSTAÿ sq_stringþÿÿÿAVF JJ wave amplitudeÿ sq_stringþÿÿÿAVF Q wave amplitudeÿ sq_stringþÿÿÿAVF R wave amplitudeÿ sq_stringþÿÿÿAVF S wave amplitudeÿ sq_stringþÿÿÿAVF R' wave amplitudeÿ sq_stringþÿÿÿAVF S' wave amplitudeÿ sq_stringþÿÿÿAVF P wave amplitudeÿ sq_stringþÿÿÿAVF T wave amplitudeÿ sq_stringþÿÿÿAVF QRSAÿ sq_stringþÿÿÿ AVF QRSTAÿ sq_stringþÿÿÿV1 JJ wave amplitudeÿ sq_stringþÿÿÿV1 Q wave amplitudeÿ sq_stringþÿÿÿV1 R wave amplitudeÿ sq_stringþÿÿÿV1 S wave amplitudeÿ sq_stringþÿÿÿV1 R' wave amplitudeÿ sq_stringþÿÿÿV1 S' wave amplitudeÿ sq_stringþÿÿÿV1 P wave amplitudeÿ sq_stringþÿÿÿV1 T wave amplitudeÿ sq_stringþÿÿÿV1 QRSAÿ sq_stringþÿÿÿV1 QRSTAÿ sq_stringþÿÿÿV2 JJ wave amplitudeÿ sq_stringþÿÿÿV2 Q wave amplitudeÿ sq_stringþÿÿÿV2 R wave amplitudeÿ sq_stringþÿÿÿV2 S wave amplitudeÿ sq_stringþÿÿÿV2 R' wave amplitudeÿ sq_stringþÿÿÿV2 S' wave amplitudeÿ sq_stringþÿÿÿV2 P wave amplitudeÿ sq_stringþÿÿÿV2 T wave amplitudeÿ sq_stringþÿÿÿV2 QRSAÿ sq_stringþÿÿÿV2 QRSTAÿ sq_stringþÿÿÿV3 JJ wave amplitudeÿ sq_stringþÿÿÿV3 Q wave amplitudeÿ sq_stringþÿÿÿV3 R wave amplitudeÿ sq_stringþÿÿÿV3 S wave amplitudeÿ sq_stringþÿÿÿV3 R' wave amplitudeÿ sq_stringþÿÿÿV3 S' wave amplitudeÿ sq_stringþÿÿÿV3 P wave amplitudeÿ sq_stringþÿÿÿV3 T wave amplitudeÿ sq_stringþÿÿÿV3 QRSAÿ sq_stringþÿÿÿV3 QRSTAÿ sq_stringþÿÿÿV4 JJ wave amplitudeÿ sq_stringþÿÿÿV4 Q wave amplitudeÿ sq_stringþÿÿÿV4 R wave amplitudeÿ sq_stringþÿÿÿV4 S wave amplitudeÿ sq_stringþÿÿÿV4 R' wave amplitudeÿ sq_stringþÿÿÿV4 S' wave amplitudeÿ sq_stringþÿÿÿV4 P wave amplitudeÿ sq_stringþÿÿÿV4 T wave amplitudeÿ sq_stringþÿÿÿV4 QRSAÿ sq_stringþÿÿÿV4 QRSTAÿ sq_stringþÿÿÿV5 JJ wave amplitudeÿ sq_stringþÿÿÿV5 Q wave amplitudeÿ sq_stringþÿÿÿV5 R wave amplitudeÿ sq_stringþÿÿÿV5 S wave amplitudeÿ sq_stringþÿÿÿV5 R' wave amplitudeÿ sq_stringþÿÿÿV5 S' wave amplitudeÿ sq_stringþÿÿÿV5 P wave amplitudeÿ sq_stringþÿÿÿV5 T wave amplitudeÿ sq_stringþÿÿÿV5 QRSAÿ sq_stringþÿÿÿV5 QRSTAÿ sq_stringþÿÿÿV6 JJ wave amplitudeÿ sq_stringþÿÿÿV6 Q wave amplitudeÿ sq_stringþÿÿÿV6 R wave amplitudeÿ sq_stringþÿÿÿV6 S wave amplitudeÿ sq_stringþÿÿÿV6 R' wave amplitudeÿ sq_stringþÿÿÿV6 S' wave amplitudeÿ sq_stringþÿÿÿV6 P wave amplitudeÿ sq_stringþÿÿÿV6 T wave amplitudeÿ sq_stringþÿÿÿV6 QRSAÿ sq_stringþÿÿÿV6 QRSTAXÿmatrixþÿÿÿÄÀR@L@K@€K@ÀR@*@D@€H@F@I@O@€F@K@>@F@€G@€G@G@@R@€L@<@€F@B@€L@D@F@A@?@L@€I@€J@M@I@J@@Q@F@I@€A@O@€F@€E@D@>@A@D@ÀR@@Q@>@€D@A@ÀR@<@€C@8@J@R@C@E@G@@@ð?€A@B@;@H@F@K@:@€F@P@€B@€B@?@G@€A@A@€F@€B@L@ÀQ@€B@€L@ÀS@€I@€O@ÀR@3@D@Q@@R@J@P@€O@O@9@€K@€@@€B@J@B@€I@€C@3@;@B@€O@G@€L@1@I@ÀQ@€E@€@@"@=@€C@€R@<@8@M@@Q@I@R@ÀQ@€B@9@M@€N@€J@G@L@H@€C@G@€D@D@€G@€G@€A@€I@€A@ð?€H@?@A@B@2@A@€C@1@H@@@B@A@;@€F@€F@N@€M@G@E@E@K@8@D@9@Q@3@@R@D@F@I@@@L@€K@K@P@;@€P@L@€O@2@F@€L@@P@€N@€G@O@6@€P@€H@N@@P@B@F@;@5@G@*@M@€Q@P@I@€A@€@@G@€J@O@E@€G@&@2@€O@€J@€H@>@1@€P@I@@S@€G@E@L@ÀT@E@ÀP@€B@€E@B@€H@R@T@6@€L@G@€F@€E@B@L@C@€E@O@G@€H@R@N@H@J@F@G@.@M@G@€M@€P@Q@€J@€F@H@F@€@@€A@€C@C@A@€A@M@I@€E@I@R@H@€N@€O@€N@P@@@M@H@?@€D@€H@€L@€A@@R@@P@€@@€A@€L@I@N@€F@D@€K@F@€@@€M@@(@€I@€M@€O@€R@ÀP@?@,@€B@€J@O@D@€D@T@@Q@€Q@P@€L@J@€K@€B@@:@€M@K@C@H@M@ÀP@P@€M@€M@@P@E@€B@3@€G@?@€@@€C@E@K@F@H@R@B@@@4@@T@A@O@P@€J@H@:@€F@€G@€G@L@D@K@O@D@€G@€L@C@G@€E@€M@P@>@Q@G@H@R@.@€Q@ÀP@K@ÀP@ @€P@€L@B@€G@€A@ÀR@€S@€J@F@€Q@€P@B@€I@0@€M@€N@€F@>@€G@ÀQ@€K@"@€P@@O@5@S@9@M@€P@€L@P@€D@€J@ÀP@>@;@@P@I@;@M@€O@=@€I@@B@€A@M@P@ @&@€G@&@€Q@4@€C@@@€A@€B@€H@€B@€B@@P@€D@=@€F@4@€J@€B@B@@@€S@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?Àg@ d@€e@àe@Àg@ e@d@@d@e@àd@@e@ d@€e@@e@d@Àb@`e@Àc@ d@Àd@d@ e@ c@ d@ c@ e@@e@d@€d@d@àe@`d@d@`c@f@d@€e@€d@`d@àe@ c@d@d@ d@d@€c@d@Àc@àc@àc@€c@€c@€e@`d@ d@d@d@e@ d@d@€[@d@Àc@@d@`c@`c@d@d@Àb@€c@`c@Àf@ d@`d@Àc@ d@ f@`e@ d@€c@€c@ f@Àb@ d@d@àc@ d@@d@ d@Àc@`c@`c@`d@ d@e@ g@Àb@ c@`c@d@€c@`c@ c@@e@d@àe@ d@ c@@e@g@@d@f@`d@€`@€e@d@€e@àc@@d@@e@@c@€d@@e@@e@àc@@d@@g@ g@`c@`d@ d@ d@ c@ f@`c@€e@e@@d@@d@d@@e@`ˆ@Àd@Àb@àe@e@àe@`c@d@d@€c@ e@€e@àd@ d@ e@@d@d@ d@ c@@e@d@@e@f@ d@@d@e@€c@d@d@@f@e@`d@e@€c@d@d@`c@àd@@d@€c@àe@`f@àe@`c@@e@`d@@e@ d@€c@Àd@d@€f@€e@ e@`c@Àd@e@ `@`c@d@d@àc@d@e@@d@@f@f@ c@f@_@€f@Àe@ e@d@d@€c@d@@e@ d@ d@@f@ e@d@ d@`e@e@€e@@e@@e@`e@ e@`d@€d@ d@ e@f@ b@@e@ d@d@`c@ f@Àd@ d@`c@ c@ c@d@ d@@e@àe@@e@d@d@€d@ d@@e@@f@@e@ d@d@d@@e@Àb@`c@d@Àe@`d@d@d@Àc@`e@d@@c@€c@ c@Àb@d@e@àe@@e@àe@ d@ d@àe@d@`c@àe@`d@Àc@@e@@f@ e@d@d@d@@`@ d@@e@ d@€d@@e@f@d@àe@àe@`e@@e@f@€f@d@d@ d@àd@@e@ƒ@Àe@@e@@e@@Z@€f@ d@€d@@e@@e@Àd@e@€c@`c@ d@`e@€g@àe@ d@d@ d@ d@d@€e@@e@ c@@d@ d@`e@Àc@@g@ d@ d@@f@ e@€c@d@@e@ e@àe@àe@@e@Àb@d@ c@@e@`d@@e@€e@@f@ d@d@@e@ f@@b@ d@Àf@@c@`d@d@`d@d@d@^@e@ d@ g@ e@ d@@d@c@€c@@e@@f@d@f@ d@d@Àe@€c@Àc@d@àe@ d@d@^@`c@À_@ d@€c@Àb@d@€b@d@€e@d@@e@d@ d@àd@àd@€e@`d@d@d@àd@@d@@g@À]@`e@d@d@d@@`@@a@Àd@€a@àd@@f@€d@€d@`c@àe@e@f@d@d@@c@Àd@àe@ c@d@Àg@Àd@`c@d@T@P@ÀW@€W@T@€I@J@K@L@ÀP@R@€U@M@@R@V@H@€M@M@€O@R@M@ÀP@ÀR@€M@€K@T@@R@K@@P@ÀT@@U@Q@@R@€Q@ÀR@€F@T@€W@N@T@ÀQ@ÀR@O@€N@€I@€K@ÀQ@€L@€K@Q@€J@J@S@€J@T@€Q@€O@ÀS@€P@M@$@€I@M@€J@€K@@P@€O@@P@ÀQ@€T@N@€V@@R@Y@N@@P@R@@U@@R@L@J@Q@N@S@T@€M@I@@P@€Q@R@Z@V@O@€Q@€K@@Z@€K@O@Z@€Q@N@€M@D@€P@@P@T@€K@€N@€N@X@R@€V@ÀR@€@@@Q@€F@€R@L@ÀR@ÀR@€M@ÀR@€R@J@L@€S@2@ÀW@€O@€U@K@€Q@L@€V@L@N@P@€R@Q@T@T@@@R@P@N@ÀR@N@K@O@K@O@ÀR@€V@N@€I@@R@€N@ÀT@€Q@€Q@T@Q@€S@R@€J@H@€M@€G@T@€Q@@V@T@@P@S@€S@P@ÀP@€K@€Q@P@€Q@€P@@U@T@Q@€S@€K@@U@J@T@O@€N@ÀS@@T@ÀQ@€F@Q@N@>@N@€V@T@€Q@€I@@Z@@P@U@@R@@P@ÀW@9@€O@ÀS@f@T@@P@D@N@T@@S@ÀP@ÀR@U@€K@K@ÀP@€T@€V@@U@ÀS@€R@@U@I@P@ÀT@€Q@R@H@T@€X@K@€S@@U@ÀU@Q@@P@ÀW@€K@P@N@€J@€S@T@Q@_@€J@P@€R@T@ÀR@@U@ÀP@€R@ÀW@€F@€K@O@€V@@U@€Z@€Q@J@€Q@€O@€O@@R@M@€S@€N@T@@X@T@@T@ÀW@M@@P@@Q@M@€Q@M@ÀR@€R@S@ÀR@@P@P@R@>@€G@€T@@P@K@ÀP@T@€K@€M@ÀQ@ÀS@€[@€R@V@€Q@N@€Q@R@@P@$@€T@R@€V@(@@X@€W@ÀT@@Q@€R@€Q@@P@S@R@€R@@U@ÀV@V@@P@@P@L@M@€Q@ÀT@€Q@N@U@P@S@€O@€P@N@N@@V@€O@@P@H@€E@T@€T@€W@€T@H@€Q@@P@Q@T@€P@€N@€U@T@J@€Q@€S@O@@S@@S@€K@C@U@€S@ÀP@Q@<@€T@€R@ÀW@T@€M@€R@G@€Q@U@T@€J@@P@@U@N@€R@@S@@P@J@€W@€T@€Q@9@O@6@€Q@F@K@L@@P@€Q@€R@N@ÀP@N@€J@@S@H@€P@@P@E@€V@Q@€L@ÀW@5@@W@€J@@P@€O@8@=@L@E@N@@P@O@€L@€O@€T@€P@R@I@I@ÀR@€O@ÀR@€L@€Q@@U@Q@€K@€Q@ÀV@@T@@a@Y@V@Y@@S@€S@U@@V@€Y@@S@€S@ÀV@@S@ÀR@€T@€Q@ÀV@€T@ÀT@€V@ÀQ@ÀR@€T@@[@€W@ÀW@€V@X@@U@ÀQ@ÀR@€S@€T@@Q@ÀY@@U@T@€W@T@ÀS@W@U@€U@@R@ÀR@@R@€S@T@@T@ÀT@ÀY@W@V@@S@ÀS@€V@ÀV@ÀT@T@€S@S@€T@@T@T@€T@ÀQ@@R@ÀU@S@@Y@@W@€V@ÀU@V@€W@€W@€V@€S@ÀT@W@@W@Y@ÀT@`d@X@€\@@e@ a@U@€T@ÀS@R@@X@ÀU@€Y@@U@U@€S@ÀS@ÀV@€X@€U@R@@X@€T@€S@@X@€W@€T@€S@ÀS@ÀU@@W@ÀR@€Z@X@V@€V@€R@@U@€V@ÀQ@ÀR@@S@ÀU@ÀW@€R@ÀX@@V@ÀT@€S@Y@T@ÀU@ÀS@€Y@T@@U@@T@@U@ÀW@€T@@X@W@€Y@€U@T@@U@@U@À[@@V@€O@€Q@W@ÀR@@W@ÀT@U@ÀV@@T@@\@W@@T@€R@€S@T@€T@ÀW@€Z@ÀW@Y@€V@V@ÀQ@€S@@S@ÀU@ÀS@@U@ÀV@W@€X@Q@U@W@€[@[@ c@@R@@T@ÀU@ÀU@ÀW@@R@ÀW@Y@X@@X@S@O@€U@ÀQ@ÀX@Q@€V@@b@€U@T@€V@@W@ÀV@À[@@V@ÀS@@S@@R@W@€X@€S@ÀU@€X@U@€T@€V@@U@ÀQ@U@@U@@S@@W@€R@@V@@V@ÀY@@U@V@@T@U@X@€V@€X@€S@@W@T@@U@ÀP@€R@@Q@@Z@ÀW@ÀU@[@ÀX@ÀS@€U@€T@ÀV@@W@T@U@€V@@T@€V@€S@€X@@T@€V@@U@ÀS@€N@€V@ÀS@ÀT@U@€S@€U@R@ÀU@ÀV@€Y@T@€T@Z@@U@ÀT@@T@€W@U@ÀV@ÀX@@Y@€`@ÀS@ÀY@@X@``@ÀV@€V@@^@€S@U@@X@€W@X@ÀV@T@@X@W@€V@€V@À^@@Z@@U@@S@ÀT@W@@U@X@@Q@V@U@ÀZ@ÀV@€R@@V@@[@T@ÀT@W@Y@@\@@Z@ÀX@U@€R@S@ÀU@ÀU@X@V@T@@U@€V@€R@ÀW@€U@€R@ÀW@€R@@R@Z@W@@Z@@X@@Y@ÀT@U@€X@@T@@W@U@€Y@€W@ÀV@Y@W@€W@€X@@b@U@@S@€T@U@S@€Z@€Q@ÀU@€]@€U@€T@X@@V@@W@€T@@X@€U@€g@@W@R@ÀZ@@T@S@ÀT@`b@€T@€[@[@€T@@W@ÀW@T@ g@@R@€S@ÀQ@€U@@U@S@@W@ÀU@X@€U@€S@ÀU@T@€X@ÀR@€S@€T@€W@ÀT@€W@€a@ÀU@€K@ `@ÀT@@S@À^@ÀS@V@T@V@ÀS@@S@ÀU@V@€W@V@€R@@U@V@@T@ÀV@@T@T@Y@[@@W@ÀS@ h@Àe@`d@@i@ f@àd@ `@€]@@`@à`@àa@`c@€f@Àc@€`@ b@^@@c@ f@`o@€^@€`@ c@€a@`@@g@ d@€d@`b@ c@a@@_@ a@ b@@f@Àa@i@ g@`d@@d@@c@@c@c@ f@àc@€c@ a@€l@à`@€f@à`@`b@ c@@i@Àa@ b@f@€^@@^@`b@a@e@@]@Àc@Àb@@c@`e@Àd@`r@ e@`c@@a@àc@€d@€b@`b@`h@`b@àc@@f@ b@€f@`b@àb@àg@h@€d@ k@@h@d@ e@ `@@r@àa@ b@€g@€]@ b@`e@f@`h@Àa@`b@€`@d@`r@`i@@e@àc@ `@`d@ d@ c@Àb@ c@àc@Àa@€f@Àc@@d@ a@Àd@ h@ d@`d@@`@@b@`c@0p@À`@@b@c@€_@€c@àd@à`@ d@ c@`h@àd@@h@à`@À_@À^@ a@f@`e@€d@€d@Àa@_@€^@@^@@h@ c@`g@`b@k@ c@d@À\@àc@ a@a@`d@àf@àc@ b@ c@€b@@d@ i@ c@€e@ d@`a@n@ c@i@@f@`g@ c@g@€c@€`@àg@ a@ b@`a@€c@àg@ g@f@`@`g@d@e@_@a@àe@f@@a@@[@e@ d@d@àb@Àd@e@``@€Z@@f@@\@a@ e@@g@ c@g@ h@Àf@`a@`@€e@àf@ `@`c@€f@`c@Àd@@e@@d@a@ f@€e@ f@ f@b@ f@ c@ `@ c@ b@ b@Àb@€f@@c@@e@ c@`d@l@`d@ b@`c@€c@ e@à`@ c@Àc@@Z@ e@àb@c@t@Àe@ a@Àc@`d@Àe@`b@€i@€h@Àh@€b@`d@ `@ d@Àe@g@€b@ b@ a@€d@ d@g@@e@ b@ b@ d@`c@€a@àe@b@_@ a@ g@ e@k@ i@ h@@f@@c@d@€_@c@Àh@@f@`c@ h@c@Àf@€d@d@Àc@àe@ c@ j@Àc@`a@ j@c@€`@Àb@àh@@`@d@``@@c@@b@@c@e@€c@g@À^@àg@@f@ f@d@ÀY@Àh@€h@àe@àf@d@ e@Àe@àb@€_@ f@ d@€c@€g@`@@d@€\@i@ e@€h@€a@à`@ d@ e@Àf@Àc@€_@€d@ i@àc@Àa@g@f@@^@ a@@_@€]@€d@€b@Àc@`€@€d@`b@€^@@c@ e@Àf@`c@€]@€g@€i@ f@àb@@g@@d@€a@d@ c@``@àg@ `@€f@€d@c@àh@àb@ c@àe@€d@`i@ c@Àb@`d@€b@@_@ b@ b@À^@ b@`c@`c@b@Àa@@b@@e@ c@àa@àa@ c@àa@À`@àb@àh@ a@f@€Z@À_@0w@y@ x@Àw@€v@t@w@€w@ v@ðw@y@Pw@àw@0v@ðx@àu@°u@v@€x@ðx@ðw@u@Àv@`y@@x@àw@Pw@py@@z@y@€y@0u@v@w@Pv@Pv@àv@x@ v@y@ðw@àu@ u@ðw@ðv@àu@ t@w@Ðz@°w@ x@pv@@v@ w@w@€x@€w@Àv@@w@ x@ðq@@t@Pw@`w@àw@°v@ y@àu@€y@Àw@x@@v@v@ z@ðw@0w@ðz@w@@x@°t@ t@Ðu@v@pv@Ðw@ðz@Pw@ðw@0z@x@@z@`u@0w@€t@ u@`y@Àv@Ðx@ |@ n@w@0x@pu@ày@0w@ w@ðy@w@v@€v@àv@w@Àv@ðt@`x@ z@°y@@u@v@àw@Àw@0u@ x@Pt@0u@ w@@w@@u@ x@x@°u@ z@àu@Àw@ v@v@àx@Pv@Àv@0v@u@ m@ðw@ v@0u@0x@°w@w@pw@0t@x@°u@Pv@Àx@Ðy@ w@Pv@ðw@x@ðw@ w@u@ày@u@°u@w@@v@`u@°u@°t@Àw@`v@w@ w@Àw@y@ðu@u@ x@ p@ðy@Àv@p{@s@°u@@u@ðv@ z@`y@Pz@Ðu@ v@€x@°u@ x@Àt@w@Ðy@@v@`x@Àx@€w@`s@ðv@Ðu@px@0v@`{@`w@w@Ðu@t@ y@0u@t@€v@}@Àv@t@pv@`u@@v@@w@`v@€w@ðu@°w@@v@°v@w@ t@`v@ w@y@Ày@àu@ðt@Px@v@@v@v@r@py@ t@v@w@w@s@ t@`t@ v@€w@ u@@x@t@Àv@@s@Pw@°t@Àu@Àu@@t@Px@u@ v@àt@`v@ v@Pv@ðw@w@Ðx@Àw@pz@v@pw@0w@ v@€v@ w@ y@àu@Àv@ðx@@w@Pw@Ðw@ x@àw@ w@0x@Ðw@0v@`x@àu@0w@°v@w@pv@àw@t@x@À{@Pv@ {@@u@ t@°v@`r@ v@Pu@@y@€{@t@v@pv@m@0v@ u@àu@n@@y@Àx@pv@Pv@Ðw@0v@`w@pw@€u@@x@@x@`v@ v@ðw@°w@x@Ðt@Pv@Ðu@€t@Àw@ v@ x@Àv@0x@ w@pv@àr@w@`w@pv@pv@°v@pt@Ðt@u@Ðt@ z@ r@@|@àw@ v@Ðz@y@0w@u@pv@Py@Pw@|@w@Pt@Ðw@@u@Ðx@Ðv@0u@ x@ðr@ x@Pr@ t@w@ðv@Pv@àw@Ðr@ |@ p@°w@py@Pv@t@w@Ð@u@ z@Pu@w@0x@°u@ z@Àq@Ðw@°v@@x@€w@ðz@w@€w@Àx@w@ u@Àu@ v@pu@0u@ v@`w@Àq@p{@pv@ðv@`{@ v@@u@z@Àv@`v@v@Ðw@ v@ r@€v@ðv@@u@px@Pv@ðw@Px@`w@°v@x@Pt@€w@°v@àw@v@Ðv@ x@Àv@Àe@ b@ g@`f@ f@Àe@ `@ c@d@€c@€c@Àb@`d@ c@`d@ e@ e@€^@àe@Àc@ g@ f@ e@àa@ b@`h@l@e@Ðw@Ðr@€e@c@àf@€b@ `@ a@ d@Àe@Àd@àc@ a@d@Àc@b@`b@@a@€e@àa@@`@ b@ a@ e@@d@Àa@@\@d@ d@àe@ d@Àe@€c@`c@c@àd@ j@Àa@ a@ d@àb@c@€e@€d@€d@@d@@f@àd@Àc@Àa@@f@€b@ `@f@€`@ b@ b@@n@`b@Àk@€p@€n@ k@@a@`e@à`@`m@h@e@f@ h@c@€e@@]@€`@Àf@ a@@e@[@h@ a@`i@€b@àd@àd@àd@ a@`c@ d@c@€c@e@@c@ c@Àe@à`@ a@Ài@Àb@`a@Àb@Àb@àe@À`@`b@`f@€c@d@€e@à`@À`@àb@c@Àb@`d@€c@`d@àe@àd@ c@ c@àa@€e@`m@€e@`a@Àc@€f@àa@€a@ e@ f@ e@``@ h@ d@@c@]@€d@`n@€d@ a@`b@Àd@c@b@@a@`a@c@b@Àd@Àh@€e@àg@àd@@`@€d@àl@€^@Àh@@k@ o@@c@À_@ a@ài@ b@Àg@`e@`b@`e@ e@`c@a@€k@e@ n@`b@Àb@°q@àd@g@ j@€e@Àh@i@`d@€a@`b@b@€h@ b@@_@@i@ d@Àd@Àb@`p@Àf@``@g@àb@€a@ b@`d@@^@Àh@ o@€c@b@@c@àc@@e@ j@ f@€^@`a@l@@o@ a@ d@ d@àa@@i@`o@Àh@€e@€e@Àj@ p@àc@ l@a@@f@Àd@@c@ c@@`@@c@ h@àb@`d@€c@àe@Àc@@a@Àa@@b@c@@]@`c@ d@Ào@e@ e@@d@àc@`b@@_@ c@`b@àa@€g@Àg@`i@àc@ p@€`@`o@k@c@k@d@àa@Àf@@e@À_@ l@`g@à`@g@ d@ i@ c@àf@ b@c@ a@`@€d@@a@ e@ `@€g@a@ n@€e@Àa@b@@k@€f@r@`d@Àf@€k@ f@g@@r@Àa@d@@f@Àb@@e@Àc@`e@€b@@e@ e@ d@@b@À_@€c@@a@o@ c@àb@@k@`e@ l@ l@ d@`d@d@ p@ a@`d@ b@@d@c@ n@ m@€c@q@@b@`m@`e@àn@`d@€h@àa@c@€d@`n@`g@àc@`c@ e@Àa@l@ c@Pp@`c@d@Àf@€b@ a@À[@pt@Àe@@c@e@c@àe@€c@@f@À^@@e@@c@@e@g@`f@ c@ào@ e@@i@àc@€_@e@€d@ b@ a@ d@€h@`a@@c@`e@@l@ f@@d@@p@^@àc@ k@ e@€l@`@`d@ c@€b@ a@`f@c@€e@@b@@b@€`@@k@d@Àd@@c@ i@@h@@k@@a@@^@€C@€Y@àa@ÀY@ÀV@@S@€Q@€O@@R@ÀT@@P@@T@Z@€W@@P@€N@J@ÀT@ÀS@àf@€S@€T@W@€T@N@@_@ÀT@ÀX@€T@ÀV@S@€O@ÀT@@Y@T@€W@€R@ÀZ@€Z@U@€W@X@ÀQ@@\@ÀX@@Z@@S@€W@@X@€Z@T@T@Q@@X@V@ÀS@ÀS@K@ÀP@ÀR@T@€S@€O@R@ÀU@@T@@T@€Y@Y@àa@[@€]@Q@W@@T@@T@W@€Y@X@@]@T@ÀW@Q@@X@€Y@€X@]@@Z@@]@€_@Y@@U@X@àe@€T@V@@V@Q@ÀT@@W@b@T@ÀV@@W@@U@@V@@f@€Y@@X@@P@N@€V@X@€V@X@€\@X@@V@Y@ÀY@X@U@X@€`@@X@@\@€Q@ÀT@Z@ÀW@M@@V@ÀV@ÀT@]@À_@ÀS@€Z@W@€Y@€S@ÀT@@R@€S@ÀP@ÀU@Z@€W@ÀP@U@€I@O@R@€U@@X@€U@€R@X@@e@S@@T@ÀP@ÀT@W@€T@@W@€W@X@@Y@V@@W@@X@ b@ÀV@@W@€T@ÀU@À^@€S@À[@€X@€Y@€W@€\@Q@P@ÀX@ÀV@ÀS@@W@@X@@V@@W@ a@ÀP@@^@Z@U@Q@@T@€\@[@ÀU@€N@@W@€X@@Y@€U@Y@@Y@@S@€L@ÀX@M@ÀU@€U@@\@€T@X@@]@@Z@€T@S@Z@€R@ÀQ@Y@[@ÀR@@[@€Z@Y@@U@U@ÀY@^@€X@T@Y@K@ÀS@€V@€T@W@X@@U@ÀW@€Z@ÀU@ÀU@€^@Z@ÀX@Y@@Z@Y@€Q@[@€\@€Q@€Y@P@@X@`c@R@ÀS@X@Z@ÀT@ÀS@T@R@àa@À[@€]@@Y@@]@àa@ÀT@@V@Y@@R@@X@W@À\@ÀY@ÀV@€Y@€T@Y@V@€S@E@Y@€P@ÀU@ÀZ@ÀT@€U@À[@À[@R@@Q@Y@N@@T@@W@€\@P@ÀX@Y@À_@€S@ÀU@€Z@@Z@€Z@@Z@Z@ÀV@ÀZ@€]@T@@U@Y@ÀQ@@V@@S@€\@€S@@U@€U@€U@@Z@@T@@Z@@]@Z@À\@€B@@P@€i@À\@`a@€]@U@P@€U@]@Y@R@€U@@X@Q@€X@@Q@ÀW@@P@]@€S@Z@R@@T@[@€T@€[@À\@T@À[@À\@€Y@ÀV@€V@À[@€K@ÀS@ÀR@€T@ÀU@€U@@[@ i@@U@Q@€O@€W@ÀX@ÀZ@ÀY@€P@Z@R@@W@ÀU@Z@€Z@€W@ÀR@Y@S@[@€T@\@@V@X@ f@@W@@U@Y@@Q@€Z@@T@X@€Y@W@€V@€Q@T@@Y@@T@@W@ÀQ@ÀW@€T@V@R@À\@@V@ÀR@U@\@€R@ÀT@T@@]@@R@]@€O@€S@0À9@X@<@0ÀÀZ@@S@ÀP@€N@@U@R@(@8ÀQ@G@B@@S@€L@@R@(ÀI@@T@O@@J@AÀ€V@$@ À€BÀJÀ*@C@"@€H@O@K@H@À€LÀ€J@ÀP@K@€M@2À2À2@>@€O@,Àð?A@&@P@M@>@A@&ÀE@@T@€_@D@€R@€P@ÀR@L@9@€P@P@BÀA@€@@€O@8À€N@V@]@>@V@O@N@Q@F@€KÀ€T@L@Q@P@O@,@;@À"@*À aÀ3@€@@8@6@:@$À@R@ e@€E@L@Àð¿€B@€V@:@(À€D@M@€S@O@(@€K@0À5ÀJ@.@2@GÀ:@N@L@ð¿€C@=@ÀU@H@€P@€T@€M@@T@M@€K@N@B@ÀQ@V@2À€N@@ÀÀV@7@N@R@€S@8@IÀ€H@€Q@9@I@€W@D@,À@P@ð¿I@&@ÀP@L@€IÀÀ€I@&@ÀIÀ<@=@ÀS@ÀR@€P@&ÀÀT@4@4@€E@@ÀDÀS@O@8@4ÀMÀS@6À€G@A@@R@ÀV@(À€Y@€@@ð¿@ÀF@€W@@T@.À0@€P@@R@T@À[@0@€U@€A@@€J@;@K@ÀS@9@9ÀR@€A@S@O@N@,@@@P@€@@1@€P@O@€S@ð?S@€Q@7@D@€R@@4@€Q@€K@$À€M@;@3À5ÀCÀÀ€OÀBÀHÀ€G@ @ÀU@N@B@À$ÀA@ÀQ@€K@€G@@U@?À=À6@@S@@Q@L@€L@3@&@"@@P@D@€P@À€CÀ @3À:À€HÀ@Q@@R@G@0ÀI@F@FÀM@ÀP@ð¿`c@ @"@€P@ð¿;ÀHÀ€N@T@€G@ð?€C@ÀT@@@5@<@ÀdÀ*@Q@@_@E@R@.@@W@À]@@NÀD@@LÀ8@@Q@ð¿€N@ÀV@J@,@4@€[@P@€P@<@@U@I@€C@?@@Q@€R@M@&À\@@"@€@@H@@ÀOÀÀU@JÀ€J@ @€H@€J@H@"@O@$À*@AÀ€R@S@€K@ @€C@G@ð?@@$À@RÀ&@M@^@ `@W@J@@€T@F@ÀU@J@(À?ÀF@ÀS@P@@T@ÀU@?@C@€Y@€HÀ€@@€F@U@3@€eÀ€A@U@€J@9@ÀQ@€@@ÀO@€R@€R@ÀQ@@S@À€E@7@L@9@P@ÀDÀF@D@ÀcÀ=@ÀU@\@J@2ÀÀPÀ6ÀI@;@€P@ð?W@ÀP@Q@DÀ@P@8@ÀV@€E@€BÀ€U@@UÀK@<@*@€B@A@&@*@€P@€H@@@Q@A@ÀQ@€B@E@€I@4@€F@ÀR@€H@8À<@€C@€S@L@$@1@\@J@H@ c@€e@0@4@@@L@G@7@*@€A@@ À$@ÀH@J@@N@ @2ÀS@G@P@A@"@€F@ÀS@€@@,@>@8À€J@9@7@P@O@G@$@øÿ€F@€E@J@?@3@€F@3@I@Q@€dÀLÀQ@.@ÀJ@G@ÀU@ÀS@aÀàe@.@_ÀÀa@*Àøÿ$@"ÀJ@M@1@€G@À dÀ ÀN@@R@J@5@C@€Q@5@WÀàa@*@E@O@H@1@6@7@3@*@R@€L@€D@€L@@P@M@B@€O@N@€K@F@@S@F@8@@@G@€K@€J@@T@€@@B@€D@>@€I@4@J@(@C@$@E@=@@Q@2@6@$@D@9@ @$@€@@K@€E@ð¿P@€G@€L@€L@J@H@D@Z@@S@I@Q@€P@@R@,@3@3@9@àdÀ.ÀÀP@[@ÀRÀd@€\@6À^@À\À`b@C@7@L@€TÀ0@€H@L@,@€OÀ€A@øÿJ@€eÀA@€Y@€L@€B@`f@1@€U@ÀU@R@øÿJÀ€HÀB@Àe@;ÀÀW@À^ÀP@&@C@€N@@P@H@*@,@ @N@E@ a@N@€\@€O@€d@€H@Àa@øÿ@J@ fÀ@ÀYÀF@€ZÀ€G@ÀP@€P@<@€H@EÀ@]@À_@ÀR@B@dÀ€^@€M@€S@ @E@1@K@€@@>@€@@À(@9@€H@M@ÀQ@€J@0À,@2@€@@E@<@€U@5@(@:@€D@Y@8@I@ ÀÀR@ÀQ@H@5À@^À(@ÀeÀB@,@D@€IÀ"@4@@Z@]ÀB@À&@À]À€O@€@@*À@S@@9@€O@5@€F@ÀV@U@ÀW@0@ `@`dÀ€P@3@@R@ÀZ@€HÀ c@@E@€C@@ÀS@A@&@L@L@€P@@S@ÀC@€F@€D@B@2@J@.@O@P@€`À@\@€C@ b@ÀU@ÀX@@_@@P@bÀøÿX@E@@R@?@&@@d@€J@@T@1À`f@€C@øÿ@S@K@V@[@€D@€I@1@€_@SÀ7À.ÀJ@ÀÀU@"@ f@Àb@R@M@$@€D@ÀY@À\À€G@€J@ÀU@.@B@€D@€\@8@€D@ÀQ@ÀT@6@ `@À€I@ÀøÿB@ÀP@:ÀQÀa@€@@€N@€`ÀK@€L@€U@8@€A@*@Y@€K@1À€G@J@À[@2@B@€K@H@ b@ÀH@,@$ÀF@;@Q@E@@€P@3À=@ÀS@P@1À€Q@À€N@J@ÀR@ @€S@€Q@Q@€H@€D@N@€F@Q@@S@À€N@I@G@ÀP@€F@M@@Z@@c@N@€C@€D@ÀK@€N@@R@€J@ÀQ@€K@B@.@D@€Q@€C@@P@P@€N@N@€L@€Q@B@@S@N@€O@€K@€K@O@øÿB@€K@€N@€P@øÿN@@P@€Q@Q@,ÀO@O@.@€D@N@G@A@ÀP@?@€I@N@€B@H@ÀR@€R@€O@N@€P@L@€L@ÀR@R@N@^@2@€D@P@L@>@€S@2@G@€L@@€E@@Q@€J@M@P@@P@@@øÿI@øÿB@€F@€G@€K@À0@J@øÿ1@Q@O@N@€P@€J@ÀZ@@R@=@€C@P@J@J@N@D@@R@øÿÀQ@€R@6@€G@ÀT@€O@I@>@C@L@N@"À€N@L@@@€S@O@M@B@P@€L@ @L@€O@E@€E@M@G@€M@H@€N@N@À@Q@€G@I@€D@ÀS@@Q@;@øÿ&ÀÀP@øÿF@I@N@€O@"@€K@€E@I@@R@E@K@0@ÀS@E@øÿL@€N@@Q@€L@Q@@PÀ>@@1@€D@øÿ@P@N@€KÀB@€N@L@ÀR@@@S@O@*@ÀP@øÿ€E@øÿN@N@I@€O@M@ÀQ@€K@F@ÀN@€I@€M@€N@@R@€L@€G@@S@ÀQ@L@M@€Q@:@€I@øÿÀV@€R@I@R@H@ÀR@€Q@ÀR@Q@øÿ€P@€N@M@B@K@O@€C@ÀQ@€G@€P@@P@Q@;@€M@€A@A@@eÀ€F@ÀR@@@€I@€J@$À@P@øÿ:@O@€H@A@øÿI@€G@€L@S@€D@€H@Q@M@€E@€N@M@5@€K@øÿ€A@øÿ€F@N@€M@N@L@Q@€D@øÿ@S@øÿ€C@ ÀM@L@@P@IÀL@M@€C@K@€H@L@F@€R@€R@J@Q@J@€P@G@,ÀU@€R@Q@€Q@K@ÀP@€O@K@€Y@€G@€@@ÀQ@P@R@ÀP@€I@I@@øÿNÀ€R@>@€N@€P@€C@@Q@<@E@,@€A@ÀQ@H@>@&@€S@@S@<@Q@ ÀL@€K@J@€E@?@€M@€N@€B@L@R@M@F@€J@F@@WÀ?@9ÀÀP@H@@P@@R@€O@ÀN@>@J@K@E@=@f@€F@G@ÀS@C@€N@;ÀÀP@2@JÀ€J@€R@G@€R@A@€H@€M@øÿ€O@€R@€N@=@H@@Q@€O@N@0@,@N@ÀP@:@€U@R@S@€L@€I@@P@M@€H@K@€F@@@?@R@D@J@€NÀ6ÀJ@À?@€P@4@@V@@P@€I@€P@ÀQ@R@:@*À€O@D@D@ÀR@K@E@ð?€E@T@N@@E@@@S@>@ÀPÀ@À,@€A@5@€G@€J@>@€C@ð¿HÀB@€E@J@€L@(Àð¿,@4@Q@$À;@€@@"@€M@O@?@:@@€@@S@Z@>@Q@O@ÀP@?@€C@€L@M@@@@:@L@ ÀL@€R@`a@(@€T@K@€H@€M@F@:@€T@€O@€T@I@à`À€H@@ð¿"@*ÀI@€I@9@B@@>@ÀÀQ@_@G@€G@@(@<@W@€K@ÀD@O@O@€G@3@E@ð?À€L@;@;@$À€J@L@G@1@H@€C@@T@€G@K@€J@I@@R@L@S@€K@B@€O@J@0@H@F@ @@Q@.@I@I@€R@5@À?@M@9@C@€T@C@0@€K@ð¿M@€A@€O@L@;@F@€B@,@=ÀJ@€C@S@@O@€H@ÀÀN@€H@D@1@EÀ€R@€M@ÀR@&@@ÀÀP@$À3@;@€M@@T@$À€@@@.@5ÀC@X@€R@&@=@K@@T@ÀU@T@0@@Q@ À5@U@ð?ÀT@@U@€H@À€M@€H@€Q@€L@@@*@@@P@B@C@€N@@P@@R@€H@€P@@Y@4@:@@P@øÿ(@€P@€J@€EÀ€K@D@ð?*@<ÀPÀ4À @€C@CÀ€Y@€O@€H@ÀA@:@ÀP@G@F@€P@ ÀÀ9@Q@€O@€P@€K@@(@(@N@€D@€I@,@3À&@ÀÀ&À€M@€O@8@@P@€F@=ÀÀP@€M@2ÀN@$@4@À@À&ÀK@O@<@@6@R@@@(@Q@*À3@ÀP@€Y@E@ÀS@€Q@W@V@2@0À"@€@@€@@EÀ,À@]@€L@ÀS@€@@@P@€@@W@N@€M@?@€T@5@C@A@€J@€L@€E@@Z@4@:@8@@P@(À b@V@=À@P@C@€N@€J@€Q@1@ÀP@ @(@€PÀ@Q@€S@:@7@€C@F@E@G@ÀTÀ7@€K@Q@@_@€T@D@À@Q@@@ÀU@€D@€CÀ@ÀO@Q@€I@@R@ÀV@1À€E@S@3À7@D@P@?@Àd@C@€S@O@8@W@ð¿A@ÀO@N@@R@€K@€G@*@C@H@@T@4@M@@@@Q@B@€B@=ÀD@S@€I@€H@€@@@PÀÀG@€D@€M@@@S@M@€K@<À€K@9@T@E@;ÀÀS@€QÀ€E@€G@øÿøÿ7@øÿøÿøÿøÿøÿU@øÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿd@øÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ@WÀøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ@c@øÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿbÀ\Àøÿ@WÀ f@øÿøÿøÿ@VÀøÿøÿøÿøÿøÿàe@øÿøÿøÿøÿøÿøÿøÿøÿÀWÀøÿøÿøÿU@øÿøÿøÿøÿøÿøÿøÿ`dÀøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€@ÀøÿøÿøÿøÿB@øÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ2Àøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€dÀøÿøÿøÿ[À@^Àøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ@f@àdÀ d@øÿøÿøÿøÿøÿøÿøÿøÿ€CÀøÿøÿøÿ e@øÿøÿøÿøÿ cÀ€]À@]Àøÿøÿøÿ€LÀøÿøÿøÿ cÀøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€d@øÿ€`Àøÿ aÀøÿÀcÀøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€`@`a@ e@øÿ`fÀøÿøÿøÿÀbÀøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€U@øÿøÿøÿøÿøÿ€_Àøÿøÿøÿ@[Ààb@€@@øÿøÿÀUÀøÿøÿøÿøÿøÿøÿøÿ€^Àøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€\@øÿøÿ cÀeÀ€R@øÿøÿøÿÀ]ÀøÿøÿøÿøÿøÿÀV@øÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿ€dÀøÿÀ^@øÿb@@\Àøÿ@dÀøÿøÿøÿøÿøÿøÿ a@@ZÀÀdÀøÿ eÀøÿøÿøÿøÿøÿ]@øÿøÿøÿ c@øÿøÿøÿøÿøÿøÿøÿf@`@øÿøÿøÿøÿøÿ€bÀøÿøÿ@^@øÿøÿøÿøÿ*@O@øÿøÿøÿøÿøÿ_ÀøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿøÿÀY@øÿUÀøÿøÿøÿFÀøÿøÿøÿøÿøÿøÿøÿøÿøÿ€VÀøÿøÿøÿøÿøÿøÿøÿøÿU@ÀY@øÿ€O@€J@ÀR@ÀQ@øÿU@€Q@ÀP@P@€O@€Q@R@@R@L@R@S@ÀP@€Q@€P@€P@S@€P@@S@@Q@Q@€O@ÀT@ÀP@ÀS@ÀQ@€I@@T@@W@@Q@T@€O@€Q@O@ÀR@€J@€Q@€T@ÀV@P@ÀS@@V@€X@ÀP@€J@€S@@P@@R@@R@P@€M@€L@€Q@ÀQ@L@€P@Z@W@@P@€Q@ÀP@@R@K@€Q@L@@P@€J@@Q@T@N@€P@N@€O@€Q@@P@€X@€S@€U@€R@@T@I@R@ÀP@S@€O@T@Q@@U@ÀR@ÀY@@W@P@ÀS@@R@€P@R@@R@€O@@Y@@P@€L@@V@€O@R@€U@Q@Q@@U@@S@€S@€L@@P@€N@€U@@W@€Q@@T@T@ÀQ@W@@T@@R@€Q@T@R@S@€O@€N@@T@€O@€Q@M@O@Q@T@@U@ÀR@@a@N@S@@Q@N@O@O@@P@@W@€R@€R@€N@K@M@@P@@S@T@@Q@@R@P@@U@€N@Q@R@N@€U@W@ÀY@@V@€Q@@R@R@ÀR@ÀQ@L@€S@ÀP@€L@ÀR@P@R@ÀS@W@€T@U@@S@€P@@W@P@ÀQ@U@€O@U@@S@€W@€T@€J@X@€R@@V@Q@@T@@R@€Q@N@@Q@P@P@M@ÀV@W@€M@€R@€X@ÀS@@V@@T@€Q@€M@@T@ÀR@€I@U@€P@@T@L@@T@O@R@U@T@N@ÀP@€R@€U@€P@@S@ÀS@V@€S@@V@€O@À\@€N@€X@R@€X@€S@€U@T@S@@X@N@€W@@Q@€W@P@€Y@€U@ÀU@X@R@@W@@R@ÀU@R@ÀU@Q@@R@Q@O@€J@€O@@R@€S@Q@ÀQ@ÀQ@@Q@€T@€R@€S@€K@P@€S@@R@Q@R@S@S@€N@U@€J@€S@@R@R@ÀU@Q@@R@ÀR@@P@F@@W@€P@ÀT@ÀY@I@Y@€U@Q@ÀP@I@@W@€Q@@T@`d@€R@ÀS@@V@_@€M@€P@S@S@ÀP@@R@€O@O@ÀR@@P@€O@€S@U@R@S@ÀR@W@T@@T@@T@ÀQ@€S@M@R@R@€L@S@\@€Q@€O@@P@M@@R@@W@W@@W@R@€J@@Y@ÀU@Q@ÀP@I@L@R@T@ÀR@@T@€P@€N@@Q@@V@€O@X@V@@Q@€S@€N@À^@V@ÀZ@@U@@P@K@@S@ÀR@@]@@S@W@ÀQ@@U@ÀR@@Q@€O@L@ÀU@€P@€U@ÀT@ÀP@€T@€M@€^@@S@@Q@€O@€S@ÀQ@€P@@S@€N@€O@T@@T@Q@@V@€Y@Q@€S@Z@€M@ÀQ@ÀQ@€Q@T@@T@€Q@@P@T@@W@€H@€R@^@ÀQ@@T@@T@O@O@R@€K@@P@Q@O@€S@€Q@ÀR@€O@@R@U@T@ÀR@4@0@4@(@0@4@4@0@4@0@(@0@4@0@4@V@8@8@8@4@8@4@4@R@0@4@0@0@8@0@4@8@8@0@0@0@4@4@4@(@4@8@8@(@<@<@<@4@8@0@8@F@0@4@4@8@4@8@0@8@4@4@4@@@8@4@4@0@4@8@4@<@8@0@4@8@<@0@B@4@4@4@4@4@(@8@0@4@4@F@4@4@4@@@(@4@4@8@B@8@8@0@0@0@0@4@8@0@4@0@0@(@4@0@8@0@4@0@J@H@D@R@H@B@F@F@D@F@B@D@R@W@T@H@H@H@F@L@B@B@F@R@T@T@F@J@R@N@W@T@D@P@F@F@B@J@L@N@J@B@F@Q@R@P@B@R@D@L@R@B@H@N@F@F@S@R@F@B@<@F@D@N@F@N@H@H@N@D@H@H@N@F@Q@B@F@H@H@H@D@N@@@D@€a@L@J@_@^@J@S@F@H@@@B@F@N@L@R@H@F@L@D@J@R@F@F@L@L@B@F@F@D@B@D@B@B@H@J@F@J@S@B@D@F@N@P@Q@B@H@Q@B@D@B@J@F@U@P@H@<@U@H@B@F@D@L@D@D@D@J@D@J@P@J@J@D@a@Q@H@T@Q@H@J@H@B@N@H@N@J@L@L@F@F@N@H@D@H@J@D@V@B@F@J@B@B@B@S@^@P@H@R@H@H@D@F@F@4@Q@F@N@N@J@B@D@F@a@L@D@F@@@H@H@D@F@L@H@<@D@F@F@H@D@B@B@D@N@H@B@J@N@D@J@F@P@B@D@Q@H@F@L@D@F@H@H@Y@H@H@B@F@H@J@P@H@D@J@<@H@N@L@B@D@H@F@D@D@F@D@B@F@D@H@D@F@S@S@J@T@F@N@L@F@R@V@H@L@D@N@P@N@L@H@F@J@H@P@B@U@L@D@L@T@H@H@F@L@R@R@B@P@F@J@@@H@D@4@F@B@B@<@@@Q@L@F@F@J@J@N@B@Q@H@D@H@F@D@F@F@P@D@D@D@J@H@H@D@T@4@J@Q@Q@H@P@B@D@S@R@Q@F@N@Q@P@H@L@S@B@J@<@D@`@D@H@H@L@H@H@F@D@B@D@8@J@D@F@W@B@D@€c@R@F@D@P@H@D@^@F@H@Q@D@R@D@L@0@F@D@P@L@D@Q@L@L@Q@D@L@F@J@H@L@J@H@P@N@T@_@Q@P@B@N@B@B@F@D@J@T@J@F@F@P@B@D@H@B@D@H@@@H@J@F@D@L@F@F@T@4@D@H@B@D@H@<@L@0@<@8@(@B@B@@@0@F@@@0@B@H@B@B@D@D@F@D@L@@@8@@@8@B@H@F@F@B@@@B@0@D@<@N@B@<@0@F@D@H@B@@@@@V@B@H@8@0@@@@@@@(@(@D@H@@@F@@@(@B@4@B@8@H@(@R@4@F@<@0@H@D@H@(@H@H@<@D@8@L@D@D@B@B@H@@@F@B@D@H@L@@@<@F@4@8@B@H@F@D@B@B@8@@@B@D@8@B@0@8@N@B@<@@@F@F@B@B@@@0@D@<@B@D@F@@@<@@@4@@@8@J@D@@@D@B@4@B@@@B@0@B@H@D@D@B@D@4@D@4@D@D@D@0@B@<@B@D@@@P@J@S@4@H@J@@@@@D@N@P@B@8@H@@@H@8@L@4@<@4@<@B@H@F@<@B@B@@@H@F@8@B@<@F@J@F@J@H@F@F@8@4@@@@@(@B@T@P@D@4@B@H@D@@@B@B@F@L@D@<@8@J@4@H@4@D@<@J@@@D@0@Q@H@Q@<@0@0@F@N@@@4@B@D@0@@@8@B@D@<@8@(@(@0@@@8@8@H@<@4@8@8@4@<@B@4@8@@@<@8@4@8@4@<@@@4@8@8@D@F@4@<@@@D@<@<@@@8@8@4@B@8@<@D@8@4@D@<@4@8@0@<@4@@@B@@@4@F@B@@@<@8@<@B@0@8@4@@@<@8@8@@@B@B@B@8@B@8@<@8@H@@@<@8@F@B@B@8@Y@B@@@N@N@<@<@8@<@0@B@D@D@@@@@<@B@<@B@@@<@8@<@@@4@B@8@<@@@@@4@D@B@<@<@4@D@4@4@8@F@B@F@L@4@<@<@@@4@4@<@<@D@8@<@0@B@8@D@8@D@D@4@4@8@<@8@<@D@<@<@8@B@<@8@<@8@4@8@<@4@B@@@8@8@<@<@F@<@<@<@8@H@B@8@8@F@8@<@H@4@F@F@F@4@<@<@<@D@4@8@F@(@D@8@@@J@<@L@4@8@Q@@@@@<@4@8@8@8@8@<@8@@@8@8@4@8@D@4@B@8@@@8@B@<@H@4@<@<@B@0@4@4@B@<@<@F@8@<@<@<@<@<@4@B@<@B@D@D@8@F@F@<@B@<@4@D@<@8@4@4@8@D@B@8@4@8@4@8@@@@@@@<@8@@@D@8@8@B@H@B@8@4@D@D@H@F@B@8@<@@@4@D@F@D@B@<@L@@@<@4@F@H@4@B@8@F@4@<@4@(@8@D@D@0@4@8@F@8@8@<@<@@@4@F@D@8@8@D@D@8@8@8@8@4@8@<@<@8@D@<@ @<@<@<@B@D@H@8@@@8@B@B@@@F@F@<@8@F@J@<@B@4@F@8@<@D@B@<@0@8@B@@@<@<@@@D@8@D@4@F@Q@B@B@4@@@<@4@J@D@@@<@8@@@4@@@8@B@4@8@<@8@8@@@B@8@8@@@4@@@<@@@<@<@D@B@<@U@<@<@0@8@8@4@8@4@8@B@@@F@8@D@4@4@8@B@4@<@4@<@<@8@D@@@8@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?4@4@<@4@0@S@<@4@4@<@4@(@4@8@8@8@8@8@8@B@B@8@4@4@8@8@8@F@B@8@0@4@4@0@4@8@4@4@0@4@B@0@8@<@D@@@S@0@4@4@<@4@4@4@8@@@4@4@D@4@<@8@4@B@4@0@4@8@<@8@4@B@4@8@8@8@8@8@4@8@4@4@4@4@8@8@@@0@8@D@0@4@4@8@4@(@<@(@4@4@(@<@8@4@0@F@P@L@P@D@F@D@J@F@L@J@D@F@H@R@F@J@H@U@P@F@J@R@H@J@J@H@H@N@D@H@P@D@N@R@J@J@H@<@F@P@J@H@H@<@Q@L@P@F@D@H@H@L@T@R@J@B@W@P@<@F@H@J@T@J@F@F@N@4@R@P@F@L@R@@@T@H@H@U@Q@F@Q@<@W@]@L@U@Z@]@H@P@D@B@D@B@D@H@F@L@@@Q@@@R@S@L@H@N@D@S@H@D@H@F@J@B@R@L@@@L@J@D@F@D@D@H@D@H@N@P@J@F@H@U@J@V@R@H@F@D@T@@@J@U@H@F@F@F@N@F@F@H@@@H@H@T@J@P@]@H@N@H@Q@F@L@H@F@F@B@N@H@B@H@H@N@D@U@F@D@H@V@H@D@N@8@H@S@R@L@W@T@H@N@F@D@L@L@N@U@D@Q@J@F@P@R@F@€`@J@N@H@@@H@T@N@D@N@T@H@V@L@H@T@W@H@S@H@J@F@R@Q@L@H@S@L@J@T@B@Y@L@D@H@L@Q@L@D@T@F@B@@@D@J@0@J@F@H@H@F@H@U@L@D@N@J@P@D@L@B@F@H@U@F@Y@F@F@J@L@R@F@S@J@D@N@B@H@F@L@S@D@J@H@H@N@H@Q@Q@V@H@J@N@R@R@H@F@H@F@R@R@T@J@J@R@D@<@H@D@D@Q@Q@D@<@F@H@R@H@H@Q@4@L@F@F@U@Y@Q@H@N@F@H@H@@@H@J@R@J@D@F@J@F@L@F@J@H@L@F@4@P@B@S@H@S@Q@P@R@[@H@J@8@H@B@H@V@H@J@F@H@F@B@B@J@D@F@L@N@J@L@R@D@L@[@0@F@L@L@N@D@Z@H@Q@D@H@Q@D@P@@@P@H@Q@V@R@D@J@H@N@U@S@D@F@F@D@J@J@P@J@D@P@Q@F@D@T@N@@@L@D@<@D@N@D@Q@F@W@L@N@F@H@D@D@L@F@L@D@P@L@4@J@B@8@B@@@@@(@8@4@F@4@<@B@@@<@B@B@<@Q@4@J@B@L@@@D@B@<@@@S@B@H@D@D@D@8@H@<@H@<@B@8@B@D@@@(@F@@@D@<@H@8@D@8@B@N@D@J@@@H@<@J@0@J@D@F@(@0@<@4@F@H@8@8@B@<@D@4@4@D@0@0@0@H@D@D@(@F@D@F@F@D@D@D@F@H@F@@@8@D@4@B@D@@@N@B@8@<@B@B@B@D@<@F@R@@@B@D@L@B@0@0@B@@@<@F@8@B@H@D@@@F@@@D@B@D@(@B@B@H@Q@J@L@(@@@4@D@<@B@F@H@H@0@0@F@@@(@4@L@F@L@B@H@D@U@H@H@8@J@L@F@8@(@(@L@8@4@<@@@8@@@F@D@0@J@@@@@B@B@@@@@(@B@D@D@<@(@B@8@P@@@J@B@P@D@J@<@8@@@0@P@<@B@@@N@4@F@U@J@<@B@B@B@W@(@8@H@B@L@<@L@8@8@L@0@B@D@<@J@S@Q@B@H@@@8@H@@@0@J@D@4@4@8@D@L@B@4@4@<@4@0@(@0@ @4@(@L@<@8@D@B@8@F@8@<@<@@@<@4@<@J@F@<@<@<@<@F@8@<@8@<@@@B@D@<@D@8@<@0@8@D@4@@@<@<@(@<@4@8@F@@@(@@@@@<@<@8@<@<@J@<@@@@@4@B@F@8@8@<@@@<@8@<@B@@@ @D@<@H@D@@@D@D@H@H@@@@@H@8@H@B@N@B@J@P@D@@@B@8@4@<@B@D@<@<@@@B@F@F@F@<@S@@@<@L@@@@@D@<@@@<@4@D@@@B@@@@@8@@@8@8@B@4@F@8@H@8@8@8@H@<@8@<@8@F@8@<@4@B@<@@@@@D@8@4@4@4@@@4@8@8@8@<@D@B@<@@@@@<@8@@@<@@@8@B@L@8@8@@@8@@@8@@@8@@@@@<@@@F@F@0@H@8@F@@@L@@@B@D@D@8@F@D@H@F@4@4@@@8@D@<@<@N@H@B@@@B@@@J@J@8@@@<@@@B@<@@@F@B@<@0@8@8@<@D@B@N@<@@@H@@@8@F@B@F@4@@@<@@@8@8@D@8@4@0@8@H@ @<@<@<@B@8@<@@@<@8@B@<@8@B@@@4@<@8@<@<@<@8@8@@@@@<@8@<@B@<@8@4@8@<@F@8@D@4@@@H@<@B@D@H@8@@@@@B@P@8@@@<@<@D@F@8@H@<@@@<@8@(@8@F@4@<@F@8@0@<@<@4@B@4@D@(@8@F@F@H@F@8@<@D@<@<@<@D@8@@@8@@@4@D@8@<@<@8@@@<@D@<@<@H@8@@@@@<@@@B@8@8@<@@@@@H@H@D@J@<@@@8@<@8@8@4@<@8@<@B@8@@@@@@@8@F@J@F@4@<@H@<@B@L@D@<@<@8@<@D@8@8@D@8@B@D@8@4@F@<@<@@@@@D@F@<@@@@@@@F@@@8@F@<@8@4@<@8@D@<@8@0@8@8@F@D@<@@@B@B@@@<@4@4@B@@@@@@@D@B@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?J@@@<@4@J@8@@@T@B@8@0@0@4@8@U@V@H@B@T@8@<@8@R@F@N@U@8@V@4@8@<@8@<@8@4@(@@@4@4@F@J@Q@@@S@<@4@@@H@8@8@L@B@B@D@8@0@4@@@W@4@4@0@<@@@0@B@<@@@0@<@4@4@4@T@8@H@8@<@H@Q@<@R@F@H@D@8@T@0@4@4@@@D@F@J@0@8@@@H@B@@@<@8@L@S@8@F@H@<@N@B@B@8@B@4@4@F@@@J@R@L@4@J@<@D@(@@@Q@<@4@D@U@<@<@P@N@<@B@J@@@F@4@8@H@<@B@F@@@4@8@8@0@L@0@B@@@8@@@<@@@4@4@N@L@8@B@8@T@<@@@8@S@D@@@4@(@R@4@@@4@@@8@8@H@D@@@Q@B@N@4@<@4@@@H@@@8@0@4@4@0@D@8@]@J@B@P@F@L@B@R@Z@F@D@P@D@J@F@R@4@J@N@L@<@D@<@R@F@0@<@<@J@L@4@@@4@@@H@V@D@B@B@B@P@4@F@J@L@X@<@F@0@R@P@<@H@F@P@H@D@L@Q@4@<@B@D@8@J@F@R@H@J@S@U@H@P@8@W@W@J@Q@U@@@<@8@F@8@@@<@4@8@8@R@B@P@4@W@@@H@D@D@<@@@L@Q@N@@@P@@@D@@@0@8@B@<@F@D@D@@@J@H@D@Q@J@W@H@B@F@8@Q@8@@@N@D@8@U@F@H@F@R@F@F@L@8@L@P@T@8@R@0@H@0@N@D@4@@@H@F@4@F@D@F@S@N@8@@@S@F@4@H@4@B@R@H@D@D@Q@0@P@J@D@Q@D@H@L@(@4@B@N@R@4@@@L@R@D@@@4@S@F@8@J@J@B@X@<@8@R@<@H@S@N@P@D@H@H@R@Q@T@D@8@H@L@(@<@F@L@N@8@<@8@@@4@@@<@H@J@L@@@4@@@<@J@P@F@T@<@8@<@S@F@T@F@B@P@B@S@4@8@@@F@N@R@B@D@U@8@V@N@W@D@<@N@4@@@B@F@Q@J@F@P@B@0@<@S@D@P@B@P@8@4@Q@D@8@0@F@B@4@J@8@R@W@J@H@4@H@H@J@8@L@N@@@@@H@F@S@R@0@B@(@D@<@V@4@S@D@P@T@N@<@P@@@B@L@J@F@Q@F@H@@@F@<@N@D@F@N@H@8@L@D@B@N@8@J@X@J@N@N@T@Q@Q@8@F@J@D@4@<@F@P@H@D@L@H@D@S@S@L@H@B@H@0@T@H@J@L@<@@@B@N@J@R@@@4@<@J@4@P@D@T@N@R@@@H@0@R@L@8@S@<@N@4@H@<@<@ @D@B@(@R@<@@@J@@@S@Q@P@0@F@8@<@Q@<@8@N@D@H@8@<@B@@@B@4@D@@@L@B@F@0@@@F@R@4@@@R@F@L@<@J@Q@J@0@H@8@0@J@J@8@F@U@F@4@N@@@B@0@D@<@N@D@Q@<@4@R@J@D@J@L@P@N@8@N@4@<@N@L@Q@B@F@H@L@F@Q@F@Z@P@D@D@R@L@0@8@N@U@F@F@H@J@8@P@<@B@<@B@4@@@4@S@L@L@N@(@8@Q@P@4@P@<@(@J@0@J@S@S@F@F@F@P@L@F@P@J@B@4@B@D@0@B@4@J@H@S@H@F@S@F@D@@@<@B@4@D@8@R@D@4@L@L@R@B@D@J@L@H@Q@H@F@P@D@(@8@R@@@(@B@€`@P@B@Q@D@(@ @(@@@(@4@L@(@D@N@0@@@J@J@Q@8@N@@@H@D@<@B@H@N@B@B@J@<@H@<@J@8@0@0@4@<@B@8@0@(@0@D@0@N@@@P@F@4@H@ @4@J@L@(@H@0@(@(@<@F@8@<@8@8@N@D@J@L@N@H@8@8@4@<@B@B@J@J@8@<@8@F@ @@@D@@@(@F@0@D@@@ @J@0@H@4@J@0@(@B@D@Q@F@4@J@8@@@P@@@0@J@D@N@@@N@F@B@(@<@<@F@<@<@D@8@ @0@D@F@R@@@B@B@D@J@8@D@F@@@J@@@J@@@P@J@4@0@S@<@D@0@0@<@D@L@J@F@F@L@P@4@F@J@J@0@@@<@D@B@0@F@4@@@4@8@0@4@<@8@<@H@4@8@<@0@L@8@@@D@F@D@B@B@J@0@B@ @0@0@@@8@B@4@<@4@D@L@<@J@L@(@B@ @<@D@@@8@(@8@F@L@ @<@4@H@@@0@(@0@@@@@ @L@J@H@8@B@<@0@D@@N@4@8@F@(@H@L@H@(@4@D@<@ @ @F@F@8@D@ @L@N@0@8@H@8@J@D@(@H@P@8@8@@@F@J@L@@@4@J@<@H@(@H@B@D@Q@B@B@H@(@L@(@Q@ @0@0@@@@@F@4@ @ @0@4@B@0@F@F@(@P@@@8@H@<@(@H@D@4@S@(@4@0@J@<@F@H@B@W@J@L@F@<@0@N@ @0@4@F@D@B@B@<@8@N@U@H@J@8@<@F@Q@F@D@4@U@ @F@4@8@H@(@N@J@J@B@ @<@<@@@D@8@H@F@0@B@F@L@<@@8@D@4@(@N@ @@@8@F@B@D@ @D@0@4@H@D@F@8@4@ @4@@@0@<@8@F@B@F@(@<@4@@@8@ @0@B@H@8@F@L@0@R@(@B@D@F@L@N@J@8@8@<@F@F@4@N@<@D@F@4@B@@@B@<@D@0@0@0@(@D@F@<@4@(@(@H@8@D@0@8@D@B@4@<@8@4@D@(@<@(@@@N@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?J@H@J@P@H@F@F@H@D@L@D@D@F@L@T@D@J@H@S@Q@H@J@R@S@P@S@H@R@H@@@S@H@Q@R@F@H@H@L@J@S@H@P@S@H@J@D@F@J@J@P@J@H@J@Q@@@F@H@J@S@H@F@H@L@B@L@J@L@R@J@J@J@N@J@N@`@N@[@]@H@R@D@F@D@F@L@B@P@J@U@N@J@N@H@F@D@H@B@R@F@J@J@D@B@D@D@H@N@W@H@H@H@H@H@U@S@U@H@S@D@H@U@D@D@D@D@F@J@B@H@H@L@L@J@U@H@J@H@Q@F@L@H@B@D@D@L@D@J@L@H@Q@F@L@U@H@L@H@P@]@B@S@J@J@D@J@F@V@F@L@Q@H@P@H@F@`@P@J@F@@@H@F@D@L@Q@<@L@H@F@U@F@V@F@L@H@T@R@F@R@L@N@T@D@R@D@J@Q@L@F@S@H@H@B@D@B@R@D@H@F@N@L@B@F@J@H@H@J@D@F@F@H@F@F@D@F@R@P@J@S@H@H@L@F@D@L@@@H@F@P@H@H@P@R@@@H@L@Q@H@H@F@Q@S@T@F@L@R@J@@@H@@@J@D@@@F@H@T@@@F@L@D@Q@L@J@F@F@D@H@H@H@L@D@J@F@H@H@J@D@J@N@H@F@J@F@S@J@P@T@T@H@H@^@F@J@H@F@<@D@H@B@B@F@N@J@P@F@a@P@D@J@N@J@D@[@L@H@D@R@P@8@Q@H@Q@T@F@Q@H@R@F@P@J@H@H@J@P@L@F@S@P@H@R@F@J@D@<@D@R@V@H@N@H@J@D@H@D@@@J@D@H@B@R@H@P@@@<@B@0@@@(@<@B@<@B@@@@@0@4@8@8@(@H@H@4@0@D@B@4@B@B@8@4@F@4@<@<@8@@@<@8@B@@@4@B@B@8@4@D@0@@@F@<@4@<@8@<@F@8@@@N@4@4@0@F@0@8@J@B@<@8@0@@@8@0@F@0@<@B@<@D@D@0@4@B@4@4@(@@@<@F@F@4@4@F@B@B@F@8@B@D@B@H@4@D@<@4@J@D@0@@@4@@@8@4@8@<@B@<@4@B@B@F@<@<@0@D@H@<@F@4@8@<@F@0@D@D@0@B@@@@@@@0@<@4@B@4@@@4@H@8@B@F@H@8@4@8@B@@@<@D@F@F@H@0@0@0@4@D@0@@@B@0@4@<@B@<@8@F@<@0@P@0@S@<@8@@@D@@@H@8@4@N@P@8@8@D@4@@@8@@@D@0@D@4@<@4@<@4@B@8@<@4@B@@@4@B@8@B@(@B@8@4@D@@@B@L@B@0@F@H@4@<@8@8@0@<@0@@@N@4@D@T@H@B@4@B@B@<@<@B@0@H@J@@@4@J@(@F@0@8@(@4@4@J@4@<@@@8@4@B@0@D@0@<@@@J@4@4@B@4@4@F@<@4@8@8@B@<@H@<@H@L@F@J@J@L@H@B@F@D@<@F@B@F@S@@@R@H@B@B@F@Q@H@D@D@Q@F@H@D@P@<@@@H@S@Q@H@N@N@F@P@J@B@D@H@F@Q@Q@D@D@B@B@L@T@P@J@<@Q@H@<@H@B@F@J@F@F@H@B@L@@@D@D@@@B@F@@@@@@@B@H@D@B@H@@@0@F@4@D@H@D@B@@@4@<@<@4@B@0@P@B@@@4@(@H@8@B@@@4@<@4@U@N@@@V@R@L@J@H@L@F@N@H@H@H@F@L@J@R@J@L@Q@J@L@B@ @J@J@J@Q@N@R@ @L@(@L@F@H@P@0@L@D@H@H@L@J@D@ @P@N@0@P@L@Q@P@P@U@N@F@J@J@P@R@ @N@L@0@F@Q@0@(@H@H@L@D@J@N@N@J@F@H@H@P@(@L@J@T@W@H@J@H@J@R@Q@H@J@N@F@L@J@N@L@J@J@F@F@ @J@N@P@H@ @L@Q@ @(@R@S@F@R@F@L@J@0@J@H@L@J@D@Q@(@H@D@L@@H@J@N@L@4@H@Q@ @F@R@J@H@J@D@H@(@F@U@H@L@(@(@J@F@N@L@P@H@J@J@H@H@H@J@J@L@P@H@H@P@D@H@(@N@W@R@T@ @U@L@N@0@L@T@N@J@H@R@P@L@Q@F@N@H@D@J@D@N@H@ @0@(@J@H@H@L@L@(@L@P@F@L@J@L@L@H@P@ @H@Q@L@R@N@@Q@Q@ @(@(@Q@P@L@ @J@L@H@J@N@H@J@(@N@H@0@N@H@ @S@R@F@P@D@H@H@ @J@H@P@L@L@ @J@L@N@H@W@R@J@N@L@H@D@F@ @J@J@N@H@L@F@D@L@H@L@J@J@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?B@J@<@H@(@F@4@<@8@4@8@4@(@4@4@8@0@Q@B@B@4@<@4@4@0@F@T@L@V@P@@@4@4@(@4@4@8@8@4@4@<@<@8@J@F@U@F@8@8@8@8@0@4@4@8@8@<@J@4@4@B@4@8@(@8@B@<@4@N@P@4@J@8@@@4@8@J@8@H@4@4@8@4@8@4@D@(@8@4@8@J@4@0@8@4@4@4@8@8@0@B@@@N@0@0@<@<@<@4@F@@@4@N@4@R@0@@@@@4@@@<@B@<@8@<@8@<@Q@4@F@L@P@4@8@N@4@<@Q@4@@@4@0@0@0@4@8@8@J@4@P@8@4@L@F@B@N@H@<@<@8@D@U@D@B@N@@@8@B@L@S@<@L@<@S@8@Y@Q@P@X@R@F@J@D@B@@@@@L@N@@@<@D@B@R@P@F@L@8@P@R@N@P@B@H@D@P@T@B@<@D@8@D@@@F@B@L@F@D@8@F@8@F@B@J@@@@@<@P@4@J@D@^@N@Q@B@H@H@D@X@R@J@P@H@0@4@P@@@J@N@D@B@J@N@@@@@B@<@B@B@B@D@B@D@L@N@B@8@@@L@V@D@P@F@@@<@B@B@B@<@<@4@T@@@D@P@0@P@4@@@B@H@U@F@<@L@D@B@D@N@4@Q@@@W@4@<@N@L@B@F@L@P@J@N@B@P@B@J@N@@@L@B@H@L@F@8@@@P@_@S@0@Q@D@J@B@8@H@@@L@F@J@N@D@8@F@€`@(@4@8@H@(@L@S@R@D@<@@@B@R@@@@@<@8@B@L@L@D@F@J@D@D@Q@0@@@N@F@@@N@D@@@D@H@B@H@H@@@R@H@J@J@N@D@J@8@@@F@H@B@L@4@@@B@(@F@J@@@8@B@@@F@P@R@D@P@D@L@U@D@U@V@S@B@<@B@R@L@@@B@F@@@F@N@B@R@N@B@N@S@J@D@@@F@T@L@4@J@F@J@B@H@8@B@@@B@4@<@N@J@W@D@L@L@V@<@P@B@<@D@N@B@<@<@N@8@@@@@V@<@B@T@J@T@Q@<@P@H@8@Q@@@N@D@D@N@T@D@Q@R@H@<@<@B@]@D@F@H@L@H@Q@Q@8@8@<@F@N@8@F@@@@@€b@R@B@F@@@_@D@@@R@B@F@<@J@(@H@F@H@D@Q@F@J@4@@@@@4@W@8@J@D@L@D@S@P@F@P@@@L@@@@@B@T@J@T@H@L@D@S@8@0@H@J@D@J@J@P@B@B@4@W@(@B@P@L@D@<@B@B@D@@@Q@<@B@B@H@B@F@B@4@4@@@0@F@L@L@F@8@@@Q@D@L@@@D@0@D@N@H@8@D@<@4@B@B@N@D@<@J@B@B@<@4@F@F@<@4@(@ @D@8@<@B@8@<@N@4@8@N@8@D@R@B@B@H@H@N@4@D@4@@@@@H@J@B@J@L@0@@@F@H@L@<@N@H@D@<@D@@@@@B@U@B@F@4@N@8@Q@B@<@F@4@H@B@0@L@H@N@H@B@F@D@J@@@<@8@0@@@F@D@0@F@N@<@H@D@4@J@H@<@B@H@H@4@J@D@4@(@@@P@B@L@V@L@8@L@D@F@<@N@Q@@@@@<@J@F@D@P@H@@@D@H@R@J@D@D@N@B@@@H@<@N@N@B@N@L@F@@@@@0@S@L@D@J@@@D@F@J@L@B@F@0@J@4@J@H@8@J@D@D@D@W@H@P@<@8@F@Q@D@<@<@@@F@8@B@8@@@D@B@@@D@@@8@8@F@<@0@0@D@4@<@<@4@4@N@ @(@@@8@0@B@B@L@(@B@8@D@B@4@8@4@4@0@0@0@<@L@0@0@F@8@D@8@@@4@ @B@J@@@4@<@8@D@@@4@4@0@4@D@D@4@F@4@D@B@8@(@D@(@8@8@@@0@(@L@U@ @@@ @W@4@B@4@<@H@8@0@B@0@8@@@ @Q@D@<@@@4@8@4@<@F@B@0@8@(@B@4@D@4@4@B@0@F@D@(@4@F@H@4@N@4@4@(@4@4@H@0@0@@@L@4@F@4@@F@ @0@8@<@<@(@0@8@4@8@B@D@8@@@0@4@(@B@4@D@4@<@<@8@<@F@8@8@8@H@D@(@J@B@@@@@<@(@D@@@H@<@@D@8@B@D@(@H@8@@@8@<@H@0@P@D@W@<@L@(@4@@B@8@0@8@F@0@4@B@0@D@N@@@0@<@8@8@<@@@8@<@B@ @4@B@8@0@8@D@4@4@<@4@<@<@0@J@<@@@H@F@8@F@J@0@<@B@4@D@ @0@0@8@8@<@D@(@4@0@8@D@@@<@8@8@@@F@8@<@F@@@(@(@4@@@@@J@B@D@B@@@@@8@F@F@4@8@F@L@8@4@F@@@@@ @<@8@<@8@<@(@4@4@B@8@0@B@<@4@8@J@@@F@0@F@0@0@8@L@D@J@0@H@(@B@0@4@ @B@@@8@4@B@<@L@L@(@H@(@@@8@8@N@@@8@8@F@L@0@0@0@S@4@4@D@J@8@H@8@B@D@0@<@D@(@8@H@4@L@B@0@B@4@X@4@8@J@4@8@(@@@<@8@B@J@8@<@8@<@H@8@0@ @@@<@4@8@<@D@@@L@8@B@0@D@8@8@4@4@B@J@B@@@4@F@(@ @D@<@B@<@H@(@B@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?H@F@8@(@R@@@4@V@T@<@0@4@B@8@<@0@8@4@8@8@4@8@8@@@F@L@4@4@@@8@4@F@B@8@4@8@0@8@4@4@<@4@0@4@0@D@4@@@B@H@F@8@Q@4@4@@@4@4@4@F@4@4@4@H@8@@@4@4@4@8@H@F@N@0@P@U@4@@@8@<@@@D@4@B@P@8@4@T@8@4@0@0@8@0@@@8@B@4@8@Q@0@8@8@8@@@8@8@<@<@0@(@(@@@N@`@N@F@F@J@B@N@U@8@F@T@D@J@H@Q@8@H@L@R@D@J@F@H@B@8@B@Q@B@H@Q@N@H@@@8@B@R@V@H@F@J@F@P@<@D@H@H@L@S@V@H@<@W@U@Q@D@H@J@P@H@F@N@R@4@T@P@D@B@R@B@S@J@L@S@R@D@Q@<@V@X@L@R@X@T@H@<@<@4@F@B@D@J@D@F@@@R@B@P@S@N@L@Q@F@Q@D@D@R@J@L@B@S@D@8@L@F@@@8@B@@@J@B@F@L@L@N@D@H@H@J@W@N@B@H@F@S@<@H@R@H@F@H@F@L@D@F@H@4@H@L@D@L@R@U@B@P@D@Q@L@L@F@@@D@F@L@<@@@H@L@J@4@U@N@D@T@H@H@L@0@B@S@S@J@<@S@0@N@H@D@P@H@J@T@8@<@8@H@P@R@@@U@R@P@D@<@J@U@H@B@L@N@B@W@H@D@V@F@F@U@U@F@D@R@N@H@H@U@L@H@T@B@S@N@L@8@P@P@P@@@N@B@8@H@L@F@@@H@8@S@J@S@<@B@P@L@P@F@P@S@T@F@T@D@D@J@H@R@H@U@B@D@@@<@B@8@N@R@B@B@F@L@D@S@P@0@F@H@N@Q@4@F@B@H@L@J@D@N@Q@H@R@<@D@F@U@P@T@H@@@D@F@4@L@F@ @H@P@F@S@Y@Q@J@X@F@J@H@<@H@P@Q@H@H@F@Q@D@S@8@F@D@N@F@0@X@<@S@F@P@Q@N@B@Y@B@J@@@J@@@H@S@H@J@D@H@8@@@@@N@D@H@N@R@J@L@T@F@L@J@F@P@L@S@P@W@P@R@<@P@P@D@J@B@L@P@J@J@Q@D@H@@@H@V@T@L@F@F@F@@@L@S@J@@@P@P@D@<@H@P@@@Q@B@8@D@L@B@S@F@W@L@L@B@H@@@D@L@8@S@@@P@U@F@D@0@<@(@0@B@@@P@8@<@D@8@N@N@N@B@D@@@B@R@@@J@@@@@F@8@F@@@F@D@8@B@<@L@P@8@D@@@D@P@4@L@F@H@L@@@J@@@F@H@4@F@J@0@D@Q@H@D@B@L@@@D@@@@@@@(@D@(@4@0@J@F@F@D@F@B@B@H@J@F@B@F@F@8@J@F@N@B@<@B@D@D@@@H@[@Q@B@B@N@8@H@4@H@H@@@F@@@<@J@(@D@F@@@@@8@8@H@0@B@J@N@L@0@B@J@D@D@@@(@F@F@@@(@@@L@H@S@J@0@H@Q@<@X@<@F@F@<@B@J@J@D@0@4@0@8@0@0@H@D@D@L@B@@@4@B@4@<@<@D@<@@@<@B@B@J@4@Q@F@@@D@N@@@(@8@R@H@D@4@P@8@B@T@<@@@B@^@J@B@P@B@T@4@D@ @4@L@@@B@F@L@T@0@ @P@F@H@J@0@H@H@D@4@4@L@N@<@8@4@B@0@B@B@@@F@4@(@ @0@4@8@F@@@L@4@8@@@F@8@<@4@@@<@4@J@@@8@<@<@@@ @<@@@@@8@@@4@D@4@(@4@8@4@D@4@D@<@0@(@@@8@D@F@8@@@<@<@0@<@<@<@J@8@@@@@0@J@@@@@4@<@@@F@<@<@B@@@ @F@<@F@8@@@D@B@8@F@<@@@F@4@D@B@J@@@N@N@B@@@Q@(@ @<@B@D@@@4@8@D@H@D@D@B@L@D@4@L@<@<@D@4@B@<@8@F@8@8@@@<@0@(@4@0@8@8@F@4@H@<@4@8@B@<@<@D@F@<@8@8@J@B@8@@@@@D@8@4@B@4@@@ @8@B@4@<@<@D@0@B@<@@@D@@@8@8@<@@@J@@@0@@@B@<@(@@@8@<@@@@@<@J@J@J@8@T@@@0@@@ @F@<@8@F@B@H@F@(@(@S@8@D@<@ @F@B@D@<@@@B@F@N@4@<@@@8@F@<@<@D@P@8@4@@@@@8@F@8@N@<@B@J@4@<@F@<@F@@@<@H@@@8@8@F@4@(@B@Q@<@0@@@8@B@<@8@(@8@F@<@<@8@B@0@@@8@4@4@8@B@@@<@<@8@<@(@0@0@4@(@F@<@H@<@H@8@(@F@J@J@F@@@H@N@ @<@0@<@D@<@H@8@<@@@J@0@4@H@8@<@D@B@0@4@<@R@0@0@@<@H@4@D@F@B@B@B@<@@@<@D@4@F@4@@@8@B@D@<@<@ @<@8@4@B@D@B@(@@@<@H@@@D@0@J@4@@@(@H@H@D@J@8@@@<@@@(@4@4@@@8@4@B@8@D@<@<@<@H@8@4@@@H@@@D@L@4@@@0@0@8@D@(@8@4@8@8@B@F@0@F@0@<@8@@@D@H@<@8@0@@@D@@@4@H@8@8@D@<@8@D@@@8@(@<@D@@@4@<@@@B@@@4@<@0@4@B@ @@@0@B@B@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?T@L@V@N@R@N@S@S@L@L@L@R@H@N@P@H@N@R@H@P@P@€a@_@[@V@Q@S@U@N@J@H@H@J@H@B@H@N@P@L@L@P@S@\@L@Q@R@S@N@L@P@P@P@T@L@D@J@V@N@P@S@@@L@H@@@J@F@D@L@N@R@B@T@Q@P@J@S@Q@Q@^@B@R@N@Q@N@F@S@J@N@F@8@8@B@4@4@4@8@8@4@8@F@<@4@0@4@8@@@@@<@8@<@8@4@8@4@<@<@<@8@8@@@B@8@4@4@0@<@<@B@@@@@4@<@<@8@8@4@<@@@8@4@4@8@@@<@<@<@0@8@@@@@8@8@4@F@F@B@8@<@8@H@B@<@8@<@@@8@4@8@@@8@D@4@@@8@8@@@<@B@<@<@8@(@8@<@8@D@<@8@0@B@4@4@8@D@@@<@8@@@B@@@@@@@@@8@<@@@4@8@B@<@4@<@B@@@<@0@D@@@@@4@0@8@@@B@<@<@8@@@4@0@@@<@0@8@<@B@@@H@8@D@8@<@D@8@@@8@8@@@D@8@<@8@8@<@8@8@@@4@P@@@8@<@F@@@4@<@@@D@<@<@B@8@@@B@<@8@4@B@4@@@D@8@@@4@0@B@<@8@4@8@8@@@@@8@(@@@4@@@4@<@@@<@B@<@@@<@<@4@B@<@@@8@4@4@8@@@4@@@<@<@<@4@8@@@4@B@@@\@@@B@k@<@B@H@8@8@8@B@4@0@@@<@4@0@B@U@8@8@R@<@B@<@F@B@8@<@8@<@@@D@<@<@@@B@4@8@<@<@<@8@8@D@<@4@<@B@4@@@@@F@<@<@8@(@@@8@@@8@@@B@4@D@8@0@<@D@8@8@B@<@<@@@4@4@8@8@@@4@<@B@8@@@B@Q@8@8@B@H@<@(@4@<@4@8@H@4@B@<@B@<@H@<@8@0@8@8@4@@@8@<@4@4@@@<@4@<@8@<@4@<@8@<@@@8@B@4@@@4@B@4@<@<@J@B@N@L@F@L@B@P@F@B@F@H@F@H@Q@F@D@N@H@H@D@J@H@J@L@Q@@@H@N@L@D@J@L@N@L@D@J@B@L@N@J@L@N@N@N@(@H@F@H@L@N@F@J@J@S@N@H@L@8@N@L@Q@N@<@F@P@P@J@8@H@@@P@N@H@L@B@N@J@D@B@P@P@H@L@L@L@H@N@Q@N@L@H@L@L@H@J@H@H@N@R@F@Q@J@D@H@N@J@J@J@F@D@B@P@F@<@D@J@J@H@R@J@L@S@B@L@Q@P@F@N@J@H@H@L@N@N@N@F@J@L@@@L@J@N@H@F@N@H@P@Q@N@[@D@L@N@@@L@H@P@Q@J@L@N@L@F@P@J@H@B@P@H@Q@J@H@J@N@@@P@P@@@H@N@F@B@L@B@N@H@L@N@D@N@J@R@J@N@N@J@F@@@J@N@R@P@F@H@L@L@J@P@L@J@D@J@P@N@F@N@N@F@N@L@R@F@N@Q@F@L@P@F@L@P@P@N@F@Q@N@L@B@D@Q@N@J@L@N@Q@R@J@P@P@Q@@@F@D@8@L@J@D@F@N@B@F@L@H@H@H@R@T@N@<@L@D@D@L@D@H@<@L@D@F@H@L@L@F@N@J@H@Q@N@L@@@N@Q@L@N@L@P@[@N@D@P@V@H@8@P@H@J@P@P@]@N@L@F@J@F@J@F@P@L@F@0@L@P@H@J@D@L@H@L@H@Q@H@F@J@N@F@N@J@W@L@F@B@P@4@8@J@@@N@J@H@N@D@F@N@L@H@4@J@N@N@0@J@D@S@@@<@4@@@@@B@D@D@D@N@F@<@<@B@F@D@4@(@B@8@4@<@@@B@8@F@0@<@@@R@B@B@B@<@B@F@B@J@Q@@@(@@@T@J@L@F@F@(@(@<@0@Y@8@(@ @(@Q@(@(@R@<@4@(@R@P@0@4@4@0@(@(@R@ @0@(@0@0@R@0@0@4@4@(@(@(@@0@4@8@4@4@(@4@0@0@0@H@0@4@(@(@(@0@4@0@4@0@ @0@4@4@(@(@ @<@<@8@0@4@P@<@8@0@(@0@4@(@(@(@0@(@8@(@4@(@0@4@0@8@0@4@(@@(@4@ @<@0@(@ @8@ @(@0@8@0@(@0@H@8@4@4@U@4@0@0@4@(@(@8@0@ @0@8@8@S@ @8@4@0@ @@0@4@8@0@4@(@4@ @ @4@0@ @(@0@8@4@<@(@<@(@0@4@(@4@(@(@4@<@0@0@(@(@0@(@(@4@@J@4@T@0@4@4@ @0@4@8@0@4@4@(@4@<@0@(@(@8@(@4@8@0@4@(@ @4@4@0@(@Q@0@4@4@0@@4@(@8@ @0@4@0@8@0@4@0@0@(@4@0@4@(@(@(@R@4@ @4@0@0@(@(@0@0@ @8@4@W@4@8@]@R@8@<@0@ @ @8@(@ @4@0@(@ @V@Q@(@(@H@0@8@R@@@8@S@0@0@0@4@8@0@0@4@4@(@D@S@0@0@S@N@8@P@(@0@0@ @4@4@<@0@0@(@D@4@0@S@(@4@8@(@4@(@ @4@8@0@ @4@U@4@0@T@(@0@(@4@(@0@8@0@4@4@N@(@(@4@V@0@@(@4@N@0@@@(@8@(@8@0@J@0@(@Q@(@0@(@4@(@X@(@J@V@4@ @4@ @4@ @0@(@0@4@(@Q@(@4@(@J@L@0@0@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?4@4@N@R@0@H@L@N@S@R@€`@^@N@H@N@4@N@U@P@V@V@P@R@Q@T@X@Q@U@U@U@J@N@N@R@<@U@P@T@\@R@B@R@F@D@@@D@D@D@@@8@8@8@B@0@B@B@H@B@<@4@8@F@B@D@D@@@<@4@@@B@B@(@8@@@8@B@0@F@D@B@4@@@<@B@D@B@B@<@8@<@4@D@F@F@D@8@<@@@D@@@J@B@B@8@B@<@<@B@B@@@D@@@<@@@B@F@@@4@8@F@<@H@F@H@8@0@@@4@D@@@J@D@D@(@H@D@B@<@<@F@D@@@D@J@B@@@@@<@@@B@F@D@@@F@B@<@4@@@F@J@8@H@@@B@<@F@@@<@B@F@B@D@B@@@F@D@B@@@D@B@B@D@<@@@@@4@@@<@@@D@@@<@B@@@F@B@F@4@<@<@D@F@B@B@@@J@<@@@<@B@D@8@<@B@H@B@4@J@<@F@@@8@8@N@<@F@@@<@B@B@D@D@B@B@@@@@D@4@B@<@D@B@B@@@L@D@8@D@D@D@F@F@B@8@@@F@D@J@<@F@J@D@B@D@D@H@@@<@H@B@@@8@@@4@@@B@D@F@B@B@D@8@8@B@F@D@B@0@B@D@F@F@H@B@F@D@D@@@F@B@@@B@D@H@D@D@F@@@<@D@<@J@F@B@a@8@H@N@k@D@L@J@@@8@D@B@@@@@@@8@<@F@B@<@8@N@F@H@@@0@B@L@@@B@H@4@F@F@D@F@D@D@N@8@8@@@B@<@<@4@F@@@D@8@D@H@D@8@D@8@F@@@B@<@D@F@8@H@F@N@F@8@H@@@8@<@<@D@B@F@@@D@@@<@D@J@<@8@8@@@L@F@@@B@H@<@D@F@R@@@F@B@B@@@H@H@8@<@D@<@@@J@<@F@F@4@B@@@D@@@B@D@@@B@<@B@8@D@8@D@@@@@8@F@F@B@8@B@F@@@B@D@F@<@<@F@0@@@U@B@F@J@J@<@F@J@N@L@H@H@F@L@H@@@8@B@F@H@P@8@H@B@<@F@F@P@H@F@H@D@F@D@F@H@F@P@H@J@<@@@J@J@B@F@H@J@P@L@B@D@J@D@J@H@J@L@J@L@8@F@B@H@B@L@L@B@F@H@N@L@J@J@H@L@J@J@N@H@N@H@F@L@W@R@J@J@F@@@D@<@F@R@F@D@H@H@P@B@H@L@F@J@B@B@F@J@L@F@L@L@J@F@<@F@H@F@B@4@F@D@D@F@F@L@@@N@D@F@D@D@H@@@F@D@L@J@N@F@B@J@J@L@L@D@H@L@H@H@L@F@F@H@N@B@D@P@D@F@J@H@P@J@L@8@P@J@H@J@8@H@L@J@@@L@F@F@L@P@N@[@N@<@P@H@B@P@L@H@J@H@J@J@F@D@J@Y@J@D@J@F@H@N@F@B@H@J@H@@@L@B@<@N@L@@@@@D@B@F@B@B@J@N@B@F@P@F@H@J@F@L@@@8@H@H@J@F@D@N@F@L@F@H@J@H@H@N@D@H@F@F@@@L@H@H@H@D@P@@@L@H@<@F@J@4@H@N@J@J@F@F@N@N@J@F@4@<@P@L@J@H@L@J@J@L@P@L@U@(@L@@@B@D@D@N@D@H@H@@@J@8@F@@@H@H@J@P@H@4@F@@@J@D@J@J@J@B@J@4@F@H@@@D@B@L@N@F@P@H@H@N@J@J@P@J@D@L@L@L@N@Y@L@P@D@J@Q@D@D@8@D@H@B@L@J@L@J@Z@N@F@N@F@D@F@D@F@H@J@L@F@F@@@L@D@F@L@F@F@J@@@H@J@H@D@B@J@L@H@J@J@T@F@H@N@P@D@<@J@4@H@@@D@N@D@P@H@F@4@@@F@P@H@L@F@<@F@D@N@4@8@4@4@4@F@F@4@B@4@4@ @B@8@<@4@<@D@@@0@0@(@<@F@H@4@8@R@H@<@@@0@@@<@4@X@4@<@D@0@0@(@4@<@8@8@@@4@4@S@0@4@8@8@8@4@0@(@4@8@4@4@(@4@(@4@ @<@<@8@L@4@0@8@8@8@8@0@(@0@(@8@<@8@<@(@4@8@8@4@@@8@8@0@4@0@0@4@8@0@<@4@0@4@4@8@4@(@0@8@0@@@<@@@(@ @4@(@8@T@B@8@8@@@@8@8@0@0@<@8@4@8@B@4@4@4@0@4@4@<@8@4@@@8@4@(@4@<@D@0@<@4@8@0@<@4@0@8@<@4@<@<@B@@@8@8@4@<@8@4@8@0@4@4@R@4@0@4@<@4@0@8@4@@@4@@@(@0@0@8@@@4@8@8@B@0@4@0@8@4@(@0@8@8@8@ @@@0@@@0@0@0@D@0@8@4@0@4@<@8@4@4@4@4@4@4@(@8@0@8@8@8@4@D@8@(@<@4@<@<@8@8@(@4@8@<@B@0@@@B@8@8@<@8@<@4@4@@@4@4@0@4@R@4@8@8@<@4@8@<@S@0@4@<@<@8@ @4@<@8@<@B@4@@@8@8@4@<@8@4@4@<@<@8@0@8@4@0@8@0@D@<@8@B@(@<@F@T@8@D@B@4@(@8@8@0@(@4@(@0@@@4@0@(@<@@@@@4@ @8@D@4@4@<@Q@8@@@<@@@8@8@<@(@P@4@8@0@4@(@<@0@8@0@8@<@8@(@<@(@@@4@8@4@J@@@0@<@@@H@@@(@@@4@(@0@0@<@4@@@4@8@T@4@8@B@4@(@(@0@<@<@4@4@@@4@<@@@L@U@@@4@8@4@R@<@0@4@<@0@4@B@0@@@@@(@8@4@<@4@4@0@4@8@0@8@0@8@(@8@4@4@L@B@@@8@0@4@@@4@8@8@<@4@0@<@ @4@N@4@8@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?8@8@4@0@T@T@4@0@8@0@U@P@U@W@P@T@X@S@Q@U@T@V@4@T@V@V@S@@@N@H@F@H@J@H@@@B@B@<@D@<@D@F@J@J@H@8@@@D@J@H@D@F@D@D@@@B@P@H@F@B@B@B@F@D@B@H@J@N@J@H@D@N@J@F@B@B@F@D@@@@@D@J@S@H@F@B@D@F@D@D@F@H@J@F@B@D@J@H@D@L@H@D@H@D@J@J@B@D@J@J@B@L@@@4@N@N@4@@@B@F@@@D@B@J@H@H@<@H@H@V@@@B@L@F@H@B@J@D@B@D@B@B@D@F@N@D@H@H@@@@@@@F@N@@@J@@@N@B@H@F@J@B@D@D@F@D@F@J@D@J@J@H@F@F@J@F@J@H@F@H@4@J@F@F@H@H@B@N@D@F@H@L@H@@@@@D@F@H@F@D@L@D@D@F@H@D@H@F@L@D@D@8@N@B@<@<@@@0@D@N@B@J@F@B@H@D@J@F@J@F@H@L@F@<@D@L@D@B@F@D@J@H@@@F@J@F@H@F@D@@@H@J@F@L@D@J@N@H@F@F@F@J@D@D@L@D@F@D@4@D@<@D@D@N@F@D@D@D@@@H@D@J@F@D@D@F@D@H@J@L@H@F@F@H@D@Q@F@D@J@H@J@H@H@F@H@B@H@B@J@L@F@V@H@J@L@€`@L@L@L@D@D@F@F@F@L@B@@@B@F@B@B@@@@@H@L@@@8@F@F@L@H@H@J@J@D@D@F@J@H@N@D@B@H@L@<@J@@@F@J@J@D@J@H@4@F@B@F@F@L@B@B@J@<@F@H@@@J@H@Q@F@J@J@B@<@B@H@H@J@D@F@D@J@F@D@J@J@B@B@@@D@S@H@B@F@L@<@B@4@H@N@D@F@F@D@B@J@D@H@P@B@F@N@H@F@L@@@H@F@8@D@B@D@J@L@F@<@H@B@H@@@P@B@H@@@J@J@H@@@F@L@B@N@H@F@D@@@R@<@L@F@F@H@H@H@4@D@H@N@H@F@F@H@J@H@F@B@4@4@J@F@J@<@@@F@<@P@D@L@H@B@J@J@D@B@@@@@D@@@J@F@<@<@@@F@D@B@@@F@F@D@H@H@D@F@H@F@@@H@J@F@F@B@<@B@@@@@H@H@4@@@F@8@L@F@D@D@@@L@L@F@B@F@L@F@J@Z@F@H@\@U@N@B@D@D@L@@@F@B@Q@F@D@N@J@8@H@D@F@H@D@<@D@H@J@8@N@B@J@H@@@F@D@B@B@8@@@4@F@@@F@@@<@H@B@J@D@N@D@F@<@<@H@<@H@H@J@D@D@F@F@H@H@J@@@F@F@F@S@F@@@D@P@B@@@@@D@D@H@H@N@J@H@B@N@F@B@D@D@<@H@B@F@J@D@@@L@P@Q@[@L@F@<@N@B@@@J@D@F@F@F@@@L@<@4@L@X@J@D@F@F@F@L@F@@@D@L@B@D@H@<@D@H@F@4@8@@@@@D@<@J@F@L@@@B@H@D@J@J@F@H@L@8@F@4@J@<@L@B@D@F@H@D@F@F@H@F@D@L@F@F@D@F@0@J@F@H@J@4@L@@@D@B@B@@@H@F@B@J@J@H@H@D@D@<@@@L@H@ @<@B@N@H@D@F@H@D@D@H@H@H@W@T@H@8@L@D@@@N@B@D@F@N@B@B@F@B@F@D@H@J@D@<@@@B@D@B@F@F@L@F@B@D@@@D@@@H@H@4@@@L@@@J@L@F@J@N@F@H@N@J@B@L@8@J@L@V@D@F@F@<@H@P@<@B@ @H@D@D@J@F@H@D@Y@L@F@J@B@D@H@Z@@@J@J@J@F@B@N@0@B@4@4@J@F@L@F@D@D@F@B@B@N@D@H@J@H@@@H@U@@@D@U@N@0@X@D@D@F@<@0@L@B@N@H@8@<@H@@@L@4@J@8@Q@D@B@L@8@4@S@4@H@8@B@@@@@@@@@@@F@8@8@0@8@D@8@<@B@@@4@(@0@<@@@@@8@<@8@8@4@8@D@@@<@8@4@8@0@<@4@@@@@B@L@<@8@F@@@<@8@8@8@8@4@B@<@B@8@<@<@4@<@<@8@8@8@@@8@<@4@<@B@8@8@D@@@4@8@8@B@B@8@<@B@B@8@D@0@(@D@F@(@0@4@<@0@8@8@@@8@@@0@@@@@@@4@8@@@<@8@8@@@8@4@<@8@8@8@@@D@4@B@@@4@4@4@<@D@B@<@4@D@8@<@@@B@8@8@8@@@8@@@B@8@B@B@@@<@<@@@@@B@@@8@@@S@@@<@<@B@@@8@B@8@@@<@B@D@4@4@<@@@@@<@8@D@<@8@<@<@D@<@<@F@<@<@(@D@4@D@0@0@ @8@F@4@D@<@4@F@@@B@<@<@@@<@B@<@0@8@D@<@8@@@8@B@@@4@<@B@<@@@<@<@4@@@B@<@@@8@B@D@@@<@<@<@@@8@<@D@8@8@8@(@8@0@8@<@8@<@8@8@8@8@0@<@@@<@8@<@8@<@<@B@D@<@<@<@@@8@H@<@8@@@@@B@@@B@<@@@4@B@4@B@B@<@@@4@D@D@V@D@F@F@8@8@<@@@<@D@8@0@8@@@8@8@4@0@B@D@8@0@<@<@F@@@<@B@@@<@F@@@B@@@F@4@8@<@B@0@<@4@<@<@<@8@@@@@(@<@8@<@@@F@(@8@@@4@<@@@4@@@@@J@<@D@B@8@0@4@@@B@@@8@@@4@@@8@<@B@B@8@4@4@4@H@@@8@<@B@0@8@(@B@F@<@<@@@8@J@<@8@<@F@8@<@F@@@@@D@4@@@@@0@<@8@8@D@<@@@4@8@4@B@0@H@8@8@4@D@B@@@4@<@D@8@B@@@<@8@0@L@0@B@@@<@@@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?8@4@8@4@U@4@0@8@4@0@4@8@0@<@4@8@4@4@4@8@4@T@L@T@B@8@(@0@0@S@4@4@8@8@8@(@V@0@0@(@F@(@8@0@0@0@4@V@4@<@8@J@H@F@L@H@J@D@F@F@L@@@B@H@N@H@H@L@H@F@N@H@F@H@F@H@B@L@H@F@F@H@H@D@F@B@F@J@H@J@N@J@D@F@N@F@@@B@H@F@H@D@F@F@N@H@H@H@F@H@H@@@H@F@L@J@F@D@F@N@B@J@F@H@J@L@B@L@F@F@J@D@H@H@J@L@H@F@<@U@J@H@D@D@F@J@H@F@F@H@H@W@@@N@L@H@B@H@F@J@F@B@D@H@H@B@D@J@D@J@H@B@F@F@F@H@@@H@D@L@F@J@F@F@F@H@H@F@H@D@H@D@F@J@H@F@B@J@D@F@H@N@F@N@J@F@J@F@P@D@F@F@B@L@J@J@B@H@B@H@J@F@H@D@F@D@J@F@D@J@H@H@@@H@@@L@F@B@P@F@B@F@N@J@F@H@N@H@H@F@H@H@D@J@J@H@D@N@L@F@D@F@F@J@D@F@H@F@F@L@H@F@<@H@P@F@F@F@P@D@J@L@H@F@F@H@J@H@F@J@D@L@F@U@B@8@F@F@J@0@F@F@D@F@D@F@J@D@N@J@H@F@L@F@F@J@J@H@F@D@H@F@L@H@J@L@H@J@H@F@F@B@H@H@B@F@J@H@F@J@N@H@H@U@H@H@H@D@H@F@J@<@F@F@B@N@F@D@B@H@D@B@F@F@D@@@H@F@L@F@J@H@D@L@B@B@F@H@F@D@F@H@H@N@B@H@F@D@H@P@D@F@H@B@H@S@H@H@J@0@F@D@H@D@H@J@B@H@F@L@J@F@H@F@F@F@H@D@F@F@F@F@J@S@F@8@H@H@H@H@F@D@V@H@B@H@L@N@B@@@F@R@D@D@H@B@B@F@H@H@H@N@D@F@J@H@L@H@L@F@H@D@D@L@H@H@N@D@@@F@H@F@H@P@J@H@D@<@F@J@F@D@H@F@L@Q@H@J@F@H@J@@@P@J@J@H@J@F@V@H@J@D@F@H@@@D@L@L@J@(@(@8@<@D@J@4@<@L@8@H@D@Q@8@J@L@Q@B@D@B@D@F@8@N@H@8@8@8@D@B@4@H@F@F@D@B@F@F@D@H@(@H@@@D@L@L@F@B@D@D@B@0@F@L@8@@@D@8@L@8@H@8@D@J@L@H@F@H@J@J@D@W@D@F@^@N@J@F@D@F@N@B@D@D@N@F@F@N@4@4@L@B@D@F@F@F@<@B@B@J@4@N@L@H@H@B@F@D@B@F@<@B@D@B@J@D@D@B@L@F@H@D@Q@D@F@8@@@N@8@F@F@H@F@H@F@B@D@H@0@4@J@B@N@H@D@H@Q@8@<@B@B@8@J@F@P@Q@D@D@D@H@@@B@F@D@D@B@J@F@B@D@J@Q@L@Z@D@B@D@@@B@D@B@D@L@L@F@8@J@4@4@N@S@B@D@L@D@H@L@J@@@D@N@D@D@F@F@H@D@B@J@0@<@(@D@8@N@8@J@@@@@L@L@H@J@H@L@D@F@8@J@D@L@J@L@B@H@D@F@B@F@D@H@F@F@F@L@L@H@D@(@J@F@J@H@4@F@4@D@<@H@B@H@Q@H@B@H@D@L@L@@@D@N@8@F@L@L@B@D@R@L@D@J@<@L@B@D@F@<@L@X@T@H@4@T@F@F@N@8@B@J@N@D@D@J@<@@@F@F@F@H@D@@@H@4@D@@@B@D@J@J@B@@@8@D@B@N@H@@@L@F@Q@H@J@D@D@B@L@4@P@H@J@D@N@H@Y@F@D@H@D@F@P@D@D@J@4@D@F@F@D@H@B@[@N@H@F@D@ @N@X@@@D@J@N@J@B@F@_@0@B@D@B@N@F@J@F@8@B@@@@@B@L@H@4@H@J@D@N@V@8@8@T@J@4@R@D@D@N@B@B@4@H@F@L@H@4@D@B@8@J@B@H@4@Q@D@D@4@ @0@ @8@0@(@4@4@0@ @(@8@0@0@D@0@B@@@<@B@@@B@<@<@@@D@D@8@@@B@8@<@D@@@@@B@@@@@@@<@<@8@B@<@<@<@@@@@8@<@8@<@B@@@@@B@B@<@F@B@<@4@8@@@8@@@8@<@<@N@<@<@B@@@@@@@4@<@<@B@@@<@<@8@B@8@@@<@@@@@D@B@B@@@<@B@F@@@@@B@D@F@8@4@4@B@@@8@8@8@@@@@<@<@@@@@J@4@B@@@@@4@@@@@@@<@D@8@@@@@B@8@@@8@@@@@8@@@@@<@@@B@H@8@D@<@B@<@F@<@@@@@<@@@<@@@8@<@B@@@<@H@@@8@<@@@F@<@B@<@<@@@<@<@<@F@<@F@B@@@B@4@@@8@@@B@<@@@D@<@<@@@<@F@B@<@@@D@@@4@B@<@F@H@<@0@@@F@@@<@@@D@H@@@@@@@8@<@@@@@@@0@D@B@<@<@@@<@@@<@<@@@<@<@B@@@@@H@@@F@<@B@<@F@B@@@B@@@8@<@@@@@@@8@@@8@D@<@H@8@(@<@@@B@ @@@@@8@<@F@<@<@<@D@@@<@<@B@<@<@B@@@<@<@<@@@<@B@<@<@D@<@B@<@<@<@D@<@@@F@<@B@H@H@8@D@D@@@8@@@B@<@8@<@<@B@0@D@<@8@F@<@<@8@<@B@@@<@<@8@4@B@@@D@<@@@@@N@B@@@F@<@@@<@D@<@<@@@D@B@B@<@<@D@H@8@F@@@8@@@@@@@@@B@ @<@@@<@8@<@@@8@<@<@B@D@H@@@<@8@<@@@8@<@<@<@<@@@<@<@D@@@@@@@<@@@F@F@<@4@B@@@B@8@0@8@H@@@<@@@4@8@8@<@@@<@B@8@<@B@<@D@@@@@8@@@<@8@D@@@B@D@<@4@@@@@@@@@F@B@@@8@4@@@@@<@<@<@<@D@H@@@@@<@@@B@4@F@D@B@@@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?4@8@0@0@0@4@8@0@@@0@(@(@(@4@4@V@(@0@<@4@<@4@<@4@8@0@0@4@8@4@<@4@4@8@(@0@4@4@8@<@0@8@4@4@4@0@0@T@0@<@4@8@(@4@0@(@0@8@0@B@4@0@4@<@8@8@8@8@<@0@8@0@4@4@0@B@4@8@4@0@4@0@4@0@4@8@T@8@4@4@0@<@8@0@0@<@4@0@8@0@0@J@H@F@N@J@F@D@H@F@J@B@D@F@J@F@H@J@H@H@P@D@D@H@H@J@F@L@H@J@F@J@F@D@D@B@H@J@H@J@Q@H@B@F@H@H@<@B@H@H@H@D@D@D@J@F@N@H@F@H@F@8@F@H@J@H@D@F@D@L@D@L@H@D@J@L@B@J@F@H@J@F@H@B@J@W@H@F@X@^@J@D@F@D@D@J@D@H@H@H@F@R@@@J@L@H@P@F@F@L@H@@@D@D@D@B@D@H@D@H@J@D@F@H@H@H@@@H@H@L@D@J@H@D@F@F@H@J@F@D@J@F@F@H@F@F@D@F@D@D@D@J@F@J@J@F@H@H@S@F@H@H@F@N@H@J@B@D@B@F@F@F@H@D@H@F@L@F@F@J@J@H@B@F@B@F@F@@@Q@[@F@F@N@H@D@H@J@H@L@H@F@L@F@H@<@J@F@a@J@J@B@B@F@H@F@D@H@B@H@H@F@H@F@D@D@H@L@F@F@H@P@D@P@J@L@D@H@H@H@H@L@H@F@L@F@U@D@B@B@B@F@D@N@H@H@D@4@D@L@N@D@J@H@F@F@L@D@H@F@H@H@D@F@D@H@L@J@J@J@H@H@F@F@D@F@J@J@F@F@J@L@H@J@N@L@J@@@H@H@S@B@H@<@F@D@F@Q@F@F@L@F@@@B@H@D@8@D@D@H@@@H@H@J@D@F@H@D@J@@@F@D@F@F@D@F@H@H@J@B@F@H@D@H@H@F@F@H@F@F@P@J@B@H@F@H@J@J@R@J@F@D@F@H@J@F@F@F@B@]@H@H@D@B@F@F@F@J@B@F@H@J@F@H@L@H@F@^@J@D@F@L@J@B@D@D@R@F@F@H@@@F@@@F@F@J@J@D@B@D@H@D@D@N@B@H@J@B@D@H@J@J@F@D@H@J@B@J@N@@@H@D@@@D@J@D@F@H@F@D@Q@F@J@F@D@J@D@J@H@H@J@H@D@S@H@F@D@F@F@<@B@L@J@J@(@(@B@B@B@J@8@<@L@B@F@B@Q@D@L@H@Q@B@F@D@D@H@@@L@F@4@B@H@B@4@H@<@D@B@@@H@D@J@N@F@B@J@J@B@@@D@B@B@(@D@J@8@@@@@J@4@H@B@D@H@H@F@F@F@H@B@4@B@J@S@P@H@B@F@L@B@J@B@P@D@D@N@B@L@B@D@D@B@D@@@<@H@4@R@L@F@H@@@L@B@H@D@B@@@D@@@H@D@B@@@L@F@F@B@L@B@D@@@P@J@0@N@F@F@F@F@D@B@H@F@4@4@H@B@L@J@D@F@N@B@<@@@J@D@H@N@N@Q@D@D@@@D@<@@@D@D@B@F@F@H@@@@@H@Q@H@L@B@@@D@<@<@B@D@D@<@H@F@H@0@F@4@P@B@D@L@D@L@F@H@8@<@F@L@@@D@F@F@F@B@<@0@H@ @@@0@4@J@4@B@4@J@H@H@H@F@J@B@H@F@L@B@L@P@N@<@F@8@D@F@D@B@F@D@F@F@L@J@H@D@(@H@D@J@F@@@D@0@D@<@L@B@H@P@J@@@D@J@L@D@Q@8@S@<@D@L@D@H@L@<@L@D@B@B@J@X@R@F@B@F@B@D@J@4@H@H@N@D@D@J@H@@@F@B@L@D@H@8@F@0@B@@@F@J@L@@@B@<@0@D@<@H@F@<@J@D@L@N@B@D@H@H@Q@N@@@Q@B@L@H@F@D@F@D@F@N@D@D@S@L@D@F@D@J@@@L@@@S@J@F@D@D@L@X@8@B@N@L@H@@@F@L@(@0@B@L@F@H@F@B@R@<@F@@@N@H@^@F@H@D@L@T@4@R@V@F@4@R@B@F@L@H@B@0@D@D@J@H@4@L@D@4@J@B@D@@@N@D@D@N@0@0@4@0@@@<@<@B@<@@@8@<@<@@@D@<@@@J@B@<@@@<@<@D@D@8@<@<@@@8@@@8@@@<@@@<@8@8@B@<@@@<@@@D@@@4@D@H@@@0@4@<@<@@@4@8@B@L@D@@@@@<@<@B@8@8@<@@@<@4@<@@@B@8@@@<@D@B@B@B@@@B@@@<@D@<@F@@@€`@H@F@Q@R@B@<@<@8@8@@@D@<@@@<@8@H@4@<@<@@@D@8@F@@@<@D@8@8@B@B@8@<@8@<@@@8@@@@@<@<@B@J@@@D@<@@@<@F@<@8@<@<@8@8@@@<@8@<@<@8@F@8@4@D@4@@@<@<@@@8@<@<@@@<@F@@@H@D@B@@@4@8@B@<@<@<@@@D@4@8@@@8@F@B@<@<@D@8@8@<@<@F@H@U@@@8@F@D@F@<@@@F@D@<@<@<@@@<@0@<@<@Z@@@@@8@8@<@F@D@8@D@4@<@8@<@D@8@8@H@<@B@8@D@<@L@4@@@D@B@D@D@<@B@@@B@<@<@B@<@D@8@4@8@B@H@8@F@@@<@D@F@F@<@B@8@@@<@8@<@@@8@<@<@<@<@B@<@8@@@B@@@@@B@@@@@8@<@8@F@<@@@F@<@B@H@H@8@D@F@@@0@8@@@D@8@<@D@<@D@<@F@<@8@B@<@B@4@<@F@@@8@B@@@4@@@@@B@8@8@@@J@@@B@F@F@D@D@B@<@<@<@@@D@<@@@8@D@<@8@F@@@<@<@<@@@D@<@<@B@@@B@8@<@<@8@<@<@@@@@H@8@D@P@<@@@8@D@<@<@@@@@4@<@<@<@D@<@@@@@J@Q@@@8@<@B@@@8@4@4@<@@@<@<@D@<@4@D@8@<@@@8@B@8@<@H@<@B@D@<@@@4@D@<@@@@@<@8@@@@@4@@@8@D@8@<@4@4@@@B@8@<@8@D@F@@@@@8@<@@@8@<@@@<@B@ð?ð?ð?ð?ð?ð?ð?4@4@4@8@0@0@4@(@4@4@8@0@<@4@0@(@0@(@8@4@V@4@4@@@8@@@8@0@8@8@0@8@4@0@4@0@4@(@8@4@8@(@4@8@8@(@0@8@0@(@4@@@0@8@0@4@8@8@<@@@0@4@8@(@4@<@0@0@0@8@8@4@Q@4@8@4@@@4@4@8@4@0@0@<@8@0@8@8@<@<@8@8@4@@@0@4@4@4@8@4@4@<@0@<@8@4@8@4@4@0@0@8@B@D@0@8@4@4@0@4@8@4@0@8@0@0@<@<@(@4@4@0@0@0@8@0@0@(@4@0@0@L@H@F@P@J@B@F@H@D@F@D@D@F@H@P@H@H@J@P@Q@F@D@H@L@J@N@N@H@P@F@H@F@D@H@D@Q@H@J@J@Q@H@B@F@H@H@@@D@H@J@H@D@F@H@L@F@N@J@H@H@F@8@B@F@J@H@J@F@F@L@D@Q@P@S@L@J@B@J@F@H@Q@F@N@@@L@€b@H@F@[@^@J@D@F@D@F@H@F@J@J@H@B@S@B@N@R@F@Q@H@H@N@J@B@D@@@D@B@S@F@D@H@L@D@F@H@H@H@@@J@H@P@B@H@S@D@F@Q@F@J@D@F@T@D@F@S@F@D@D@F@D@B@B@H@B@J@Q@L@H@J@T@F@H@J@S@L@H@J@B@D@B@L@J@D@F@D@N@F@N@B@H@H@N@J@F@N@D@P@J@B@J@\@F@L@P@J@F@F@J@H@N@P@D@R@F@H@Q@H@F@€`@H@J@B@<@H@J@F@D@H@F@J@L@D@H@F@D@H@J@L@F@H@S@N@F@P@J@N@T@H@H@J@H@N@F@J@L@F@T@D@D@B@D@F@F@P@F@H@H@H@D@L@R@B@H@J@D@F@J@F@F@F@J@H@D@F@D@J@U@L@J@J@J@J@H@D@H@H@L@J@Q@L@L@L@J@H@P@N@Q@@@L@H@S@F@J@B@D@D@Q@S@H@J@V@H@B@B@H@D@8@J@D@D@B@H@H@Q@D@F@H@F@L@D@J@H@F@N@F@F@H@F@H@N@H@H@D@H@D@R@F@F@D@F@P@L@H@J@(@H@J@Q@J@R@N@N@F@F@H@L@@@J@F@F@^@H@H@H@D@H@D@F@J@B@F@H@N@H@J@L@D@H@b@H@F@D@L@J@B@^@H@N@F@D@H@B@H@<@F@S@J@L@D@F@H@H@J@B@N@D@F@J@B@D@H@J@J@D@T@H@J@D@R@B@@@H@B@<@H@J@D@H@H@L@D@R@D@F@D@D@J@D@J@F@F@H@F@R@D@H@L@D@8@B@J@B@H@@@B@4@B@H@@@(@B@L@P@<@D@B@@@L@D@0@@@F@B@H@4@B@F@B@F@J@D@F@H@@@D@B@@@H@8@F@D@F@F@D@D@@@H@N@J@0@<@D@N@@@H@N@B@H@L@<@B@4@<@F@0@L@D@F@J@B@D@8@8@B@<@D@J@J@D@H@B@@@@@H@H@J@F@B@D@D@F@J@H@H@H@D@D@@@H@B@F@H@Q@J@B@B@B@@@D@B@B@4@N@D@H@F@D@B@B@D@F@F@(@4@N@B@J@@@H@8@8@8@B@J@B@D@F@F@8@<@F@<@<@F@F@D@F@D@J@B@F@<@L@J@N@P@4@@@B@D@L@D@D@F@F@L@J@H@B@(@F@B@H@B@(@B@8@N@H@L@F@B@D@J@B@Q@P@4@F@F@H@J@8@H@V@N@D@@@N@4@0@J@F@F@B@B@H@H@B@F@@@4@D@0@B@B@H@J@<@B@<@B@4@F@J@D@J@R@B@D@P@L@F@P@J@F@B@B@B@D@P@B@B@R@L@D@H@L@H@4@J@B@J@4@B@R@L@F@@@F@L@@@J@B@D@S@D@<@L@D@[@D@F@B@L@@@0@Q@V@4@D@R@F@D@@@(@B@D@F@4@L@D@8@H@@@J@@@L@@@D@4@4@<@@@<@8@D@@@D@8@8@8@F@D@8@<@H@D@<@<@<@<@D@D@D@8@B@<@8@@@<@B@<@<@8@@@8@B@8@@@<@<@B@<@4@D@H@@@0@4@8@<@@@4@4@D@L@D@<@<@8@<@B@8@B@8@<@8@8@8@@@@@8@@@8@H@B@<@B@<@F@<@@@D@<@F@@@Z@J@H@P@R@B@8@<@4@<@<@F@8@B@<@@@F@D@<@<@<@B@B@H@@@@@B@4@@@D@@@D@8@4@<@@@8@@@8@@@B@D@H@<@F@4@<@8@F@8@8@B@8@F@8@<@8@4@8@8@8@D@4@4@D@0@<@4@<@<@B@8@<@<@8@D@@@F@B@D@<@4@@@B@H@D@8@<@D@8@8@@@@@D@@@8@8@F@D@<@D@8@F@F@N@<@<@D@F@H@8@D@F@H@D@8@<@<@8@@@8@<@X@D@<@4@@@<@F@D@8@F@8@<@<@4@F@8@8@F@<@@@8@D@8@N@8@<@H@B@D@F@8@F@8@B@B@@@@@<@B@8@8@8@B@H@@@F@<@8@F@D@4@<@B@4@8@<@4@<@<@<@8@<@<@<@F@<@8@<@@@@@<@@@@@@@<@8@8@F@<@<@H@@@B@J@H@4@D@H@8@@@8@<@F@8@<@4@D@D@B@F@<@8@@@<@D@4@8@F@@@8@D@8@4@<@<@@@4@4@<@J@<@B@H@H@D@F@D@8@<@8@<@D@<@<@4@D@B@4@F@<@8@<@@@<@J@<@F@F@B@<@@@<@@@@@8@8@<@@@4@H@8@D@N@<@<@8@D@<@8@<@<@@@8@<@<@D@@@<@8@H@R@8@4@D@@@<@4@N@B@8@<@8@<@D@<@0@D@8@<@<@8@D@D@<@H@@@@@D@D@@@@@D@<@@@<@8@D@<@<@B@<@4@D@<@4@0@B@<@B@<@B@<@D@H@<@D@8@8@<@8@<@@@B@@@ð?ð?ð?ð?ð?ð?ð?ð?ð?š™™™™™É¿333333ã¿ð?ÍÌÌÌÌÌì?à?à¿333333Ó¿š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿š™™™™™Ù?š™™™™™É¿š™™™™™É?š™™™™™É?š™™™™™¹?à¿à¿š™™™™™¹?à?࿚™™™™™Ù?š™™™™™¹?š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌì¿333333û¿š™™™™™¹?ffffffæ¿333333㿚™™™™™É?š™™™™™¹¿š™™™™™¹?š™™™™™Ù?š™™™™™é¿š™™™™™É¿333333Ó¿š™™™™™¹¿š™™™™™¹¿333333㿚™™™™™É¿š™™™™™¹?࿚™™™™™é¿š™™™™™É?š™™™™™¹?š™™™™™¹¿š™™™™™É¿333333㿚™™™™™¹¿333333Ó¿š™™™™™Ù¿333333Ó¿š™™™™™¹?š™™™™™¹?333333Ó¿š™™™™™¹¿š™™™™™¹¿à¿333333Ó¿333333㿚™™™™™¹?š™™™™™Ù¿ð¿š™™™™™¹?ffffffæ¿333333ã?š™™™™™¹¿à¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿333333Ó¿š™™™™™¹¿ø¿à¿š™™™™™¹¿š™™™™™É¿š™™™™™Àš™™™™™¹¿š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™Ù¿š™™™™™É¿š™™™™™É?𿚙™™™™¹?š™™™™™¹¿š™™™™™É¿333333Ó¿š™™™™™¹¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹?š™™™™™É?࿚™™™™™¹?࿚™™™™™¹¿ð¿š™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™Ù?š™™™™™é¿š™™™™™É?࿚™™™™™É¿š™™™™™Ù?š™™™™™¹?333333Ó¿š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™¹¿š™™™™™¹¿ffffffæ?š™™™™™É?š™™™™™Ù?š™™™™™¹?࿚™™™™™É?š™™™™™Ù?š™™™™™É?š™™™™™É¿š™™™™™É¿333333Ó?333333Ó¿333333Ó¿ð?š™™™™™Ù¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿š™™™™™Ù¿š™™™™™¹¿ÍÌÌÌÌÌì¿ð¿š™™™™™¹¿333333Ó¿š™™™™™¹¿š™™™™™¹¿à¿à¿ffffff濚™™™™™¹¿ÍÌÌÌÌÌì¿à¿š™™™™™Ù?333333ó¿ð¿333333Àš™™™™™¹¿333333Ó¿333333㿚™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿š™™™™™ñ?࿚™™™™™Ù¿š™™™™™É¿ffffffö¿333333Ó¿š™™™™™¹?š™™™™™Ù¿333333Ó¿ð¿333333ã¿333333Ó¿š™™™™™É?333333ã?à¿ÍÌÌÌÌÌì?à¿333333ã?š™™™™™¹¿ð¿š™™™™™é?š™™™™™É¿š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿š™™™™™¹?333333Ó?š™™™™™¹¿333333Ó¿š™™™™™é¿š™™™™™¹?ÍÌÌÌÌÌì¿333333Ó¿ffffffö¿š™™™™™¹?ffffffæ¿à¿ffffffæ¿333333Ó¿ffffffö¿š™™™™™¹?333333ã¿à¿š™™™™™É?š™™™™™É¿š™™™™™é¿š™™™™™¹?333333Ó¿ffffffæ¿ð¿ffffffæ¿ÍÌÌÌÌÌô¿š™™™™™¹?࿚™™™™™Ù¿š™™™™™é¿š™™™™™É?š™™™™™É¿š™™™™™¹¿š™™™™™¹?࿚™™™™™¹?333333Ó¿š™™™™™É¿š™™™™™É?š™™™™™Ù¿š™™™™™Ù¿š™™™™™¹¿š™™™™™Ù¿à¿š™™™™™É¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿333333Ó¿š™™™™™¹?à¿à¿à¿ffffffæ?ffffffæ¿333333Ó¿ÍÌÌÌÌÌô¿ffffffö?333333Ó?š™™™™™É¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿333333㿚™™™™™Ù¿š™™™™™É?š™™™™™Ù?š™™™™™¹?𿚙™™™™¹¿š™™™™™É¿333333㿚™™™™™¹?ÍÌÌÌÌÌì?š™™™™™Ù¿š™™™™™É¿à¿š™™™™™Ù?à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀš™™™™™É?333333Ó¿š™™™™™Ù¿ffffffæ¿ffffff濚™™™™™¹¿š™™™™™Ù¿333333ã?š™™™™™¹?š™™™™™É?š™™™™™¹¿š™™™™™Ù?š™™™™™Ù¿š™™™™™É¿š™™™™™¹¿š™™™™™É?š™™™™™¹?à?š™™™™™¹¿š™™™™™¹¿š™™™™™É?333333Ó¿ffffff濚™™™™™é¿š™™™™™¹¿ð¿š™™™™™¹¿à¿š™™™™™é¿š™™™™™Ù¿333333ã¿ÍÌÌÌÌÌô¿š™™™™™é¿š™™™™™¹¿à¿š™™™™™¹?š™™™™™¹?𿚙™™™™¹¿ø¿ffffffö¿333333Ó¿ffffff濚™™™™™É¿š™™™™™Ù¿333333㿚™™™™™¹?š™™™™™É?333333ó¿š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™É¿"ÀÍÌÌÌÌÌô¿š™™™™™É¿š™™™™™Ù?š™™™™™Ù¿333333Ó¿ffffffö¿333333㿚™™™™™É?ffffff濚™™™™™¹¿š™™™™™É¿š™™™™™Ù?š™™™™™É¿333333ã?333333Ó?š™™™™™É¿š™™™™™¹¿š™™™™™É?333333㿚™™™™™¹¿ffffffæ¿333333Ó¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿333333Ó¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹?š™™™™™É¿š™™™™™Ù¿à¿š™™™™™É¿333333㿚™™™™™Ù?š™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™É¿ð?333333Ó¿333333ã?333333Ó¿š™™™™™É?š™™™™™É?š™™™™™¹¿333333Ó¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿à¿š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™¹?š™™™™™Ù¿š™™™™™Ù¿à¿š™™™™™Ù¿333333ã¿à¿à¿š™™™™™é¿333333㿚™™™™™Ù¿š™™™™™Ù¿š™™™™™Ù¿à¿333333ã¿ffffffæ¿à¿š™™™™™Ù¿ÍÌÌÌÌÌÀ333333㿚™™™™™Ù¿ÍÌÌÌÌÌì¿ffffffæ¿ffffff濚™™™™™é¿š™™™™™é¿š™™™™™Àà¿ð¿š™™™™™Ù¿š™™™™™Ù¿ffffffæ¿333333㿚™™™™™Ù¿ÍÌÌÌÌÌô¿ð¿š™™™™™Ù¿š™™™™™Ù¿333333ã¿333333ã¿ÍÌÌÌÌÌì¿333333㿚™™™™™Ù¿š™™™™™é¿š™™™™™ñ¿ø¿š™™™™™Ù¿ffffffö¿333333ã¿ð¿š™™™™™é¿š™™™™™é¿à¿à¿ÍÌÌÌÌÌÀ࿚™™™™™é¿š™™™™™Ù¿ffffff濚™™™™™Ù¿ffffffæ¿à¿ffffffæ¿333333㿚™™™™™é¿š™™™™™é¿ÍÌÌÌÌÌü¿ø¿š™™™™™Ù¿à¿š™™™™™Ù¿333333ã¿à¿333333ã¿ffffffæ¿ffffff濚™™™™™Ù¿š™™™™™é¿š™™™™™ñ¿ð¿333333ã¿ÍÌÌÌÌÌÀ࿚™™™™™Ù¿š™™™™™Ù¿ffffffæ¿333333㿚™™™™™Ù¿ð¿à¿333333㿚™™™™™Ù¿Àffffffæ¿ÍÌÌÌÌÌì¿333333ã¿ffffffÀš™™™™™Ù¿š™™™™™Ù¿333333ã¿ffffffæ¿ÍÌÌÌÌÌü¿ð¿š™™™™™ñ¿à¿š™™™™™Ù¿à¿à¿ffffffæ¿à¿à¿ÍÌÌÌÌÌì¿333333ã¿à¿š™™™™™Ù¿333333ã¿à¿ffffffæ¿333333ã¿333333㿚™™™™™Ù¿ffffff@ÍÌÌÌÌÌ@@333333@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌü?ffffff@ffffff@ @333333@ffffff@333333@š™™™™™ @ffffff$@ffffff@š™™™™™@š™™™™™ @333333@#@333333@@ÍÌÌÌÌÌ@š™™™™™@ffffff@ @ffffffþ?333333@ÍÌÌÌÌÌ$@ffffff)@333333@ffffff@333333@ÍÌÌÌÌÌ"@ffffff@@ÍÌÌÌÌÌ @ffffff@'@333333!@ @@333333@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@ @@ÍÌÌÌÌÌ @š™™™™™&@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff!@ffffff@333333@š™™™™™#@ffffff@@ffffff@ @333333@@ffffff@ÍÌÌÌÌÌ @@333333@333333@š™™™™™@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ @ffffff@š™™™™™ @ffffff%@ÍÌÌÌÌÌ @333333 @ffffff@ @š™™™™™@333333@333333@š™™™™™@š™™™™™ @š™™™™™ @ÍÌÌÌÌÌ @333333@š™™™™™@333333û?@@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ!@@ffffff@š™™™™™@š™™™™™@ @333333$@ @š™™™™™@š™™™™™!@333333@ffffff&@š™™™™™ @ @333333 @ffffff @@š™™™™™@ffffff@š™™™™™@š™™™™™@@š™™™™™@333333!@@ @333333%@333333 @š™™™™™@@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@@333333@ÍÌÌÌÌÌ)@ÍÌÌÌÌÌ@@@@ÍÌÌÌÌÌ @333333@ffffff@ÍÌÌÌÌÌ @ffffff @ÍÌÌÌÌÌ@@ffffff(@ffffff@ffffff@ffffff@ffffff@333333@ÍÌÌÌÌÌ@333333@333333@š™™™™™@@333333@š™™™™™ @333333@ÍÌÌÌÌÌ@š™™™™™@@@333333@ffffff@ÍÌÌÌÌÌ$@š™™™™™@@ffffff@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@333333@ÍÌÌÌÌÌ @ffffff@ffffff@333333%@ffffff@@š™™™™™@š™™™™™@333333-@333333"@ffffff@@š™™™™™@333333@333333@333333@333333 @ffffff@ÍÌÌÌÌÌ@$@š™™™™™@ÍÌÌÌÌÌ@333333#@ @333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌô?,@ffffff @@ÍÌÌÌÌÌ/@@333333û?@š™™™™™@@š™™™™™@@š™™™™™@ÍÌÌÌÌÌô?š™™™™™@ffffff@ffffff @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™@@@ÍÌÌÌÌÌ @ffffff@ÍÌÌÌÌÌ@333333@š™™™™™@333333 @@ffffff@ÍÌÌÌÌÌ@!@333333 @ffffff @ffffff@333333#@š™™™™™@ÍÌÌÌÌÌ @@ @333333 @š™™™™™&@ffffff@333333@ffffff"@ffffff@@333333 @@š™™™™™#@š™™™™™@š™™™™™ @š™™™™™@š™™™™™ @ffffff@š™™™™™$@š™™™™™@š™™™™™@ffffff&@š™™™™™@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ'@š™™™™™@333333%@@ffffff@ffffff@ffffff@@@333333@š™™™™™@š™™™™™@ffffff@ffffff@ffffff!@ffffff@š™™™™™,@ÍÌÌÌÌÌ@333333@333333@š™™™™™!@ÍÌÌÌÌÌ@ffffff@333333@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@333333 @š™™™™™@333333$@333333@@ÍÌÌÌÌÌ@š™™™™™@ffffff%@ffffff-@333333@ÍÌÌÌÌÌ!@š™™™™™'@ffffff @"@š™™™™™@333333!@333333@ÍÌÌÌÌÌ@333333@#@ffffff @ffffff@ffffff@ÍÌÌÌÌÌ(@@333333 @ffffff@333333@ÍÌÌÌÌÌô?š™™™™™@333333@333333#@š™™™™™ @@333333@š™™™™1@š™™™™™ @ffffff@333333@ÍÌÌÌÌÌ#@333333@@ffffff@@333333@@š™™™™™@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@š™™™™™@ÍÌÌÌÌÌ@@333333ã?@@@!@š™™™™™@ffffff@333333@333333!@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ)@š™™™™™!@š™™™™™@š™™™™™!@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ&@333333@š™™™™™@@š™™™™™@333333@ÍÌÌÌÌÌ @ffffff@$@ÍÌÌÌÌÌ@ffffff"@š™™™™™@ffffff@333333@333333@333333 @ffffff@@333333%@š™™™™™@ÍÌÌÌÌÌ"@š™™™™™ù?ffffff$@fffffæ3@333333)@ffffff@š™™™™™@333333@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™@š™™™™™ @ @@š™™™™™@š™™™™™@ÍÌÌÌÌÌ'@š™™™™™ @š™™™™™@333333û?ÍÌÌÌÌÌ@ÍÌÌÌÌL2@ffffff @333333#@ffffff!@ÍÌÌÌÌÌ$@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @333333@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@333333@333333@ffffff@333333@ffffff@š™™™™™ @333333@š™™™™™@@š™™™™™@ÍÌÌÌÌÌ@ffffff@333333@@@ffffff@ffffff@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @@@333333@š™™™™™@ÍÌÌÌÌÌ@ffffff@ffffff@ffffff@ÍÌÌÌÌÌ@ffffff @ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@š™™™™™$@ð¿ffffffÀffffffæ¿ffffffö¿š™™™™™Àš™™™™™ñ¿Àffffffþ¿333333û¿š™™™™™é¿333333ã¿ÍÌÌÌÌÌì¿ffffffö¿333333ã¿ffffffö¿š™™™™™é¿š™™™™™Ù¿ffffffæ¿ffffff Àš™™™™™ñ¿ð¿ÍÌÌÌÌÌ쿚™™™™™ù¿ÍÌÌÌÌÌü¿333333ó¿ø¿ffffffö¿ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿ffffffÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ffffffæ¿ffffffÀÀÍÌÌÌÌÌü¿Àš™™™™™ù¿š™™™™™ñ¿š™™™™™ù¿333333㿚™™™™™ù¿333333ó¿ ÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ÀÍÌÌÌÌÌô¿š™™™™™ñ¿ð¿333333û¿ð¿333333ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌ À333333ã¿ð¿š™™™™™ñ¿ffffffÀš™™™™™é¿à¿333333㿚™™™™™ù¿333333û¿ø¿ÍÌÌÌÌÌ쿚™™™™™ñ¿š™™™™™Ù¿š™™™™™ù¿š™™™™™ñ¿ À333333ã¿ffffffö¿à¿š™™™™™ñ¿ffffffæ¿ÍÌÌÌÌÌô¿ffffff'À333333ã¿ø¿š™™™™™ÀffffffÀš™™™™™Ù¿ÀÍÌÌÌÌÌÀ333333û¿ÍÌÌÌÌÌì¿à¿ÍÌÌÌÌÌÀ333333û¿š™™™™™é¿š™™™™™ñ¿ÍÌÌÌÌÌü¿ffffffæ¿à¿ÍÌÌÌÌÌÀð¿ÍÌÌÌÌÌü¿Àffffffö¿À333333ó¿š™™™™™Àš™™™™™ñ¿à¿333333ã¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀš™™™™™ù¿333333ó¿ÍÌÌÌÌÌì¿À333333Àø¿ø¿333333ó¿š™™™™™é¿ÍÌÌÌÌÌü¿Àà¿333333ó¿ð¿333333ã¿333333Àffffff濚™™™™™é¿ffffffæ¿333333ã¿ffffffÀš™™™™™é¿ffffffö¿333333û¿ffffffÀš™™™™™Àð¿333333û¿333333㿚™™™™™Àš™™™™™ñ¿333333ã¿ffffffö¿333333ó¿ÍÌÌÌÌÌÀffffffþ¿ÍÌÌÌÌÌì¿ffffffÀÀffffffþ¿š™™™™™Ù¿ø¿333333ÀÀÍÌÌÌÌÌô¿ø¿ÍÌÌÌÌÌü¿à¿š™™™™™Ù¿333333ó¿š™™™™™ù¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌü¿333333ã¿ð¿š™™™™™é¿333333û¿333333û¿333333ó¿333333À333333ó¿ffffff À333333ã¿à¿š™™™™™ÀÀÍÌÌÌÌÌô¿š™™™™™ñ¿ À ÀÍÌÌÌÌÌô¿š™™™™™Ù¿ÍÌÌÌÌÌÀ333333û¿ffffffÀffffffÀ333333À333333㿚™™™™™ñ¿š™™™™™Ù¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿Àš™™™™™ù¿ð¿ÍÌÌÌÌÌô¿š™™™™™ñ¿ð¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ø¿ð¿š™™™™™ù¿š™™™™™Àš™™™™™ Àffffffþ¿À333333Àš™™™™™é¿333333 Àš™™™™™é¿ffffffæ¿ø¿ffffffö¿š™™™™™Ù¿ffffffæ¿333333ÀffffffÀ333333û¿333333㿚™™™™™é¿333333ÀÍÌÌÌÌÌô¿ffffffö¿ffffffþ¿ÍÌÌÌÌÌô¿À Àš™™™™™ù¿š™™™™™é¿333333㿚™™™™™+À333333ã¿ffffffÀÍÌÌÌÌÌ쿚™™™™™ ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿Àš™™™™™Ù¿ffffff ÀÍÌÌÌÌÌÀš™™™™™ À333333ó¿š™™™™™é¿333333ã¿333333û¿ÍÌÌÌÌÌÀ333333㿚™™™™™é¿š™™™™™ñ¿333333û¿š™™™™™é¿ffffffö¿à¿š™™™™™ù¿333333À333333û¿ffffffþ?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™Ù?333333Ó?š™™™™™ñ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™É?š™™™™™é?š™™™™™Ù?š™™™™™Ù?š™™™™™é?š™™™™™é?š™™™™™é?š™™™™™Ù?333333ã?333333Ó?š™™™™™Ù?š™™™™™É?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?à?š™™™™™é?333333ã?š™™™™™É¿333333Ó¿š™™™™™é?ffffffæ?ÍÌÌÌÌÌì?333333Ó?333333ã?à?š™™™™™¹¿ffffffæ?ffffffæ?š™™™™™Ù?ffffffö?š™™™™™ù?333333ó?š™™™™™Ù?333333ã?à?š™™™™™é?333333ã?š™™™™™Ù?š™™™™™ñ?ffffffæ?à?333333Ó?ð?ffffffæ?š™™™™™é?š™™™™™é?š™™™™™Ù?š™™™™™É¿ffffffæ?333333ã?š™™™™™é?333333ã?š™™™™™Ù¿ð?š™™™™™é?š™™™™™é?š™™™™™Ù?ÍÌÌÌÌÌì?333333ã?333333ã?š™™™™™é?š™™™™™é?š™™™™™é?š™™™™™é?š™™™™™é?š™™™™™é?š™™™™™ù?ffffffæ?333333ã?ffffffæ?ffffffæ?à?333333ã?333333ã?š™™™™™é?š™™™™™É?ffffffæ?š™™™™™É?333333Ó?à?š™™™™™é?š™™™™™¹¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333Ó?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌì?ð?333333ã?ffffffæ?š™™™™™é?333333ã?333333Ó?ð?ð?à?ffffffæ?333333ã?ÍÌÌÌÌÌì?ð?š™™™™™Ù?à?ÍÌÌÌÌÌì?333333ã?ffffffæ?333333Ó?š™™™™™Ù?333333ã?à?ÍÌÌÌÌÌì?࿚™™™™™Ù?š™™™™™é?333333ó?ffffffæ?ffffffæ?ð?333333Ó?š™™™™™ñ?š™™™™™Ù?à?ffffffæ?à?š™™™™™ñ?š™™™™™é?š™™™™™É?š™™™™™é?ffffffæ?ø?š™™™™™é?š™™™™™é?333333ã?à?333333ã?š™™™™™é?ffffffæ?333333Ó?333333ã?ffffffæ?333333ã?à?à?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌì?ffffffæ?333333Ó?ffffffæ?ÍÌÌÌÌÌì?333333ó?š™™™™™é?à?š™™™™™Ù?à?ÍÌÌÌÌÌì?ð?š™™™™™ñ?š™™™™™é?à?š™™™™™Ù?à?333333Ó?ÍÌÌÌÌÌì?333333ã?ffffffæ?ÍÌÌÌÌÌì?à?ð?333333ã?ÍÌÌÌÌÌì?à?333333ó?š™™™™™é?š™™™™™Ù?à?š™™™™™é?à?à?š™™™™™Ù?ÍÌÌÌÌÌì?333333Ó?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™Ù?à?333333ó?à?š™™™™™é?ð?ÍÌÌÌÌÌì?333333ã?š™™™™™É¿333333ã?à?à?ø?333333ã?ÍÌÌÌÌÌì?333333ã?š™™™™™ñ?333333ã?š™™™™™é?š™™™™™é?š™™™™™Ù?š™™™™™É¿ð?š™™™™™Ù?š™™™™™é?333333Ó?à?333333ã?ffffffæ?ffffffæ?ð?ð?333333Ó?ð?ð?333333ã?333333Ó?ffffffæ?333333ã?š™™™™™Ù?à?ÍÌÌÌÌÌì?ð?ffffffæ?333333ó?ÍÌÌÌÌÌì?š™™™™™É?š™™™™™¹¿š™™™™™Ù?ð?š™™™™™Ù?š™™™™™é?à?à?š™™™™™É¿š™™™™™Ù?ffffffæ?š™™™™™é?š™™™™™é?ð?ffffffæ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333û?333333ã?ffffffæ?š™™™™™Ù?š™™™™™é?à?333333ã?ð?š™™™™™é?ffffffæ?š™™™™™é?ð¿333333ã?š™™™™™É?333333ã?ffffffæ?š™™™™™ñ?333333ã?à?333333Ó¿ð?ffffffæ?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?333333ã?ffffffæ?à?š™™™™™Ù?š™™™™™é?ffffffæ?ÍÌÌÌÌÌì?333333ã?š™™™™™é?ffffffæ?à?š™™™™™Ù?ÍÌÌÌÌÌô?š™™™™™É¿š™™™™™é?333333ã?ÍÌÌÌÌÌô?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™É?š™™™™™Ù?š™™™™™¹¿ÍÌÌÌÌÌô?š™™™™™ñ?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?333333ã?ÍÌÌÌÌÌì?333333ã?š™™™™™ñ?ð?ð?333333ã?333333ã?à?333333Ó?à?š™™™™™Ù?ð?š™™™™™Ù?ð?ffffffæ?333333Ó?333333Ó?š™™™™™Ù?š™™™™™Ù?ffffffæ?333333ã?š™™™™™Ù?333333ã?333333Ó¿ð?š™™™™™é?à?ffffffæ?š™™™™™Ù?333333ã?ÍÌÌÌÌÌì?ffffffö?ð?à?333333Ó?š™™™™™É?333333ó?š™™™™™é?ffffffæ?333333Ó?à?ÍÌÌÌÌÌô?ffffffö?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™é?à?ffffffæ?š™™™™™É?š™™™™™É?ffffffæ?š™™™™™é?333333ã?ffffffæ?ffffffæ?333333ã?333333ã?à?š™™™™™é?ð?š™™™™™é?ð?š™™™™™é?à?333333ó?š™™™™™Ù?ð?š™™™™™¹¿333333ó?ø?ffffffæ?à?š™™™™™é?333333ã?ffffffæ?333333Ó?à?ÍÌÌÌÌÌì?333333ã?ð?š™™™™™é?ÍÌÌÌÌÌì?ffffffæ?š™™™™™é?ø¿à?š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌô?ffffffæ?333333ã?333333ã?š™™™™™É?à?ÍÌÌÌÌÌì?š™™™™™Ù?à?333333ã?à?š™™™™™é?š™™™™™é?š™™™™™é?333333Ó?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™ñ?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?ð?š™™™™™ñ?à?à?ÍÌÌÌÌÌì?š™™™™™¹¿à?à?ÍÌÌÌÌÌì?š™™™™™é?à?š™™™™™Ù?ffffffæ?š™™™™™é?ffffffæ?ÍÌÌÌÌÌì?333333ã?š™™™™™Ù?š™™™™™é?ffffffæ?ÍÌÌÌÌÌô?ffffffæ?333333ã?ÍÌÌÌÌÌ@ø?@ffffffþ?ffffff@333333û?ð?ð?ð?ÍÌÌÌÌÌô?š™™™™™Ù?š™™™™™ù?ffffffæ?š™™™™™ñ?ffffff@333333û?ffffffæ?à?ð?333333û?@333333Ó?ð?ð?ffffffþ?ø¿ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?ffffffö¿333333û¿333333ó?ð?333333@ð?ffffffæ?ð?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@š™™™™™ @š™™™™™ù?ø?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?š™™™™™ñ?ffffffö?š™™™™™é?š™™™™™ù?š™™™™™é?š™™™™™Ù?š™™™™™Ù?š™™™™™é?ffffffö?333333ó?ø?š™™™™™¹?@š™™™™™ù?ð?š™™™™™Ù?ffffffæ?ð?š™™™™™@333333ó?333333û?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?à¿ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™ñ?333333@333333@š™™™™™ñ?333333û?ÍÌÌÌÌÌ@š™™™™™ù?ÍÌÌÌÌÌô¿333333ã?ÍÌÌÌÌÌì?š™™™™™é?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?š™™™™™Ù?š™™™™™¹?333333Ó?š™™™™™ñ¿333333û¿333333ã?ÍÌÌÌÌÌô¿ÍÌÌÌÌÌü¿ø?333333ó?ffffff@ð?ø?333333ó?333333û?š™™™™™@ffffffþ?ð¿333333û?à?š™™™™™Ù?@ÍÌÌÌÌÌô?à?333333Ó?ÍÌÌÌÌÌ@333333Ó?ð¿ÍÌÌÌÌÌü?ffffffö?ð?333333 @@š™™™™™@@@ffffffþ?à?ÍÌÌÌÌÌì?š™™™™™é?ffffffö?ÍÌÌÌÌÌì?333333ã?š™™™™™@ffffffæ?ÍÌÌÌÌÌì?ffffffæ?š™™™™™é?š™™™™™é?ffffffæ?ÍÌÌÌÌÌô?@ffffffþ?333333ó?333333û?333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?ø?š™™™™™ñ?ø?333333@@š™™™™™@@333333@333333ó?š™™™™™ù?š™™™™™é?ÍÌÌÌÌÌ@333333@@š™™™™™ù?š™™™™™@š™™™™™ù?ffffffæ?333333û?š™™™™™ñ?@š™™™™™ñ?ø?ffffffþ?ÍÌÌÌÌÌô?š™™™™™Ù?333333ó?ffffff@š™™™™™ñ?ffffff濚™™™™™Ù?ÍÌÌÌÌÌì?333333ó?333333ã?à?š™™™™™ù?ÍÌÌÌÌÌô?@ÍÌÌÌÌÌü?à¿ffffffö?ffffffö?ð¿333333Ó?ffffffö¿333333ã¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌì¿333333㿚™™™™™À@š™™™™™é?à?š™™™™™é?š™™™™™ù?ÍÌÌÌÌÌô?333333ó?ø?ÍÌÌÌÌÌ@š™™™™™@š™™™™™Ù?ÍÌÌÌÌÌì?333333û¿ÍÌÌÌÌÌ@š™™™™™é¿ÍÌÌÌÌÌô?333333û?ffffffö¿333333@333333Ó?š™™™™™Ù?š™™™™™ñ?š™™™™™É¿333333û?š™™™™™é?@ffffffö¿š™™™™™ù?š™™™™™Ù¿š™™™™™é?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?333333ã?333333ã?š™™™™™ù?ffffffö?@š™™™™™é?ffffffæ?ð¿ffffffþ?ffffff濚™™™™™ù?ø¿ø?ÍÌÌÌÌÌì¿à¿š™™™™™@š™™™™™é?333333û¿333333@333333Ó?ÍÌÌÌÌÌü?333333ã¿ÍÌÌÌÌÌô?ffffffæ?à?š™™™™™ù?ø?š™™™™™é?ffffff濚™™™™™ñ¿ÍÌÌÌÌÌì?š™™™™™@ÍÌÌÌÌÌÀ࿚™™™™™é?333333Ó?333333ó?333333@333333@333333ã?š™™™™™ù?š™™™™™É?ffffffö?@š™™™™™@ÍÌÌÌÌÌ@333333ó?ffffffþ?ÍÌÌÌÌÌì?ffffffæ?ffffffö?333333ã?@š™™™™™é?333333û?š™™™™™@š™™™™™¹?ÍÌÌÌÌÌ@ffffffö?ÍÌÌÌÌÌô?ø?333333Ó¿ffffffö?333333ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?333333Ó?ÍÌÌÌÌÌô?333333ó?š™™™™™ @š™™™™™é¿š™™™™™ù?š™™™™™Àš™™™™™@ffffffþ?@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ð?ffffffæ¿333333ã¿ffffff@ffffffæ?333333û?ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô?ffffff@š™™™™™ù?ffffffæ?ÍÌÌÌÌÌô?ffffffþ?à?ÍÌÌÌÌÌü?ø?š™™™™™É¿333333Ó?333333û?ffffffæ¿ffffffþ¿ð?333333ó?ffffffæ?333333ã¿ÍÌÌÌÌÌô?ffffffö¿@ÍÌÌÌÌÌì?ffffffþ?ffffffþ?333333ó?ø?ffffffæ?ð?ffffffþ?š™™™™™¹?à?ø?333333ó?ffffffæ?ffffff@@ffffffþ?ÍÌÌÌÌÌô?à?à?š™™™™™ñ?333333ã¿ð¿333333ã?ffffff Àš™™™™™É?š™™™™™é¿ffffff濚™™™™™ñ?š™™™™™Àš™™™™™É¿333333Ó¿ÍÌÌÌÌÌì?333333ã?š™™™™™ù?š™™™™™@ÍÌÌÌÌÌô¿š™™™™™Ù?š™™™™™É¿ø?ffffffþ¿333333ã?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì?ffffff@š™™™™™É?š™™™™™é¿š™™™™™ñ?333333û?š™™™™™@࿚™™™™™ñ?ð?ÍÌÌÌÌÌ@š™™™™™@333333û?š™™™™™É?ffffffþ?ffffff!ÀÍÌÌÌÌÌô¿ffffffæ?š™™™™™@ffffffö?š™™™™™ù?š™™™™™É¿ÍÌÌÌÌÌì¿333333û?333333ó?333333Ó?ÍÌÌÌÌÌô?š™™™™™@ÍÌÌÌÌÌ@333333Ó¿š™™™™™ @š™™™™™@333333ó?333333Ó?ÍÌÌÌÌÌ@ð¿ÍÌÌÌÌÌô?à?ÍÌÌÌÌÌü?333333Ó?ffffffþ?à?333333ã?333333ã?333333Ó¿ffffffö?ffffffþ?ø¿ÍÌÌÌÌÌì?š™™™™™ù?333333ó?ð?333333@ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™ñ?@ÍÌÌÌÌÌì?@333333Ó¿ffffffþ?š™™™™™ñ?š™™™™™ù?ÍÌÌÌÌÌô?š™™™™™Ù¿ÍÌÌÌÌÌô?ø?ffffffö?š™™™™™ù?š™™™™™é?š™™™™™@ffffffæ?ffffffþ?š™™™™™é?š™™™™™ñ?ÍÌÌÌÌÌ@š™™™™™@š™™™™™Ù?333333+@3333331@š™™™™™ÀÍÌÌÌÌL;@333333#@%À333333@ffffff@ffffff@š™™™™™ @š™™™™™@ÍÌÌÌÌÌ#@š™™™™™6@ffffff-@ffffffD@ÍÌÌÌÌÌ*@ÍÌÌÌÌÌ@ffffff@@333333:@ffffff@ffffff@333333&@3333335@ffffff2@ffffff?@š™™™™™ù?ÍÌÌÌÌÌ2@33333³B@ÍÌÌÌÌ C@33333³3@ffffff2@š™™™™™,@>@@š™™™™™ @@ffffff$@ffffff?@ÍÌÌÌÌÌ9@@333333 @333333*@33333³5@3333335@7@š™™™™™(@2@ÍÌÌÌÌÌ@š™™™™™?@fffffæ3@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ1@fffffæ4@!@3333335@š™™™™7@2@333333(@ffffff@ffffff'Àffffff&@ffffff@š™™™™™%@333333%@ffffff!@š™™™™™$@ffffff)@333333,@333333$@33333³3@1@ffffff"@€2@ffffff(@š™™™™™¹?ffffff"ÀÍÌÌÌÌÌ#@ÍÌÌÌÌÌü?š™™™™™&@ÍÌÌÌÌÌ*@ÍÌÌÌÌÌ"@ffffff*@š™™™™™ @š™™™™™ @3333339@ffffff1@$@%@€F@š™™™™™!@š™™™™™5@@š™™™™™4@333333ó¿333333@ffffff'@ffffff5@ @fffff&B@€0@ffffff*@ffffff#Àš™™™™™8@333333%@š™™™™™2@š™™™™™*@š™™™™™.@333333ó¿3333336@š™™™™™3@š™™™™™@#@333333#@š™™™™™&@@ffffff%@ÍÌÌÌÌÌ&@333333"@ÍÌÌÌÌÌ0@€;@š™™™™™@ÍÌÌÌÌÌ2@ffffff@@ffffff'@ffffff-@.@ffffff(@ÍÌÌÌÌLE@ÍÌÌÌÌÌü?-@ffffff$@ø?@š™™™™™ @ÍÌÌÌÌÌ0@@š™™™™™)@ÍÌÌÌÌÌ*@ffffff"@ffffffæ?fffffæ2@ffffff,@@333333@ÍÌÌÌÌÌì?fffffæ4@ffffff@333333@333333@(@@š™™™™™ @ffffff"@ffffff,@fffffæ1@š™™™™™ ÀffffffI@ffffff5@ffffff&@ÍÌÌÌÌÌD@ÍÌÌÌÌL2@ @ÍÌÌÌÌÌ'@ffffff'@ffffff@ÍÌÌÌÌÌ1@ffffff$@ffffff4@ÍÌÌÌÌÌ5@ÍÌÌÌÌÌ0@ÍÌÌÌÌÌ*@6@š™™™™™@2@ÍÌÌÌÌÌ#@ffffff @š™™™™™@@ffffff7@š™™™™™@3333338@ÍÌÌÌÌÌ&@&@3333334@š™™™™™@@@ffffff9@N@333333!@ÍÌÌÌÌÌ@@A@ÍÌÌÌÌL2@š™™™™0@š™™™™™ @š™™™™™Ù¿#@333333&Àš™™™™™G@ffffff.@€6@ÍÌÌÌÌŒG@š™™™™™,@š™™™™™ù¿@ÍÌÌÌÌÌ'@ÍÌÌÌÌÌG@š™™™™6@ffffff@333333@š™™™™™ Àffffff!@ffffff@@*@333333*@ÍÌÌÌÌÌ)@š™™™™™@@š™™™™™@)@ÍÌÌÌÌÌ @333333%@ÍÌÌÌÌÌ@333333@333333@ÍÌÌÌÌÌ6@3333331@+@fffffæ3@ÍÌÌÌÌÌ7@333333@fffffæ8@%@ÍÌÌÌÌÌ:@333333@@€;@33333³:@š™™™™™@ÍÌÌÌÌÌ5@'@333333@š™™™™™'@33333³0@4@fffffæ6@ÍÌÌÌÌÌ.@š™™™™™@ÍÌÌÌÌÌ'@š™™™™1@š™™™™™&@33333³<@š™™™™2@ÍÌÌÌÌÌ"@ÍÌÌÌÌL<@ffffff(@ffffff7@fffff¦@@ÍÌÌÌÌÌ@ffffff3@ÍÌÌÌÌÌ@(@@ffffffþ?ffffff"@$@"@@š™™™™™ @"@š™™™™™@ÍÌÌÌÌÌ/@š™™™™7@fffff&K@)@3333337@ @ffffff:@3333335@š™™™™™%@š™™™™™6@š™™™™7@@ÍÌÌÌÌÌ*@@ffffff$@ffffff5@fffffæ:@3@ÍÌÌÌÌÌ(@333333@ffffff)@€6@€G@À@B@š™™™™?@@ÍÌÌÌÌÌ8@š™™™™™3@fffffæ0@ffffff@@ÍÌÌÌÌÌ%@š™™™™Ù@@€=@@ÍÌÌÌÌL0@9@ÍÌÌÌÌÌ@333333ÀÍÌÌÌÌÌ/@š™™™™™@š™™™™1Àš™™™™™'@ffffff@ÍÌÌÌÌÌ@333333ã?š™™™™™)À33333³3@š™™™™™G@@*@ÍÌÌÌÌÌ/@€8@33333³5@ÍÌÌÌÌÌ@fffffæ6@333333'@ð¿-@ffffff,@ffffff'@š™™™™™À'@ffffff!@ÍÌÌÌÌÌ,@ð?333333@333333%@*@š™™™™™@š™™™™™@ÍÌÌÌÌÌ!@>@"Àš™™™™™,@š™™™™6@333333+@€3@ÍÌÌÌÌÌ3@ffffff@ÍÌÌÌÌÌô?ÍÌÌÌÌL@@fffffæ0@33333³4@š™™™™<@ffffff:@-@€;@333333"@š™™™™™.@@E@"@ÍÌÌÌÌÌ&@333333ó?ÍÌÌÌÌÌ!@fffff&D@@š™™™™™/@33333³6@š™™™™5@ÍÌÌÌÌÌ3@š™™™™™@333333 @333333(@ffffffÀ333333Àš™™™™™ñ¿š™™™™™1@š™™™™3@333333@š™™™™™E@š™™™™™É?š™™™™™1@ffffffc@fffff¦F@š™™™™™@š™™™™™@ffffff+@ @š™™™™™é?333333?@(@š™™™™™À333333;@š™™™™™!@ffffff3@333333@ÍÌÌÌÌL@@fffff¦@Àš™™™™™/@ffffff@ÍÌÌÌÌÌ*@š™™™™™I@ÍÌÌÌÌÌ@ÍÌÌÌÌL@@š™™™™8@333333<@333333+@@$@ffffff@ @@333333&@ÍÌÌÌÌÌ-@š™™™™™(@33333³6@ffffff'@ffffff0@ÍÌÌÌÌÌ3@š™™™™7@,@ÍÌÌÌÌÌÀffffff*@ÍÌÌÌÌÌô¿ÀÍÌÌÌÌÌ!@ÍÌÌÌÌÌ"@ÍÌÌÌÌÌ$@:@ÍÌÌÌÌÌ.@ÍÌÌÌÌL0@ffffff@ffffff:@333333À333333"@!@ffffff'@ffffff@š™™™™™)@ffffffö?333333-@/@š™™™™™@š™™™™™@-@ÍÌÌÌÌL4@ÍÌÌÌÌÌ>@€:@ÍÌÌÌÌÌ3@ÍÌÌÌÌŒF@š™™™™™?@ffffff@%@ffffff+@333333'@+@ÍÌÌÌÌÌ @33333³6@fffffæ:@fffffæ5@š™™™™M@fffffæ9@ÍÌÌÌÌÌ%@%@333333'@33333³C@ÍÌÌÌÌŒB@333333@ÍÌÌÌÌL1@333333;@33333³?@š™™™™0@ffffff%@š™™™™™@@3333335@š™™™™™(@333333>@ÍÌÌÌÌÌ8@€@@fffffæA@"@333333.@ÍÌÌÌÌL;@ÍÌÌÌÌ C@€N@B@ÍÌÌÌÌÌ0@*@š™™™™™5@ÍÌÌÌÌÌ=@ÍÌÌÌÌÌ?@š™™™™;@fffffæ5@3333337@ÍÌÌÌÌÌ!@À@@9@4@333333:@€>@ffffff"@ffffffE@ÀA@ffffffA@ffffff.@ffffff!@ffffffÀ33333³?@ffffff*@ffffff8@fffffæ7@ÍÌÌÌÌÌ/@333333@ÍÌÌÌÌÌ;@6@fffffæ0@ffffffD@š™™™™B@š™™™™™0@33333³>@š™™™™<@ÍÌÌÌÌÌ)@2ÀÍÌÌÌÌÌ+@333333"@3333330@ÍÌÌÌÌL3@š™™™™8@ÍÌÌÌÌÌ/@333333@@š™™™™™.@ÍÌÌÌÌÌ@š™™™™™2@ffffff"Àš™™™™7@3333334@ÍÌÌÌÌL;@ÍÌÌÌÌL9@333333:@333333&@ÍÌÌÌÌÌ0@fffffæ8@š™™™™YG@3333337@fffffæ;@ÍÌÌÌÌÌ?@š™™™™™0@ffffffÀÍÌÌÌÌŒG@fffffæ2@3333335@ffffff,@ÀA@š™™™™™@(@33333³>@š™™™™3@ÍÌÌÌÌL1@33333³B@ÍÌÌÌÌL7@ÍÌÌÌÌL7@€7@ffffff>@€7@ÍÌÌÌÌL4@š™™™™YA@ffffff(@333333>@š™™™™™*@333333@€?@2@€3@ffffff0@ÀG@333333@š™™™™2@3@ÍÌÌÌÌÌ/@€4@š™™™™™*@ÍÌÌÌÌL=@ffffff@ÍÌÌÌÌL2@fffffæ4@fffffæ4@333333%@333333?@333333@@š™™™™™3@ffffff=@ffffff/@ÍÌÌÌÌ C@,@ffffff*@ @fffffæ;@3333339@€5@ffffff4@fffffæ>@33333³<@ÍÌÌÌÌÌô?ÍÌÌÌÌlP@fffffæ=@š™™™™™;@ffffffH@ÍÌÌÌÌL>@ÍÌÌÌÌL5@33333³5@,@ÍÌÌÌÌÌ,@333333E@š™™™™2@ffffff/@š™™™™™8@33333³6@6@ÍÌÌÌÌÌ9@ÍÌÌÌÌÌ@š™™™™™;@33333³2@ÍÌÌÌÌÌ3@fffff&F@ÍÌÌÌÌÌ2@)@33333³A@333333@ffffff(@š™™™™™@š™™™™™É¿333333%@ø?€5@š™™™™™:@š™™™™™8@333333'@fffff&B@<@fffffæ:@)@ÍÌÌÌÌÌ!@33333³3@ÍÌÌÌÌÌ-@33333óO@ÍÌÌÌÌL1@ÍÌÌÌÌL;@ÍÌÌÌÌL6@ÍÌÌÌÌ B@333333 Àš™™™™™-@€9@ÍÌÌÌÌÌ;@fffffæC@š™™™™™"@š™™™™™@333333@ @ÍÌÌÌÌL3@333333"@š™™™™™:@š™™™™™é?ÍÌÌÌÌÌ6@š™™™™™@ÍÌÌÌÌÌì?ffffff(@333333;@ÍÌÌÌÌL5@33333³4@ffffff@š™™™™™*@š™™™™™2@fffff&@@fffffæD@fffffæ1@€9@3333330@33333³7@š™™™™™6@3333339@ffffff @-@ffffffþ¿9@ÍÌÌÌÌÌF@ÍÌÌÌÌÌ%@ffffff@š™™™™E@333333@š™™™™9@ÍÌÌÌÌÌ$@ffffff>@fffffæ9@3333333@ÍÌÌÌÌÌ0@š™™™™6@š™™™™™;@333333@š™™™™3@ÍÌÌÌÌL7@;@ÍÌÌÌÌÌ@ffffffÀffffff-@€8@š™™™™YD@ffffff7@š™™™™ÙE@$@ÍÌÌÌÌÌ7@š™™™™™ @š™™™™™)@ffffff<@33333³:@ÍÌÌÌÌÌ?@š™™™™™-@33333³6@ffffff-@/@€8@333333:@33333“R@š™™™™™0@33333³A@š™™™™9@š™™™™;@fffff&B@33333³3@fffffæ?@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@8@š™™™™™/@ÍÌÌÌÌÌ3@ÍÌÌÌÌ B@33333³=@fffffæ=@ÍÌÌÌÌÌ4@33333óA@333333û?€@@ÍÌÌÌÌÌ%@š™™™™:@fffffæH@33333óE@)@€E@333333=@š™™™™™%@333333ó?33333³9@ÍÌÌÌÌL1@@F@5@ÍÌÌÌÌÌ+@ÍÌÌÌÌŒA@ÀA@š™™™™™"@@<@333333%@À33333³5@333333@ÍÌÌÌÌÌ@333333ã? @ffffff-@š™™™™8@/@ffffff4@ffffff3@fffffæ2@fffff¦C@'À33333³D@33333³1@*@š™™™™<@33333³8@ÍÌÌÌÌÌ<@à¿fffffæ1@š™™™™™8@š™™™™™-@333333 @š™™™™™.@€2@2@333333:@ffffff4@fffffæ6@ÍÌÌÌÌLD@Àfffffæ0@š™™™™=@ÍÌÌÌÌÌ@š™™™™™(@š™™™™™;@ffffffCÀ@ffffff;@333333)@š™™™™™:@š™™™™™!@ÍÌÌÌÌL8@333333(@fffff&A@ÍÌÌÌÌÌ*@ÍÌÌÌÌÌ:@fffffP@333333Ó¿1@š™™™™™ñ¿š™™™™3@ffffff0@ffffff#@ÍÌÌÌÌÌ@>@ffffffD@š™™™™5@ffffffö¿š™™™™™/@8@š™™™™™@ffffff&ÀÍÌÌÌÌÌ#@7@š™™™™A@33333³3@ÍÌÌÌÌ L@ø?ffffff;@ÍÌÌÌÌ E@ÀA@š™™™™™)@33333³9@š™™™™™6@3333331@š™™™™™¹¿š™™™™™1@€8@š™™™™™@33333³=@š™™™™™1@fffffæB@š™™™™:@š™™™™>@ÍÌÌÌÌÌ%Àfffffæ@@š™™™™™)@š™™™™™/@R@š™™™™™ÀÍÌÌÌÌŒC@;@fffffæD@š™™™™™1@ÍÌÌÌÌÌ4@ffffff(@ffffff)@333333/@@š™™™™™4@33333³=@333333ã¿=@€6@ffffff;@333333?@fffff&E@ÍÌÌÌÌL7@à?ÍÌÌÌÌÌ(@š™™™™™@3333333@ffffff.@ffffff9@333333"@ffffffD@33333³7@€;@333333.@6@š™™™™™@ffffff5@š™™™™1@3333337@!@33333³A@333333@fffffæ;@š™™™™™4@š™™™™™'@ÍÌÌÌÌÌ4@33333³@@fffffæ5@š™™™™™é?š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™¹¿333333Ó¿333333ó?š™™™™™é¿š™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™É¿333333Ó?š™™™™™¹¿ffffffæ?࿚™™™™™É?š™™™™™¹¿š™™™™™Ù¿à?š™™™™™¹?š™™™™™É¿333333Ó¿š™™™™™Ù?š™™™™™Ù¿š™™™™™É¿333333Ó?š™™™™™¹?š™™™™™¹¿š™™™™™¹¿à¿š™™™™™Ù¿š™™™™™É¿š™™™™™É¿š™™™™™É?333333Ó¿ffffffæ?š™™™™™É¿š™™™™™É?333333Ó¿š™™™™™Ù¿š™™™™™¹¿ð¿à¿š™™™™™É?š™™™™™É?࿚™™™™™Ù¿š™™™™™É¿à¿333333Ó¿333333ã?š™™™™™¹¿š™™™™™¹¿š™™™™™¹?333333Ó¿š™™™™™É?333333Ó?š™™™™™É?333333ӿ࿚™™™™™Ù¿š™™™™™É?333333Ó¿333333û¿š™™™™™ù¿š™™™™™Ù?š™™™™™ù¿333333ó¿š™™™™™É¿333333Ó¿à?ffffff濚™™™™™É¿š™™™™™É¿š™™™™™É¿333333Ó¿š™™™™™Ù¿š™™™™™¹¿à¿š™™™™™¹?š™™™™™É¿333333Ó¿š™™™™™¹?ÍÌÌÌÌÌü¿š™™™™™É?š™™™™™¹¿š™™™™™¹¿ÍÌÌÌÌÌì?š™™™™™É?š™™™™™É¿š™™™™™¹?š™™™™™¹¿š™™™™™É?š™™™™™é¿à¿ð¿š™™™™™¹?ffffffæ?š™™™™™¹?333333Ó¿333333ã?š™™™™™¹¿à?š™™™™™Ù¿š™™™™™¹?š™™™™™¹¿ffffff濚™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™é?š™™™™™É¿š™™™™™¹?à?ffffffæ?š™™™™™¹¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™¹?š™™™™™É?࿚™™™™™É¿š™™™™™é¿333333㿚™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿à?š™™™™™É?š™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿š™™™™™É¿ÍÌÌÌÌÌ쿚™™™™™Ù?š™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌô¿à¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿š™™™™™¹?š™™™™™¹?333333Ó¿š™™™™™Ù¿š™™™™™¹?333333Ó?š™™™™™Ù¿333333ã¿333333Ó¿333333Ó¿š™™™™™¹?333333ã¿à¿à¿333333Ó¿š™™™™™É¿š™™™™™¹?š™™™™™É¿333333㿚™™™™™¹?š™™™™™Ù¿333333Ó?š™™™™™é¿ÍÌÌÌÌÌô¿ð¿š™™™™™¹?à?333333Ó¿ffffff濚™™™™™É?à?š™™™™™Ù¿333333ã¿à?333333㿚™™™™™¹¿š™™™™™Ù?š™™™™™É?š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿333333Ó?š™™™™™¹¿š™™™™™¹¿š™™™™™Ù¿š™™™™™¹?š™™™™™É¿ÍÌÌÌÌÌô¿ð¿š™™™™™¹¿ø¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™é¿š™™™™™É¿š™™™™™¹¿š™™™™™¹?333333㿚™™™™™Ù?š™™™™™¹?333333Ó¿ffffffæ?ffffffæ¿333333Ó?š™™™™™Ù?𿚙™™™™É?ffffff濚™™™™™Ù¿333333ã?š™™™™™é¿š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™É?š™™™™™É?333333Ó?333333Ó¿š™™™™™É¿ÍÌÌÌÌÌì¿à¿š™™™™™Ù?š™™™™™É¿š™™™™™Ù?š™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™é?š™™™™™¹¿š™™™™™¹¿š™™™™™É¿š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™¹¿š™™™™™Ù?ffffffæ¿ð¿ø?š™™™™™¹?š™™™™™ñ¿š™™™™™É?ffffff濚™™™™™¹?š™™™™™¹¿š™™™™™Ù?ffffffþ¿š™™™™™Ù?š™™™™™¹?à¿333333Ó¿š™™™™™Ù?333333Ó?à?š™™™™™¹?333333ã?ffffffæ?š™™™™™¹?š™™™™™¹?333333ã¿ffffffö¿ð?ffffffæ¿333333Ó?š™™™™™¹?ffffffö¿333333㿚™™™™™É¿333333Ó?à?š™™™™™Ù¿ø?333333Ó¿333333ӿ࿚™™™™™¹?š™™™™™É¿š™™™™™É¿à¿š™™™™™¹?š™™™™™¹¿à?š™™™™™É?š™™™™™É¿š™™™™™é?š™™™™™É¿à¿ffffff濚™™™™™Ù¿š™™™™™é¿ð?š™™™™™É?š™™™™™¹?ð¿ffffffæ¿ð¿ÍÌÌÌÌÌ쿚™™™™™Ù¿š™™™™™É¿à?š™™™™™¹?š™™™™™É?š™™™™™Ù?ð¿ð¿ÍÌÌÌÌÌì¿à¿à¿à¿333333Ó¿333333Ó¿333333ã?š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿ffffff濚™™™™™¹?š™™™™™¹?333333ã¿ffffff濚™™™™™¹¿333333Àffffffæ?š™™™™™É¿š™™™™™Ù?š™™™™™Ù¿à¿ø¿à¿š™™™™™¹?ffffffæ?š™™™™™Ù¿à?š™™™™™É?333333㿚™™™™™É?š™™™™™é?333333ã?š™™™™™¹?š™™™™™É¿š™™™™™¹¿ð¿š™™™™™¹¿333333㿚™™™™™É?š™™™™™¹¿à¿ÍÌÌÌÌÌì¿à¿à¿š™™™™™Ù?ffffff濚™™™™™Ù?333333Ó¿š™™™™™¹?ffffffþ?š™™™™™é?š™™™™™é¿š™™™™™É?š™™™™™é¿333333Ó¿333333ã¿ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™É?š™™™™™Ù¿š™™™™™é¿333333Ó¿š™™™™™¹¿333333Ó¿333333Ó¿š™™™™™É¿š™™™™™Ù¿333333Ó?à?š™™™™™¹?š™™™™™¹?š™™™™™é?ffffffö?š™™™™™é?š™™™™™Ù¿š™™™™™Ù¿333333ó¿ÍÌÌÌÌÌô¿à¿š™™™™™Ù¿333333 Àffffffö¿ffffff濚™™™™™é¿ø¿333333ó¿à¿š™™™™™é¿š™™™™™ñ¿333333ó¿ð¿š™™™™™é¿à¿ð¿ÍÌÌÌÌÌÀš™™™™™ÀÍÌÌÌÌÌ쿚™™™™™ñ¿š™™™™™é¿š™™™™™ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿š™™™™™Àð¿ffffff濚™™™™™Ù¿333333ã¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿333333ã¿ð¿š™™™™™Ù¿à¿333333û¿š™™™™™Ù¿š™™™™™ñ¿š™™™™™ù¿š™™™™™Àš™™™™™ñ¿ffffffÀš™™™™™Ù¿ffffffæ¿à¿Àffffffæ¿ffffffæ¿à¿š™™™™™ñ¿š™™™™™ñ¿ffffffæ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀ333333ã¿ÍÌÌÌÌÌô¿ð¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀà¿à¿ð¿ÍÌÌÌÌÌô¿š™™™™™ù¿333333ó¿š™™™™™é¿ÍÌÌÌÌÌü¿š™™™™™Ù¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ쿚™™™™™é¿à¿333333ó¿333333ã¿ð¿à¿à¿ð¿à¿ð¿ø¿ÍÌÌÌÌÌô¿333333ã¿333333ã¿333333À333333㿚™™™™™Ù¿š™™™™™é¿š™™™™™ù¿ffffff濚™™™™™Ù¿333333Àš™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™Ù¿š™™™™™Ù¿š™™™™™Àš™™™™™é¿à¿à¿333333û?@š™™™™™@333333"@š™™™™™ù?š™™™™3@ffffff@ÍÌÌÌÌÌ @š™™™™™@ÍÌÌÌÌÌ#@š™™™™™@@š™™™™™ @333333-@ÍÌÌÌÌÌ)@š™™™™™@š™™™™™#@@ffffff@333333 @333333@ @333333@ffffff@ffffff&@@ÍÌÌÌÌÌ"@š™™™™™#@333333@@ffffff@@š™™™™™"@@ÍÌÌÌÌÌ @š™™™™™@@ffffff@š™™™™™é?ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @š™™™™™0@ffffff@333333 @ÍÌÌÌÌÌ@š™™™™™@333333@ffffff@š™™™™™@@ffffff @ffffff1@@ffffff@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌ@333333@#@š™™™™™@š™™™™™!@š™™™™™@333333*@š™™™™™-@ffffff@ffffff@ffffff)@333333#@333333û?š™™™™™@ÍÌÌÌÌÌ@333333 @ffffff @!@ffffff&@š™™™™™ @333333@'@333333"@333333#@333333)@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™!@333333@3333333@333333@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@ffffff@333333@š™™™™™@333333'@ffffff@333333@ÍÌÌÌÌÌ%@š™™™™™@333333/@!@ÍÌÌÌÌÌ&@333333@333333@ffffff @ÍÌÌÌÌÌ@š™™™™™2@333333@ffffff@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ-@š™™™™™$@333333@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ#@333333!@ @ø?ffffff@333333@š™™™™™"@ÍÌÌÌÌÌ@š™™™™™ @@@#@ffffff$@"@!@ffffff@&@š™™™™™'@ÍÌÌÌÌÌ@'@333333@š™™™™™'@333333@ÍÌÌÌÌÌ @333333&@ffffff@ffffff@333333)@ÍÌÌÌÌÌ!@333333@!@@š™™™™™@@333333@ÍÌÌÌÌÌ!@333333@š™™™™™$@ffffff@ÍÌÌÌÌÌ @ @ÍÌÌÌÌÌ#@ffffff @ffffff @ffffff@333333&@333333"@ffffff@ffffff@333333@ @333333@ffffff@333333@ffffff%@@ffffff@ÍÌÌÌÌÌ#@š™™™™™#@@š™™™™™@ @š™™™™™#@ffffff@333333@ffffffæ?333333û?ffffff@333333@"@š™™™™™ @333333@@$@!@333333@ffffff+@@-@+@ÍÌÌÌÌÌ@@š™™™™™@š™™™™™@333333'@š™™™™™&@@š™™™™™@ÍÌÌÌÌL1@333333!@ffffff+@333333!@ffffff@ffffff0@@ffffff@ @ÍÌÌÌÌÌ@ffffffþ?333333@333333@ffffff@333333 @ffffff@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@@ÍÌÌÌÌÌ+@@š™™™™™ @ffffff"@ÍÌÌÌÌÌ*@ÍÌÌÌÌL0@333333@333333@ÍÌÌÌÌÌ@š™™™™™@333333(@333333@ffffff@š™™™™™@ÍÌÌÌÌÌ@ffffff @333333@ffffff'@ffffff%@@ÍÌÌÌÌÌ@š™™™™™@ffffff@š™™™™™Ù?š™™™™™@ÍÌÌÌÌÌ@š™™™™™@333333@333333@"@ffffff@ffffff@ÍÌÌÌÌÌ@333333"@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@ffffffþ?ffffff @š™™™™™ @333333@ffffff$@333333@š™™™™™@ffffff@@333333'@333333!@333333"@ @ffffff@ÍÌÌÌÌÌ@@š™™™™™@š™™™™™ @333333@ffffff)@ÍÌÌÌÌÌ@ffffff@@@š™™™™™/@š™™™™™@333333 @@fffffæ0@ÍÌÌÌÌÌ@ffffff@š™™™™™"@š™™™™™@ffffff@ÍÌÌÌÌÌ @@ÍÌÌÌÌÌô?ffffff@ffffff*@ÍÌÌÌÌÌ@@š™™™™™%@ÍÌÌÌÌÌ@@ffffff@š™™™™™@ø?ÍÌÌÌÌÌ@š™™™™™#@333333@š™™™™™@š™™™™™@@š™™™™™ @š™™™™™"@333333@ÍÌÌÌÌÌ@333333@333333@š™™™™™ @ÍÌÌÌÌÌì?ÍÌÌÌÌÌ @š™™™™™@333333@!@(@š™™™™™@ÍÌÌÌÌÌ@ffffff@š™™™™™@333333&@ÍÌÌÌÌÌ$@š™™™™™"@333333%@@ffffff@ffffff@!@ÍÌÌÌÌÌ @"@@333333@ffffff @ffffff @ @ÍÌÌÌÌÌ"@@ÍÌÌÌÌÌ@@@š™™™™™@333333@š™™™™™#@333333)@@@š™™™™™@@#@333333@š™™™™™-@333333@333333@ffffff@@š™™™™™#@ÍÌÌÌÌÌ@333333'@@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ffffff%@(@333333@%@%@@333333*@333333(@$@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌ!@ffffffæ?ÍÌÌÌÌÌ@ffffff$@ffffff#@-@ffffff@@ÍÌÌÌÌÌ@333333@ffffff@@@š™™™™™1@ÍÌÌÌÌÌ!@333333 @ffffff @ffffff @š™™™™™@ffffff-@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ$@333333@-@333333!@333333%@š™™™™™#@ÍÌÌÌÌÌ.@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™!@š™™™™™@ffffff)@š™™™™™@ÍÌÌÌÌÌ@333333"@@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ$@ffffff/@333333@333333@ÍÌÌÌÌÌì?@#@ÍÌÌÌÌÌ"@#@!@ffffff@333333%@ffffff!@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@@ffffff@ffffff@ffffffþ?%@š™™™™™ù?š™™™™™"@ÍÌÌÌÌÌ@ð¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀà¿ffffffÀš™™™™™ñ¿ø¿ffffff濚™™™™™é¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀš™™™™™Ù¿ÍÌÌÌÌÌü¿333333û¿š™™™™™ñ¿333333û¿ÍÌÌÌÌÌô¿š™™™™™ñ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀffffff濚™™™™™ Àffffffæ¿ÍÌÌÌÌÌÀš™™™™™é¿ø¿ÍÌÌÌÌÌô¿333333û¿ø¿333333ÀÍÌÌÌÌÌô¿š™™™™™ Àš™™™™™ù¿333333ÀÍÌÌÌÌÌÀ333333ó¿ÀÀš™™™™™ù¿ÍÌÌÌÌÌÀffffffÀffffff濚™™™™™ ÀÍÌÌÌÌÌÀÀš™™™™™Ù¿ÍÌÌÌÌÌÀÀš™™™™™Àffffffö¿ffffffÀÍÌÌÌÌÌÀš™™™™™Àð¿ffffffö¿ffffffÀÍÌÌÌÌÌü¿ffffff À333333ó¿ÍÌÌÌÌÌÀð¿333333Àà¿ffffffæ¿ffffffþ¿š™™™™™Àš™™™™™é¿š™™™™™Ù¿ffffffö¿š™™™™™ÀÍÌÌÌÌÌÀffffffÀš™™™™™ñ¿ffffff濚™™™™™Àš™™™™™ ÀÍÌÌÌÌÌü¿à¿š™™™™™ù¿ffffffÀ333333û¿ð¿333333ã¿333333Àš™™™™™ñ¿ÍÌÌÌÌÌô¿ffffffæ¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333ÀÀÍÌÌÌÌÌÀÀø¿ÍÌÌÌÌÌÀffffff Àš™™™™™ù¿š™™™™™ù¿š™™™™™ Àš™™™™™ÀÍÌÌÌÌÌì¿333333Àš™™™™™Àš™™™™™ñ¿ffffffÀÀ333333û¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™ù¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿333333ÀffffffÀ333333ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333û¿ffffffþ¿ð¿š™™™™™ù¿333333#ÀÍÌÌÌÌÌÀffffffö¿333333À333333ã¿ffffffÀ333333ó¿ð¿ffffffþ¿ffffffö¿333333û¿333333û¿š™™™™™ÀÍÌÌÌÌÌô¿ð¿š™™™™™ÀÀÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌ쿚™™™™™Ù¿ffffffö¿ffffff ÀÍÌÌÌÌÌô¿Àš™™™™™ù¿333333û¿ÍÌÌÌÌÌÀš™™™™™ñ¿ð¿ø¿ffffff濚™™™™™é¿ÀÀ333333ÀÍÌÌÌÌÌÀ333333ÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌü¿ÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌì¿ ÀÀÍÌÌÌÌÌÀð¿333333ã¿ffffffæ¿Àð¿ð¿ffffffö¿ÀÀš™™™™™ÀÀš™™™™™À333333ã¿À333333ã¿ffffffÀÍÌÌÌÌÌô¿333333ÀÀffffffÀ333333û¿ø¿ffffffþ¿Àø¿à¿333333û¿ÍÌÌÌÌÌü¿333333û¿ffffffþ¿š™™™™™ÀÍÌÌÌÌÌÀffffffÀffffff Àffffffö¿ø¿333333Àš™™™™™Àš™™™™™Ù¿ffffffö¿š™™™™™ñ¿À333333û¿ÍÌÌÌÌÌÀÍÌÌÌÌÌü¿333333ó¿ffffff ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿333333û¿š™™™™™ÀÀ333333ã¿ÍÌÌÌÌÌ쿚™™™™™À333333À333333Àš™™™™™ù¿ffffff#À333333ó¿ø¿ÍÌÌÌÌÌÀ333333û¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀffffffÀffffff"À333333ÀffffffÀÍÌÌÌÌÌÀÀÀÍÌÌÌÌÌì¿ffffffÀø¿ffffffæ¿ÍÌÌÌÌÌÀš™™™™™ Àš™™™™™ù¿ffffffö¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀ€0À333333ã?ffffffæ?ð?š™™™™™Ù?š™™™™™ @š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™é?333333ó¿ø¿ÍÌÌÌÌÌô?š™™™™™¹?ffffffæ?š™™™™™Ù?ffffffþ?ffffffö?à?333333ã?ÍÌÌÌÌÌü?333333ó?333333û?333333ó?ÍÌÌÌÌÌì?š™™™™™é?ÍÌÌÌÌÌì?333333ó?ffffffö?š™™™™™¹?ÍÌÌÌÌÌì?333333ó?ð?ø?š™™™™™ñ?š™™™™™é?š™™™™™é?š™™™™™¹¿š™™™™™ù?š™™™™™é?š™™™™™ñ?š™™™™™¹?š™™™™™ñ?ffffffæ?ð?333333ó?ffffffþ?ÍÌÌÌÌÌì?š™™™™™ñ?ÍÌÌÌÌÌô?ffffffö?ÍÌÌÌÌÌì?š™™™™™é?ð?ffffffö?ÍÌÌÌÌÌô?š™™™™™é?ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?à?š™™™™™é?ffffffþ?ÍÌÌÌÌÌì?ð?ÍÌÌÌÌÌô?š™™™™™é?š™™™™™É¿š™™™™™é?à?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?à¿@ø?š™™™™™ù?š™™™™™é?š™™™™™É?ffffffæ?333333ó?ffffffæ?š™™™™™ñ?ÍÌÌÌÌÌô?š™™™™™ñ?333333ã?ÍÌÌÌÌÌü?ffffffö?ð?š™™™™™ñ?š™™™™™ñ?333333ó?@333333ó?ÍÌÌÌÌÌô?ffffffö?333333ã?š™™™™™ñ?š™™™™™Ù?ð?ð?ÍÌÌÌÌÌô?š™™™™™É?ÍÌÌÌÌÌì?ð?š™™™™™é?ffffffö?ffffffæ?ffffffþ?š™™™™™é?š™™™™™ñ?333333ó?ÍÌÌÌÌÌì?ð?333333û?333333ó?ø?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™ñ?ÍÌÌÌÌÌ쿚™™™™™é?ffffffæ?ð?ffffffö?š™™™™™¹?š™™™™™Ù?ø?š™™™™™é?š™™™™™é?333333ã?ÍÌÌÌÌÌì?ð?š™™™™™ñ?333333ó?ÍÌÌÌÌÌì?ð?333333ã?333333ó?ÍÌÌÌÌÌô?š™™™™™é?ÍÌÌÌÌÌô?à?ffffffö?ÍÌÌÌÌÌô?ffffffæ?ÍÌÌÌÌÌü?333333ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?š™™™™™ñ?ÍÌÌÌÌÌô?ð?333333û?à?ø?š™™™™™ñ?š™™™™™É?ÍÌÌÌÌÌô?ø?333333ã?ø?ø?ffffffæ?ffffffæ?ð?š™™™™™é?ffffffæ?333333ó?333333û?ffffffæ?333333ã?333333ó?ð?ffffffö?š™™™™™ñ?š™™™™™ñ?š™™™™™é?š™™™™™É?ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™ñ?ÍÌÌÌÌÌì?ffffffö?ð?š™™™™™Ù?333333ã¿333333ã?š™™™™™ñ?ffffffæ?333333ó?ffffffæ?ffffffö?š™™™™™ñ?333333ã?ÍÌÌÌÌÌì?ø?š™™™™™ñ?ø?333333ã?333333ó?š™™™™™é?à?š™™™™™ñ?ð?š™™™™™Ù¿š™™™™™ñ?š™™™™™é?š™™™™™ñ?ÍÌÌÌÌÌì?333333 @ffffffæ¿ffffffæ?333333ã?ÍÌÌÌÌÌì?333333ã?š™™™™™É¿ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?333333ã¿ø?š™™™™™ñ?š™™™™™ù?š™™™™™ù?333333ã?333333û?ø?333333ã?š™™™™™ñ?š™™™™™¹¿ffffffö?š™™™™™é?š™™™™™ñ?š™™™™™Ù?ð?ffffffæ?ffffffþ?333333ó?ð?à?333333ã?ffffffö?ø?ÍÌÌÌÌÌô?ð?ø?ð?ffffffö?ø?ffffffö?333333û?ø?š™™™™™é?333333ó?š™™™™™É?š™™™™™ñ?š™™™™™ù?š™™™™™ù?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?333333û?333333ó?š™™™™™é?ÍÌÌÌÌÌì?à?@š™™™™™ù?333333ó?š™™™™™é?ð?333333û?333333û?ð?333333ã?ÍÌÌÌÌÌì?à?333333ó?ffffffö?ÍÌÌÌÌÌì?š™™™™™ñ?333333ã?333333ã?ð¿ÍÌÌÌÌÌì?333333ã?333333ã?š™™™™™é?ÍÌÌÌÌÌü?š™™™™™É?š™™™™™ñ?š™™™™™¹¿š™™™™™é?ð?ð?ÍÌÌÌÌÌì?š™™™™™Ù?ð?š™™™™™é?ÍÌÌÌÌÌì?ð?ffffffæ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?ffffffö?ÍÌÌÌÌÌì?š™™™™™ñ?à?à?š™™™™™@š™™™™™É?333333ã?ð?ÍÌÌÌÌÌô?333333ó?š™™™™™ù?333333Ó?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ffffffæ¿ø?š™™™™™É¿ÍÌÌÌÌÌô?š™™™™™É?333333ó?ø?333333û?š™™™™™Ù¿333333û?š™™™™™é?ð?ÍÌÌÌÌÌü?ð?š™™™™™é?333333ã?š™™™™™ù?ð?333333ã?ÍÌÌÌÌÌô?333333û?š™™™™™é?333333ó?š™™™™™Ù?333333@š™™™™™é?ð?š™™™™™ñ?ffffffö?333333û?ffffffæ?š™™™™™ñ?333333ó?ÍÌÌÌÌÌô?ffffffæ?ffffffö?š™™™™™ù?ð?ffffffö?ð?@ÍÌÌÌÌÌì?333333ã?š™™™™™É¿à?ÍÌÌÌÌÌ@333333û?ÍÌÌÌÌÌì?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ð?ð?š™™™™™é?à?333333ã?š™™™™™ñ?ø?š™™™™™é?š™™™™™ù?š™™™™™Ù?š™™™™™é?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?333333ã?ø?333333û?ÍÌÌÌÌÌì?ffffffö?ffffffþ?š™™™™™ñ?ø?à?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô¿333333ó?š™™™™™¹¿333333ã?333333ó?ð?š™™™™™ñ?333333ã?š™™™™™ñ?š™™™™™ñ?333333Ó?š™™™™™ñ?ffffffæ?333333ó?ð?ffffffæ?š™™™™™é?333333ã¿333333ã?š™™™™™Ù?333333û?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?š™™™™™É?ffffffö?š™™™™™¹?333333Ó¿ø?š™™™™™ñ?ð?ffffff@333333ã?333333ó?333333ó?ÍÌÌÌÌÌü?š™™™™™é?š™™™™™ù?ø?ffffffö?š™™™™™ñ?ffffffæ?333333ã?ð?ÍÌÌÌÌÌì?š™™™™™ñ?333333ó?à?ÍÌÌÌÌÌô?333333û?ffffffö?333333û?333333ó?333333ó?š™™™™™é?ð?ÍÌÌÌÌÌì?š™™™™™é?ð?š™™™™™Ù?ffffffö?ð?ÍÌÌÌÌÌì?ø¿à?ð?ø?333333û?š™™™™™@ø?333333û?333333@ffffffö?ffffffæ?ffffff@333333ó?ffffffö?ÍÌÌÌÌÌü?š™™™™™é?333333û?@ffffff@ÍÌÌÌÌÌ@ffffffæ?š™™™™™É?ÍÌÌÌÌÌü?š™™™™™ @ø?ÍÌÌÌÌÌü?ffffffæ?š™™™™™ù?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@@š™™™™™¹?š™™™™™é¿333333ó?š™™™™™é?@ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ø?333333 @@à?ð?333333ã?ffffffþ?ffffffþ?š™™™™™é?ÍÌÌÌÌÌô?ffffffö?š™™™™™É?ffffffö?à?333333û?ffffffö?š™™™™™é?ÍÌÌÌÌÌ@333333ã?@ffffffö?333333ã?š™™™™™¹?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?@333333@ @333333@š™™™™™é?à?333333@ø?333333û?333333@ÍÌÌÌÌÌ@ø?ffffffö?ffffff @ÍÌÌÌÌÌ@š™™™™™ñ¿š™™™™™Ù¿š™™™™™@š™™™™™é?333333ã?@à?š™™™™™ñ?ffffffþ?ø¿ffffffö¿333333ã?š™™™™™Àffffffæ?à?š™™™™™¹?333333û?333333Ó?ÍÌÌÌÌÌ@333333@ø?š™™™™™ @š™™™™™ñ?ÍÌÌÌÌÌì¿ffffffæ?ð?ffffffö?@333333ó?š™™™™™Ù?à?š™™™™™@ø¿à?ÍÌÌÌÌÌô?š™™™™™ù?ffffff@š™™™™™@ÍÌÌÌÌÌü?š™™™™™@ÍÌÌÌÌÌü?@ÍÌÌÌÌÌô?ffffffö?333333û?ð?ÍÌÌÌÌÌ@ffffff@š™™™™™ñ?@333333ó?ÍÌÌÌÌÌü?333333ó?š™™™™™ñ?333333 @š™™™™™é?ÍÌÌÌÌÌô?333333@š™™™™™@š™™™™™@333333@333333@ð?ffffffö?ÍÌÌÌÌÌü?333333û?ffffff@ÍÌÌÌÌÌ@š™™™™™ @ffffffþ?333333@š™™™™™ù?333333û?ffffffö?333333@333333û?ÍÌÌÌÌÌ@ffffffö?ffffffþ?ÍÌÌÌÌÌ@333333ó?ffffffæ?ÍÌÌÌÌÌü?š™™™™™ù?ffffff@š™™™™™é?ffffff @š™™™™™@@š™™™™™é?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?š™™™™™ñ?333333ó?ffffffö?š™™™™™ @ø?ø?333333ó?ffffffö?š™™™™™ù?333333û?š™™™™™Ù¿à?333333 @ffffffæ?ÍÌÌÌÌÌì¿333333Ó¿š™™™™™é?š™™™™™É?š™™™™™Ù?333333ã¿à?@š™™™™™é?ÍÌÌÌÌÌì?333333ó¿ffffffö?@ffffff@ffffffö?333333Àš™™™™™@333333ã?333333ó?ffffffö¿ffffff@333333@333333@@333333ó¿ffffff@@ÍÌÌÌÌÌ@š™™™™™ @ÍÌÌÌÌÌü¿à¿š™™™™™@ffffff濚™™™™™Ù?ð?333333ã¿ÍÌÌÌÌÌü?š™™™™™ù?ÍÌÌÌÌÌ@š™™™™™@š™™™™™ù?à?ÍÌÌÌÌÌô?333333ó?ffffffþ?ø?š™™™™™é?š™™™™™Ù?ffffff@333333ã? @333333ã¿ffffff@à?333333ó¿š™™™™™ù?ø?š™™™™™ñ¿ÍÌÌÌÌÌü?š™™™™™Ù¿@ð¿ffffffþ?333333û?š™™™™™ñ?š™™™™™ù?ffffff@à¿333333ã?š™™™™™ñ?ffffff@ÍÌÌÌÌÌ@333333û¿333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?ffffffæ?ÍÌÌÌÌÌ@ffffff@ð?ÍÌÌÌÌÌü?333333Ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™ù?@ÍÌÌÌÌÌ@ @@š™™™™™é?š™™™™™Ù?à?@ÍÌÌÌÌÌì?@ÍÌÌÌÌÌ@ffffffæ?ÍÌÌÌÌÌü?š™™™™™ñ?ÍÌÌÌÌÌô?ffffffö?ð?š™™™™™ù?ffffffþ?333333ã?ÍÌÌÌÌÌì?333333ã?333333@ÍÌÌÌÌÌü?333333ã?š™™™™™ñ¿š™™™™™ù?333333û¿333333@ffffffö?š™™™™™@š™™™™™é¿š™™™™™ù?š™™™™™ñ?333333û?ÍÌÌÌÌÌô¿@333333ã?333333ó?ÍÌÌÌÌÌü¿333333@333333@333333ã?333333@ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?333333ó?ø?@ø?ffffffö?š™™™™™é?ø?š™™™™™Ù?333333û¿š™™™™™@š™™™™™ñ?333333û?ø?š™™™™™À333333Ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ffffff@ffffffö?333333@ÍÌÌÌÌÌì?ffffffæ?ÍÌÌÌÌÌü?333333 @333333Ó?ÍÌÌÌÌÌü?333333ã?ffffffö?ð?š™™™™™@š™™™™™@š™™™™™ù?ÍÌÌÌÌÌü?à?š™™™™™ñ?ffffff@ÍÌÌÌÌÌì¿333333ó?š™™™™™é?à?ø?ffffff@333333ã?ffffff@333333û¿š™™™™™¹¿ffffff@š™™™™™ñ?š™™™™™@333333û?@333333Ó¿š™™™™™é?à?à?š™™™™™Àffffffæ?ÍÌÌÌÌÌô¿333333 @@š™™™™™Ù?ð?ffffffö?š™™™™™@@333333Ó?š™™™™™ù¿333333Ó?333333ã?ÍÌÌÌÌÌ @ð?ffffffþ?ÍÌÌÌÌÌô?š™™™™™Àš™™™™™¹?ffffffþ?333333@ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@333333ã?ÍÌÌÌÌÌü¿š™™™™™@ÍÌÌÌÌÌ@@š™™™™™ñ?333333@ffffff @à?333333@ffffff@ÍÌÌÌÌÌ @š™™™™™ù?333333@333333Ó?š™™™™™é?333333ã?ð?333333ã?ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?333333Ó?333333û¿š™™™™™¹?ø?š™™™™™ @ÍÌÌÌÌÌü¿à?ffffff@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@š™™™™™ù?ð?333333ã?@ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?333333 @à?ÍÌÌÌÌÌü?333333ó?ffffff@ÍÌÌÌÌÌü?à?ffffffæ?ffffff@š™™™™™ñ?ffffffæ?š™™™™™é?ÍÌÌÌÌÌ@ffffffþ?333333@à?š™™™™™@333333ã?@@š™™™™™ @š™™™™™1@ffffff,@€8@ÍÌÌÌÌÌ@š™™™™YB@+@ffffff3@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌ:@3333333@ÍÌÌÌÌÌ@333333@ffffff@@33333óF@š™™™™™-@ffffff6@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌ/@333333"@ffffff+@€2@<@333333"@fffffæ;@ÍÌÌÌÌÌÀ€5@ffffff.@ffffff'@ffffffÀÍÌÌÌÌÌ)Àš™™™™™#@ffffff6@333333/@333333'@333333>@š™™™™™,@ÍÌÌÌÌÌ/@333333-@fffffæ4ÀÍÌÌÌÌÌ,@š™™™™™0@ÍÌÌÌÌÌ5@ÍÌÌÌÌŒC@333333 @ø?333333.@fffffæ1@š™™™™4@@ffffff@ÍÌÌÌÌÌ"@333333+@ÍÌÌÌÌLG@<@€9@fffffæ6@ffffff@33333³3@333333=@@ffffff.@fffffæ0@ÍÌÌÌÌL<@š™™™™™M@ÍÌÌÌÌÌ'@ffffff @š™™™™;@ÍÌÌÌÌÌ<@ffffffÀffffff<@3333332@ÍÌÌÌÌL0@ @š™™™™™>@š™™™™™*@333333+@š™™™™™#@;@š™™™™C@ÍÌÌÌÌL@@š™™™™™8@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌÀ333333D@@B@ÍÌÌÌÌLI@33333³7@:@ÍÌÌÌÌLF@ffffff"@333333@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌÀ@ÍÌÌÌÌÌ+@ffffff)@ffffff@€>@ffffff@ÀI@š™™™™™ ÀÍÌÌÌÌLD@ÍÌÌÌÌÌ9@333333@ffffff@š™™™™™2@fffff¦@@fffffæ7@333333@ffffff$@ffffff0@fffffæ?@ÍÌÌÌÌÌ8@š™™™™™@3333331@@š™™™™™@ffffff:@ÍÌÌÌÌL6@ÍÌÌÌÌÌ@š™™™™™À333333@@ffffff5@š™™™™™!@ÍÌÌÌÌÌ,@+@ÍÌÌÌÌÌ0@33333³8@š™™™™™5@3333335@š™™™™A@š™™™™™/@333333H@333333E@ÍÌÌÌÌÌ@€6@ @š™™™™™G@š™™™™™ @ÍÌÌÌÌÌ @ÍÌÌÌÌLG@333333@@š™™™™4@š™™™™™.@ÍÌÌÌÌÌ)@ffffff+@ÍÌÌÌÌÌ(@ÍÌÌÌÌÌ%@333333À'@ÍÌÌÌÌL5@3333335@33333³:@ffffff6@š™™™™YH@š™™™™™@333333;@š™™™™™/@ÍÌÌÌÌÌ;@ÍÌÌÌÌÌ@?@fffffæ4@À333333@ÍÌÌÌÌÌ%@@š™™™™™$@#Àš™™™™™*@š™™™™™7@€3@333333@ÀD@fffffæ3@ffffff'@)@š™™™™™A@333333.@š™™™™™ @ffffff.@333333Àffffffö¿9@ffffff+@3333334@ffffff@333333%À333333;@@33333³=@ffffff/@ÍÌÌÌÌÌ"@fffffæB@333333@š™™™™YD@L@š™™™™™@333333+@š™™™™™0@ÍÌÌÌÌÌ.@ÍÌÌÌÌŒB@ÍÌÌÌÌLD@ffffff@š™™™™E@š™™™™F@ÍÌÌÌÌÌ9@€1@ÍÌÌÌÌÌ@333333@33333P@333333@š™™™™™!@8@ÍÌÌÌÌÌ(@ @€9@ÍÌÌÌÌÌ@ffffffþ?33333³?@ffffff1@333333-@ÍÌÌÌÌÌ/@š™™™™™%@ÍÌÌÌÌL0@ffffff$@ÍÌÌÌÌŒH@33333³2@2@ÍÌÌÌÌÌ3@33333sI@€F@š™™™™™$@333333=@(@€=@ÍÌÌÌÌL@@333333+@ffffff(@+@ÍÌÌÌÌÌ/@fffffæ6@ÍÌÌÌÌÌ@33333óF@ÍÌÌÌÌÌ6@ÍÌÌÌÌÌü?š™™™™™É?š™™™™™ù¿ÍÌÌÌÌÌ@.ÀÍÌÌÌÌÌÀ333333"Àš™™™™™-@3333332@š™™™™™@3333334@fffffæ?@ffffff,@ÍÌÌÌÌÌü?ÍÌÌÌÌL;@4@š™™™™™8@ÍÌÌÌÌÌ$@fffffæ0@333333㿚™™™™™@š™™™™™!@š™™™™Y@@š™™™™™5@8@&@ÍÌÌÌÌÌ*@š™™™™™,@ÍÌÌÌÌL?@fffffæ>@š™™™™™3@ffffff>@ffffff*@š™™™™™À"@š™™™™™@š™™™™™Ù?ÍÌÌÌÌÌü¿@A@ffffff9@ffffff%@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌ@€>@3333333@333333ã?š™™™™6@L@333333;@ÍÌÌÌÌÌì?ffffff2@3333335@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌ)@à?ÍÌÌÌÌÌ Àš™™™™™&@ffffff;@fffffæ0@3333330@š™™™™™E@š™™™™0@š™™™™™.@€9@333333@š™™™™™ÀÍÌÌÌÌÌ(@š™™™™™1@ffffff!@š™™™™™0@ffffff6@ÍÌÌÌÌÌ@ø?š™™™™0@ÍÌÌÌÌÌ#@33333³2@ffffffÀÍÌÌÌÌL0@333333(@š™™™™™2À€7@ffffff,@š™™™™™@fffff&A@33333³M@ÍÌÌÌÌÌ:@ÍÌÌÌÌÌ(@$@ffffff#@ffffff7@ÍÌÌÌÌL5@ÍÌÌÌÌÌ*@33333³6@ffffff@ÍÌÌÌÌL2@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌ-@š™™™™™1@ffffff4@ÍÌÌÌÌÌ@š™™™™™1@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ/@3333330@ÍÌÌÌÌÌ9@ø¿ffffffÀÍÌÌÌÌL4@š™™™™3Àš™™™™<@ÍÌÌÌÌÌ%@š™™™™™B@ffffffE@8@ÍÌÌÌÌÌ3@š™™™™=@@š™™™™™6@333333㿚™™™™Ù@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ0@=@333333#@fffffæ7@333333+@ÍÌÌÌÌL7@ÍÌÌÌÌÌ@fffffæ0ÀÍÌÌÌÌÌ@3333339@ffffff$@ffffffþ?€:@€?@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌ@@ÀE@333333(@333333?@ÍÌÌÌÌL;@ffffffÀš™™™™™$@€;@33333³:@ÀE@š™™™™™#@333333?@,@š™™™™™)@ÍÌÌÌÌÌ,Àš™™™™™%@€9@š™™™™?@ffffff<@33333³5À€9@š™™™™™3@š™™™™™:@fffff&P@š™™™™4@ÍÌÌÌÌÌ&@7@)@ÀE@33333óA@š™™™™D@33333³2@š™™™™™?@ffffff@ffffff"@ffffff1@€4@ffffff4@ÍÌÌÌÌÌ:@š™™™™™@ÍÌÌÌÌÌÀfffffæ>@ÍÌÌÌÌÌ.@ÍÌÌÌÌÌÀ3333331@333333?@333333+@3333330@ffffff @333333/À333333@€<@fffffæ0@ÍÌÌÌÌÌ?@(@33333sA@333333;@š™™™™™9@À333333#@ÍÌÌÌÌÌ&@š™™™™™'@3333334@š™™™™™ñ¿ffffff=@€EÀÍÌÌÌÌL=@€5@-@€=@33333óB@ffffffB@fffffæ2@333333U@33333³6@fffffæ6@?@š™™™™™A@333333=@33333³5@ÍÌÌÌÌÌ@F@ÍÌÌÌÌŒN@ÍÌÌÌÌÌ?@ÍÌÌÌÌLE@ÍÌÌÌÌL2@1@š™™™™7@š™™™™YI@š™™™™™:@ÍÌÌÌÌŒC@333333)@ÍÌÌÌÌŒC@ÍÌÌÌÌÌ*@š™™™™B@š™™™™™C@š™™™™™*@3333330Àffffffö¿ÍÌÌÌÌÌ,@33333óD@€<@š™™™™0@€A@š™™™™?@@G@=@š™™™™™1ÀÍÌÌÌÌÌ3@3333335@fffff&A@š™™™™™I@ @ffffff @ÍÌÌÌÌL:@33333³2@š™™™™™=@š™™™™™"@š™™™™3@3333336@3@ÍÌÌÌÌŒL@š™™™™™?@ÍÌÌÌÌŒG@33333³>@,@€4@A@š™™™™™&@š™™™™A@š™™™™ÙB@33333sK@š™™™™9S@€3@ffffff'@33333óG@ÍÌÌÌÌÌC@ffffff"@€H@fffff¦@@€:@+@ÍÌÌÌÌÌK@33333sG@ÍÌÌÌÌÌ@š™™™™™@ÀD@š™™™™E@@B@š™™™™™H@33333³2@ÍÌÌÌÌÌ@33333sJ@7@33333óE@ÍÌÌÌÌÌ?@.ÀÍÌÌÌÌŒJ@*@333333@€0@@.@fffffæ=@ÍÌÌÌÌL9@š™™™™YE@ÍÌÌÌÌÌ.@7@#@33333óL@ @fffff&S@33333³@@ffffff@@š™™™™ÙA@3333330@>@+@33333³5@ÍÌÌÌÌŒB@fffffS@ÍÌÌÌÌÌA@3333336@š™™™™>@4@ffffff%@33333óA@ffffff@@(@š™™™™™%@€4@ffffff&@33333óE@3333330@ÍÌÌÌÌÌ8@ffffff5@8@@I@fffffæ9@>@33333³I@š™™™™ÙA@€P@fffffæO@7@ÍÌÌÌÌL;@ffffff0@33333³M@>@ffffff6@@O@33333³>@fffffæ3@fffffæB@ÍÌÌÌÌÌ9@ÍÌÌÌÌÌ8@3333335@@@ÍÌÌÌÌÌ7@ÍÌÌÌÌÌ)@33333³4@ÍÌÌÌÌLA@ÍÌÌÌÌŒB@fffff&B@ffffff<@ P@€4@ÍÌÌÌÌ I@3333335@33333³M@š™™™™™8@I@€9@333333'@š™™™™ÙE@ffffff3@ffffff*@fffffæ2@333333Ó¿fffff&B@š™™™™™@@333333<@*@€H@š™™™™=@fffffæ5@ÍÌÌÌÌÌ!@š™™™™C@€F@š™™™™™(@#@333333Àš™™™™™@ÍÌÌÌÌL:@š™™™™™/@333333.@š™™™™™)@ÍÌÌÌÌÌ"@ÍÌÌÌÌL@@333333!@€0@ÍÌÌÌÌL8@š™™™™™6@@J@ÍÌÌÌÌÌ-@ @ÍÌÌÌÌ,R@š™™™™™#@5@š™™™™™ù¿33333sC@33333óL@33333³K@3@3333339@ÍÌÌÌÌ L@fffffæN@fffffÆU@š™™™™™?@333333@G@@33333³7@33333³2@š™™™™™.@ffffff#@fffffæ4@3333332@333333*@33333óF@333333C@ffffff9@š™™™™™6@3333335@fffffæ7@33333³9@fffff¦M@ffffff8@5@š™™™™ÙI@€J@ÍÌÌÌÌLS@333333@ffffffF@333333/@€7@G@3333337@š™™™™™¹¿@@@ffffff+@33333óE@š™™™™™ÀÍÌÌÌÌŒN@ÍÌÌÌÌL@@#@333333)@,@Àš™™™™™Àffffff@š™™™™™+@š™™™™™<@à¿ÍÌÌÌÌÌ%@ÍÌÌÌÌL8@@E@fffffæ1@€7@ÍÌÌÌÌLG@ffffff9@š™™™™ÙA@(@;@š™™™™™#@š™™™™™,@ÍÌÌÌÌÌ>@š™™™™H@š™™™™H@H@ffffff4@š™™™™0@33333³0@33333sG@ÀA@A@š™™™™YG@ÍÌÌÌÌÌ1@@€1@š™™™™™'@&@ÍÌÌÌÌÌ@fffff¦F@ÍÌÌÌÌLD@ffffff+@33333³1@ÍÌÌÌÌÌ*@fffffæL@33333³>@š™™™™™@@fffff¦O@@š™™™™@@333333<@fffff&C@@ffffff8@333333(@ffffff@ @33333sH@ffffff6@ÍÌÌÌÌÌ7@ÍÌÌÌÌÌ;@333333D@33333sA@š™™™™™<@fffffæ8@š™™™™™¹?fffffæ7@3333339@2@š™™™™™<@€>@ffffff1@ÍÌÌÌÌÌ@€>@(@À€0@š™™™™™6@8@Àš™™™™™ À€2@ffffff/@š™™™™D@š™™™™S@ÍÌÌÌÌLA@33333³O@3333334@333333)@š™™™™Y@@fffffæH@333333.@333333@@ffffff#@€;@3333334@€B@€@@š™™™™@@ffffff5@š™™™™™4@š™™™™™+@š™™™™™>@333333@š™™™™@@333333$@š™™™™™@€>@333333û¿33333³>@fffffæ=@š™™™™™3@fffffæD@ffffffE@ÍÌÌÌÌL>@33333sF@3333332@ÍÌÌÌÌLC@ffffffþ¿33333sF@333333)@3333334@ÍÌÌÌÌÌÀš™™™™™*@333333@33333³D@ÍÌÌÌÌ N@#@333333 À333333-@ÍÌÌÌÌ F@ÍÌÌÌÌÌ5@333333@%@33333³@@š™™™™™3@ÀM@33333óI@š™™™™™8@33333³B@ÍÌÌÌÌÌ6À333333ÀÍÌÌÌÌÌ9@33333SP@€?@333333K@š™™™™™)@333333@š™™™™™?@fffffæ;@ffffff@š™™™™1@š™™™™ÙE@ffffffM@ÍÌÌÌÌÌ?@ffffffö¿333333H@J@ÍÌÌÌÌŒB@33333óT@š™™™™™6@.@ffffff9@33333³5@ÍÌÌÌÌÌI@€I@š™™™™YG@33333³4@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ2@š™™™™G@ffffff@33333³7@fffffæF@š™™™™9@33333S@@H@8@@fffffæ4@333333F@8@ÍÌÌÌÌL9@€>@ÍÌÌÌÌÌ(ÀÍÌÌÌÌÌ2@333333B@@B@ÍÌÌÌÌÌE@ÍÌÌÌÌL1@š™™™™ÙB@G@š™™™™@@@š™™™™™-@š™™™™™@@ÍÌÌÌÌÌ9@€B@ÍÌÌÌÌÌ@š™™™™ÙG@@CÀÍÌÌÌÌ F@33333ó@@š™™™™™¹?333333Ó?š™™™™™É¿š™™™™™Ù¿š™™™™™É?š™™™™™Ù¿š™™™™™ñ?š™™™™™¹¿š™™™™™É¿š™™™™™¹¿333333ã?š™™™™™É¿š™™™™™É¿š™™™™™É?š™™™™™É¿š™™™™™É¿ÍÌÌÌÌÌì?š™™™™™É?ffffffæ?ÍÌÌÌÌÌô?š™™™™™¹?š™™™™™¹¿š™™™™™É?š™™™™™É?š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™é?š™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿š™™™™™É?š™™™™™Ù?333333Ó¿ÍÌÌÌÌÌì?š™™™™™É?š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™É¿š™™™™™¹?ð¿333333Ó?à?š™™™™™É¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹¿š™™™™™¹?à?š™™™™™¹¿š™™™™™é?š™™™™™Ù¿333333Ó?š™™™™™¹?à?š™™™™™Ù¿š™™™™™É¿š™™™™™¹¿à?š™™™™™¹?333333Ó?š™™™™™É?š™™™™™É¿š™™™™™ñ¿à?ÍÌÌÌÌÌô¿ø?š™™™™™É¿à?ð¿à¿š™™™™™¹?š™™™™™¹¿à¿333333ã?š™™™™™¹¿š™™™™™¹¿333333Ó¿š™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™¹?ffffffö¿à?š™™™™™¹?š™™™™™¹¿š™™™™™é?333333Ó¿š™™™™™É?š™™™™™É¿š™™™™™Ù¿š™™™™™É?333333Ó?š™™™™™é?š™™™™™¹?š™™™™™É¿ÍÌÌÌÌÌì?333333ã?ffffffæ?333333ã?š™™™™™Ù¿š™™™™™Ù?š™™™™™é¿š™™™™™É¿à¿à?š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™¹?ffffff濚™™™™™¹¿à?š™™™™™¹?š™™™™™¹¿š™™™™™É¿à¿š™™™™™É¿š™™™™™¹?š™™™™™É¿š™™™™™¹?š™™™™™É¿333333㿚™™™™™É¿š™™™™™é¿š™™™™™É?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™¹¿333333ã?š™™™™™Ù?ffffffæ?š™™™™™¹?š™™™™™Ù?ÍÌÌÌÌÌì¿333333Ó¿š™™™™™¹¿š™™™™™É¿š™™™™™É?š™™™™™¹?š™™™™™É?š™™™™™É?š™™™™™¹¿š™™™™™Ù?333333ã?ffffffæ?š™™™™™É?@333333ã?333333ã¿333333Ó¿š™™™™™Ù¿ø¿à?š™™™™™É?š™™™™™É?333333ó?š™™™™™É?333333㿚™™™™™¹?ffffffæ?š™™™™™¹?š™™™™™é¿à¿š™™™™™¹¿333333Ó?ffffffö¿ffffffæ?š™™™™™¹?࿚™™™™™¹¿š™™™™™Ù¿š™™™™™¹?š™™™™™É?333333Ó?333333Ó¿š™™™™™¹¿š™™™™™¹?333333ã¿333333ã?š™™™™™¹¿333333ã?š™™™™™¹?š™™™™™¹?š™™™™™Ù?š™™™™™ñ?š™™™™™É¿š™™™™™é?š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™¹¿à¿š™™™™™É?š™™™™™Ù¿š™™™™™É¿ffffffæ?š™™™™™É¿š™™™™™¹?š™™™™™¹¿š™™™™™ñ?333333ó?š™™™™™Ù?333333ã?333333ã?š™™™™™É¿333333ã?ffffffæ?š™™™™™¹¿š™™™™™¹¿à¿š™™™™™¹¿333333ã?à¿à¿š™™™™™É¿š™™™™™¹?š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™É¿š™™™™™¹?š™™™™™Ù?š™™™™™É?333333ã?š™™™™™¹?333333ã?š™™™™™¹¿š™™™™™¹?ð?š™™™™™¹¿333333Ó?š™™™™™É¿š™™™™™Ù¿š™™™™™é?333333Ó¿š™™™™™¹¿ffffffæ¿ø?š™™™™™¹?š™™™™™É¿š™™™™™Ù?š™™™™™ñ¿333333ã?š™™™™™É?333333ã?š™™™™™¹¿à¿š™™™™™é¿š™™™™™¹?ÍÌÌÌÌÌì¿à?š™™™™™É?333333Ó¿š™™™™™É?š™™™™™É?š™™™™™¹¿à¿333333Ó?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?333333Ó¿333333ã?333333ã?š™™™™™é?š™™™™™Ù¿š™™™™™É?ÍÌÌÌÌÌì?ffffffæ¿333333Ó?š™™™™™¹¿š™™™™™É¿ø?š™™™™™Ù¿à¿š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿à¿š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™É?š™™™™™É¿š™™™™™¹?š™™™™™É?ø?š™™™™™¹?š™™™™™é?࿚™™™™™¹?333333Ó?š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™¹?333333ó?ffffff濚™™™™™Ù?š™™™™™¹¿š™™™™™Ù?š™™™™™É¿š™™™™™Ù?š™™™™™¹¿ð?š™™™™™¹¿à¿š™™™™™Ù¿333333ó?ffffff濚™™™™™¹¿ffffffæ¿333333㿚™™™™™¹?ffffff@@š™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿ÍÌÌÌÌÌô?š™™™™™¹?ffffffæ?š™™™™™É¿š™™™™™É¿à¿š™™™™™Ù?š™™™™™é?š™™™™™¹?333333Ó?š™™™™™¹?࿚™™™™™É?š™™™™™¹¿š™™™™™É?š™™™™™¹?ffffffæ¿ffffff濚™™™™™Ù?š™™™™™É¿š™™™™™é?š™™™™™¹¿@š™™™™™Ù?333333ó¿333333ã¿à?333333Ó¿333333û¿š™™™™™¹¿š™™™™™Ù?333333Ó?333333㿚™™™™™¹?࿚™™™™™Ù¿333333Ó¿š™™™™™¹¿333333ã?333333Ó¿š™™™™™¹?š™™™™™¹?ffffffæ?333333ó?333333ó?333333Ó?ÍÌÌÌÌÌÀš™™™™™ù¿333333㿚™™™™™Ù¿ffffffÀffffffÀÍÌÌÌÌÌÀ ÀÍÌÌÌÌÌÀ333333ã¿à¿š™™™™™Ù¿à¿ffffffæ¿333333%ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌü¿ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ffffffæ¿Àš™™™™™ ÀffffffÀÍÌÌÌÌÌü¿ð¿š™™™™™ À333333ã¿333333û¿ÍÌÌÌÌÌô¿ø¿ø¿333333㿚™™™™™ñ¿š™™™™™Ù¿333333ó¿333333ã¿à¿ÍÌÌÌÌÌü¿š™™™™™ÀÍÌÌÌÌÌÀ333333û¿ÍÌÌÌÌÌÀš™™™™™é¿à¿ffffffþ¿š™™™™™Àà¿ð¿š™™™™™%Àffffffþ¿333333ÀÍÌÌÌÌÌÀ333333ã¿à¿ø¿333333ó¿š™™™™™Àffffffæ¿333333㿚™™™™™Ù¿333333ó¿à¿ÍÌÌÌÌÌì¿ffffffö¿ffffffþ¿ÍÌÌÌÌÌô¿š™™™™™Ù¿333333ó¿ÍÌÌÌÌÌì¿333333㿚™™™™™é¿š™™™™™ À࿚™™™™™ñ¿333333ã¿ÍÌÌÌÌÌì¿ffffffÀÍÌÌÌÌÌÀffffffö¿ÀffffffÀš™™™™™!Àš™™™™™ÀÍÌÌÌÌÌÀÀš™™™™™Ù¿à¿š™™™™™é¿ffffffÀ333333û¿ ÀffffffÀ333333ã¿ÍÌÌÌÌÌô¿š™™™™™ñ¿š™™™™™À333333À333333ó¿333333ã¿ø¿š™™™™™ÀÍÌÌÌÌÌÀà¿333333 ÀffffffÀš™™™™™é¿ÍÌÌÌÌÌÀš™™™™™ Àš™™™™™ñ¿333333û¿š™™™™™À333333ã¿à¿ffffffÀÀÀ333333ó¿ÍÌÌÌÌÌÀffffffæ¿ÍÌÌÌÌÌ Àš™™™™™é¿Àš™™™™™Ù¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿à¿ffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ð¿333333Àš™™™™™Àš™™™™™é¿š™™™™™ù¿333333$Àš™™™™™ Àø¿333333ã¿333333ã¿Àš™™™™™ñ¿À333333Àffffffæ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™Ù¿€0Àffffffæ¿à¿ð¿ð¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀš™™™™™Àà¿333333À333333û¿š™™™™™Àffffffö¿333333 Àà¿ffffffÀš™™™™™ù¿ø¿333333㿚™™™™™Ù¿333333(Àð¿ð¿à¿ÍÌÌÌÌÌü¿333333ã¿ÍÌÌÌÌÌü¿š™™™™™ Àø¿š™™™™™ ÀÍÌÌÌÌÌÀ333333ÀffffffÀš™™™™™Ù¿š™™™™™ñ¿ffffffæ¿Àffffff À333333À࿚™™™™™Ù¿333333ã¿ffffffæ¿ÍÌÌÌÌÌì¿ffffffö?ÍÌÌÌÌÌì?š™™™™™@š™™™™™ù?333333ó?33333³5@š™™™™™@š™™™™™@@š™™™™™ @š™™™™™ù?š™™™™™ñ?333333*@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@@ffffff @à?ÍÌÌÌÌÌ@š™™™™™@@ffffffö?@ÍÌÌÌÌÌü?ffffff @333333@š™™™™™Ù?333333ó?ffffff@ffffff@ffffff@à?š™™™™™ @ð?ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌô?333333$@@333333û?ffffff@ffffff@ffffffæ?ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™$@ÍÌÌÌÌÌ@ø?ffffff@à?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@#@š™™™™™(@š™™™™™@ffffff@ffffff@333333@à?333333ó?ffffffþ?š™™™™™@ffffffæ?š™™™™™@333333@š™™™™™@333333û?š™™™™™'@š™™™™™ @333333 @š™™™™™@š™™™™™ñ?ffffff @ffffff@š™™™™™@ffffff)@ @ffffff @ffffff@ÍÌÌÌÌÌü?š™™™™™é?333333 @ffffffæ?ffffff@ð?ÍÌÌÌÌÌü?š™™™™™@š™™™™™ù?ffffff$@ffffff#@@š™™™™™Ù?@333333ã?@š™™™™5@ffffffþ?ffffffö?333333û?ffffff@ffffff$@@ffffffö?ÍÌÌÌÌÌ @š™™™™™ù?ÍÌÌÌÌÌ@ffffff@š™™™™™ñ?ÍÌÌÌÌÌì?š™™™™™ @ÍÌÌÌÌÌ@š™™™™™@333333û?ffffff @ffffffþ?š™™™™™@ffffff@š™™™™™@ @@ÍÌÌÌÌÌ@ @š™™™™™@333333 @@333333@š™™™™™@ø?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ð?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?333333û?š™™™™™@@@@@à?333333 @333333@ÍÌÌÌÌÌ@ffffffæ?333333@à? @š™™™™™Ù?333333@333333@ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™ù?ffffff@ÍÌÌÌÌÌì?š™™™™™@š™™™™™ù?ÍÌÌÌÌÌ@@ffffff@š™™™™™ñ?š™™™™™ù?@@à?@ÍÌÌÌÌÌô?š™™™™™@@333333@ÍÌÌÌÌÌ@ffffffæ?@à?333333@ø?ffffff@š™™™™™(@ÍÌÌÌÌÌô?ffffff4@333333@š™™™™™Ù?ffffffæ?ffffffþ?ÍÌÌÌÌÌ*@!@à?à?333333$@ffffff@š™™™™™%@ÍÌÌÌÌÌ"@à?ffffff,@333333 @š™™™™™Ù?ffffff @š™™™™™ù?ÍÌÌÌÌÌ@@ð?ø?ffffff@333333ó?š™™™™™@ffffff@@!@ffffff@333333@ffffff@333333 @ffffff+@333333@@ÍÌÌÌÌÌì?ffffff @333333@š™™™™™é?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333!@ÍÌÌÌÌÌô?ffffffö?š™™™™™é?333333@333333ã?ffffffö?ø?ÍÌÌÌÌÌ@333333@š™™™™™ @ÍÌÌÌÌÌü?š™™™™™Ù?à?@š™™™™™@š™™™™™@@ffffff@š™™™™™Ù?ffffffæ?š™™™™™é?ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ffffffæ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @333333@ffffffæ?333333ã?333333@333333ã?š™™™™™@ffffff @ffffff@ÍÌÌÌÌÌ @š™™™™™é?ÍÌÌÌÌÌì?333333@ffffff*@š™™™™™@š™™™™™@š™™™™™ù?ffffff @333333ã?ð?š™™™™™ @333333@ÍÌÌÌÌÌ @333333@@ffffff@ÍÌÌÌÌÌô?š™™™™™Ù?à?ÍÌÌÌÌÌü?@ffffff$@š™™™™™@š™™™™™@ð?ÍÌÌÌÌÌô?333333&@@ffffffæ?à?333333ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?ffffff@à?ÍÌÌÌÌÌ@'@ffffff @ffffffþ?ð?š™™™™™@333333@@ÍÌÌÌÌÌ@ffffff@333333ó?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@š™™™™™@ffffff"@ffffffþ?@à?š™™™™™ñ?@ffffff@š™™™™™Ù?@ffffff@333333 @@ffffff @333333ã?š™™™™™ @ÍÌÌÌÌÌô?333333ó?%@@333333 @ÍÌÌÌÌÌ@à?333333@ÍÌÌÌÌÌ@333333@ø?ffffff@@ffffff@333333!@š™™™™™@ÍÌÌÌÌÌì?ffffff$@ffffff@!@ffffff@ð?ÍÌÌÌÌÌì?ffffff@@333333&@ffffff@@333333ã?š™™™™™@ffffffæ?ffffffö?@.@š™™™™™@š™™™™™ù?š™™™™™@@ffffff@333333@ffffff@ÍÌÌÌÌÌ@333333%@@@ÍÌÌÌÌÌ@ffffff)@ffffff@ÍÌÌÌÌÌô?š™™™™™Ù?ffffff@š™™™™™!@š™™™™™ @š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333ã?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ0@ÍÌÌÌÌÌô?ø?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌ @ÍÌÌÌÌÌü?@ð?ffffff@@ffffff@333333@ÍÌÌÌÌÌ @ð?ÍÌÌÌÌÌ@ffffffö?à?333333@ø?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?š™™™™™À࿚™™™™™é¿š™™™™™Ù¿ÍÌÌÌÌÌü¿333333ó¿š™™™™™Ù¿ffffffÀš™™™™™ñ¿ø¿ffffffÀffffffþ¿ÍÌÌÌÌÌ%ÀÍÌÌÌÌÌ À333333Àš™™™™™é¿333333ÀÍÌÌÌÌÌÀÍÌÌÌÌÌì¿333333,À333333ã¿ffffffæ¿Àffffffþ¿Àš™™™™™ñ¿À333333 ÀÍÌÌÌÌÌ À333333ó¿ffffffæ¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333Àð¿ffffff!Àà¿333333ã¿ À333333Àš™™™™™ù¿ÍÌÌÌÌÌü¿ffffffÀÍÌÌÌÌÌü¿š™™™™™Àffffffæ¿333333ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌü¿333333㿚™™™™™Àš™™™™™Ù¿š™™™™™Ù¿š™™™™™ À333333Àš™™™™™é¿333333ÀÀÍÌÌÌÌÌÀà¿!ÀÍÌÌÌÌÌ Àš™™™™™ñ¿š™™™™™Ù¿ÍÌÌÌÌÌÀ333333û¿š™™™™™'Àffffffþ¿333333û¿š™™™™™ù¿š™™™™™(ÀÀÍÌÌÌÌÌÀffffffÀÀ333333Àà¿ffffffÀð¿ÍÌÌÌÌÌÀ333333ã¿ð¿Àš™™™™™À333333 Àš™™™™™ Àffffffö¿š™™™™™$ÀffffffÀffffffÀÍÌÌÌÌÌô¿š™™™™™ ÀffffffÀš™™™™™Àš™™™™™ñ¿333333ÀÀffffff#À333333ã¿333333û¿ffffffÀffffff Àš™™™™™ÀÀš™™™™™ñ¿333333ÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌÀ333333ó¿š™™™™™é¿333333Àš™™™™™é¿ffffff ÀÍÌÌÌÌÌÀffffffÀÍÌÌÌÌÌ!À333333(ÀÀš™™™™™Ù¿ffffffÀÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌü¿ Àš™™™™™ Àà¿ÍÌÌÌÌÌÀffffffö¿À ÀÍÌÌÌÌÌÀš™™™™™À333333Àš™™™™™ÀffffffÀ333333ÀffffffÀÍÌÌÌÌÌÀ'Àš™™™™™ù¿ffffffö¿ÍÌÌÌÌÌÀffffff À333333ã¿ÍÌÌÌÌÌÀ333333û¿À(Àš™™™™™Àš™™™™™ Àø¿ffffffÀÀš™™™™™ ÀÀÍÌÌÌÌÌì¿ø¿ffffffÀÍÌÌÌÌÌì¿à¿š™™™™™ÀffffffÀÀ333333À333333Àffffff*ÀÍÌÌÌÌÌÀÍÌÌÌÌÌô¿333333Àš™™™™™ÀÍÌÌÌÌÌÀÀ333333ÀÍÌÌÌÌÌÀ333333À333333ó¿ø¿333333ó¿š™™™™™ Àà¿333333ã¿ÍÌÌÌÌÌÀÍÌÌÌÌÌ&À333333Àš™™™™™ù¿ÍÌÌÌÌÌ(Àð¿ffffffæ¿333333㿚™™™™™Ù¿š™™™™™ÀÍÌÌÌÌÌô¿333333ó¿333333Àà¿ffffffÀ)Àš™™™™™é¿ø¿333333À$ÀffffffÀffffffÀš™™™™™Àð¿333333!Àffffffö¿333333ó¿à¿ÍÌÌÌÌÌÀÍÌÌÌÌL0ÀffffffÀffffffö?ffffff@š™™™™™ù?ffffff @ð?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ-@š™™™™™Ù?ÍÌÌÌÌÌì?ffffffæ?ÍÌÌÌÌÌô?ffffff@š™™™™™@à?š™™™™™Ù?à?ÍÌÌÌÌÌô?ð?333333ó?ÍÌÌÌÌÌ@@ffffff@333333û? @š™™™™™Ù?333333ã?ffffff@333333@ffffffæ?ffffff@š™™™™™Ù?ffffffæ?à?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô¿ffffff濚™™™™™ñ¿333333û¿333333ó¿š™™™™™é?333333Ó¿à?ð¿ð?ffffffæ?333333Ó?333333Ó¿ffffffö?š™™™™™é?ð?à?š™™™™™Ù?š™™™™™Ù?ffffffæ?š™™™™™é?333333ó?š™™™™™É¿333333ã?š™™™™™Ù?333333Ó?š™™™™™ñ?333333Ó?š™™™™™Ù?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™É?333333Ó?ffffffæ¿à?š™™™™™Ù?333333Ó?š™™™™™Ù?ÍÌÌÌÌÌô?à?š™™™™™Ù?333333Ó¿333333Ó?333333ã?š™™™™™É?333333ã?š™™™™™é?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™¹?ffffffæ?ÍÌÌÌÌÌì?333333ã?333333ã?ffffffæ?š™™™™™Ù?š™™™™™¹?š™™™™™É?333333Ó?333333ã?ÍÌÌÌÌÌì?š™™™™™É¿ð?ÍÌÌÌÌÌì?333333ó?333333ã?ffffffæ¿à?š™™™™™é?š™™™™™É¿š™™™™™É?ffffffæ?š™™™™™Ù?š™™™™™É?š™™™™™ñ?š™™™™™É?à?333333ã?š™™™™™¹¿à?ø?š™™™™™ñ?ffffffæ?ffffffæ?š™™™™™Ù?à?š™™™™™Ù?ffffffæ?š™™™™™é?ffffffæ?š™™™™™¹?š™™™™™é¿š™™™™™Ù?333333ã?ffffffæ?š™™™™™É?ø?333333Ó¿š™™™™™Ù?à?333333㿚™™™™™Ù?š™™™™™ñ?à?š™™™™™é?333333ã?š™™™™™Ù?š™™™™™É?š™™™™™Ù?ffffffö¿š™™™™™É?š™™™™™Ù?à?à?333333Ó¿333333Ó¿ffffffæ?š™™™™™Ù?333333Ó¿š™™™™™Ù?à?333333ã?š™™™™™é?ffffffæ?ð?ffffffæ?š™™™™™¹?š™™™™™Ù?š™™™™™é?333333Ó?ffffffæ?333333Ó?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™É?ÍÌÌÌÌÌô?ð?à¿ffffffæ?ð?ffffffæ?333333Ó?š™™™™™¹¿333333Ó?333333ã?333333ã?š™™™™™Ù¿ffffffæ?ffffffæ?š™™™™™¹?š™™™™™ñ?š™™™™™é?š™™™™™Ù?š™™™™™¹?333333ã?333333Ó?à¿333333ã?š™™™™™é?333333ã?š™™™™™Ù?à?à?ÍÌÌÌÌÌì?à?333333ã?š™™™™™Ù?ffffffæ¿ffffffö?š™™™™™é?š™™™™™Ù?š™™™™™É?ÍÌÌÌÌÌô?ffffffæ?š™™™™™¹?ffffff濚™™™™™Ù¿ffffffæ?š™™™™™Ù¿š™™™™™Ù?333333Ó?ÍÌÌÌÌÌì?ffffffæ?𿚙™™™™Ù?à?š™™™™™Ù?ð?š™™™™™¹?à?š™™™™™¹¿à¿ð?š™™™™™Ù?à¿333333ã?à?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌ@𿚙™™™™É?à¿à¿š™™™™™É?š™™™™™É?ffffffæ?à?333333㿚™™™™™É?ffffffæ?ffffffæ?333333ó?333333ã¿ø?ÍÌÌÌÌÌì?333333Ó¿ffffffæ?š™™™™™É?à?š™™™™™Ù?à?š™™™™™¹?333333ã?à?ÍÌÌÌÌÌô?333333ã?š™™™™™Ù?࿚™™™™™É?š™™™™™é?š™™™™™é?ffffffæ?ffffffæ?ÍÌÌÌÌÌì?333333Ó?š™™™™™ñ?ð?333333ã?š™™™™™é?333333ó?š™™™™™É?š™™™™™Ù?š™™™™™É¿š™™™™™Ù?š™™™™™ñ?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?333333ó?š™™™™™é?ffffffæ?333333ã?333333ã¿333333ó?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™¹?š™™™™™Ù?š™™™™™é?š™™™™™Ù?ÍÌÌÌÌÌì?333333Ó?333333ã?à¿ffffffæ?ÍÌÌÌÌÌì?à¿333333ã?š™™™™™¹?š™™™™™Ù?š™™™™™Ù?333333Ó?à?š™™™™™¹?ffffffæ?ffffffæ?š™™™™™é¿333333ã?à?š™™™™™É?333333ã?à?š™™™™™É?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?à?ÍÌÌÌÌÌì?333333Ó?à?ÍÌÌÌÌÌô?š™™™™™é?š™™™™™Ù?ffffffæ?š™™™™™É?š™™™™™Ù¿ÍÌÌÌÌÌì?333333Ó?š™™™™™É?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™é?š™™™™™É?à?à?š™™™™™É?ð¿333333ó?š™™™™™É¿à?š™™™™™ñ¿333333ã?ffffffæ?ð?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì?333333ã?333333Ó?ÍÌÌÌÌÌô?333333ã?à?š™™™™™Ù?333333ó?ffffffæ?333333Ó?š™™™™™é?ffffffæ?333333ã?à?ffffff濚™™™™™@à?à?ffffffæ?333333ã?š™™™™™ñ?š™™™™™Ù?à?ÍÌÌÌÌÌô?š™™™™™Ù?š™™™™™É?ÍÌÌÌÌÌì?ð?š™™™™™é?š™™™™™é?333333ã?333333ã?š™™™™™é¿333333Ó¿š™™™™™Ù¿š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™ñ?à?333333ã?ffffffæ¿333333Ó?à?š™™™™™É¿š™™™™™É?ffffffæ?š™™™™™Ù?š™™™™™¹¿š™™™™™Ù¿ÍÌÌÌÌÌì?ð?š™™™™™¹¿š™™™™™ñ?333333ã¿à?à?š™™™™™Ù?333333Ó?333333Ó?ffffffæ?ð?š™™™™™É?ÍÌÌÌÌÌì?š™™™™™ù?333333ã?à?333333Ó?à?333333ó¿š™™™™™¹?ð¿ð¿ffffffæ?à?ffffffæ?333333Ó¿ffffffæ?ffffffæ?à¿333333ã?š™™™™™É?ffffffæ?333333ã?333333Ó?š™™™™™É?ð?333333Ó?à?ffffffö?š™™™™™Ù?ffffffæ?ffffffæ¿ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™é¿ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?333333û?š™™™™™¹?333333ã?333333ã?š™™™™™ñ?333333ã?ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™Ù?ffffffæ?š™™™™™Ù?š™™™™™É?333333Ó¿š™™™™™Ù¿à?š™™™™™é?š™™™™™¹?ð?333333ó?333333ó?š™™™™™é?š™™™™™Ù?ffffffæ?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™¹?š™™™™™¹?ð?333333Ó?333333Ó?ffffffÀš™™™™™ñ¿š™™™™™Ù?333333㿚™™™™™Ù?š™™™™™É?ÍÌÌÌÌÌì¿333333ã¿ÍÌÌÌÌÌ@333333ã?333333ã¿ÍÌÌÌÌÌü?333333Ó?š™™™™™ñ?š™™™™™É?š™™™™™¹?333333ã?ffffffæ¿333333ã?@š™™™™™É?𿚙™™™™¹¿ffffffæ?ÍÌÌÌÌÌô?š™™™™™é?š™™™™™Ù¿à¿333333@ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™Ù?š™™™™™ñ?ffffffæ¿333333Ó¿š™™™™™É?š™™™™™é?333333Ó?333333ã¿333333ã¿ffffffæ?š™™™™™ù¿ð¿à¿ÍÌÌÌÌÌì¿333333ã?ffffffæ?ffffffæ¿ffffffæ?333333ã¿ffffffæ¿ð?š™™™™™É?ð?š™™™™™É?à¿333333ã?333333ã?š™™™™™É?š™™™™™é¿š™™™™™É?š™™™™™Ù¿š™™™™™Ù?š™™™™™É¿à¿333333û?ffffffþ?ffffffæ?š™™™™™é¿333333ã?š™™™™™é?š™™™™™Ù?ffffffæ?š™™™™™¹?ffffffæ¿à?333333Ó¿333333ó?ÍÌÌÌÌÌ@š™™™™™Ù?ffffffæ¿ffffffö?333333㿚™™™™™é¿333333ó?š™™™™™É?333333ó?ø?š™™™™™Ù¿ÍÌÌÌÌÌô?š™™™™™É¿š™™™™™ù¿ffffffþ?š™™™™™ñ¿ÍÌÌÌÌÌô¿š™™™™™é¿ÍÌÌÌÌÌì¿ð?š™™™™™ñ?à¿333333ã?𿚙™™™™Ù?ø¿à?ð?ø?š™™™™™É¿š™™™™™¹?333333Ó?š™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™é?333333ã¿à?ÍÌÌÌÌÌô?ffffffþ?à¿à¿333333Ó¿š™™™™™Ù¿à¿ð?š™™™™™é?š™™™™™É?ffffffö?ÍÌÌÌÌÌô?333333ã?333333Ó?ffffffæ?ÍÌÌÌÌÌì?333333ã?333333Ó?ÍÌÌÌÌÌ@š™™™™™Ù?š™™™™™É¿š™™™™™Ù?š™™™™™é?333333ó?333333ó?ÍÌÌÌÌÌü?š™™™™™¹?š™™™™™É?š™™™™™Ù?š™™™™™é¿ÍÌÌÌÌÌì?à¿ÍÌÌÌÌÌô?š™™™™™é¿š™™™™™Ù?š™™™™™é¿333333ã?š™™™™™É?š™™™™™ù?à¿333333Ó¿333333ã¿à?333333Ó¿š™™™™™é¿ffffffæ¿333333ã?š™™™™™é?š™™™™™Ù¿ffffffþ?š™™™™™é?š™™™™™ñ?333333Ó?š™™™™™é?ÍÌÌÌÌÌô?š™™™™™Ù?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?à?@ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™Ù¿ffffff濚™™™™™Ù¿š™™™™™É¿š™™™™™É?š™™™™™ñ¿ÍÌÌÌÌÌ@ffffffæ?333333ó¿ÍÌÌÌÌÌô?š™™™™™ñ?ffffffæ¿333333ã?à¿ffffff@à?š™™™™™¹¿333333ã?ÍÌÌÌÌÌì¿333333ã¿333333ã?š™™™™™ñ?ffffffæ¿À333333Ó?š™™™™™É?333333ã?ffffffæ?š™™™™™É?š™™™™™ @š™™™™™ñ?333333Ó?ffffffæ?ð¿ÍÌÌÌÌÌ @š™™™™™@š™™™™™@ÍÌÌÌÌÌü¿333333ó¿333333Ó?ð?š™™™™™ù¿š™™™™™ñ?333333Ó¿ð?ffffff濚™™™™™Ù?ffffffö?ÍÌÌÌÌÌì?š™™™™™É?333333ã¿ÍÌÌÌÌÌ쿚™™™™™ñ¿ffffffæ?š™™™™™Ù?š™™™™™ñ?ffffffþ?333333ã?ffffffþ?ffffffö?š™™™™™é?ð?ffffffæ¿333333ó¿333333ã?ÍÌÌÌÌÌì?ð¿333333Ó¿ffffffæ?š™™™™™Ù¿333333ã?š™™™™™ñ?à?š™™™™™¹?š™™™™™é?𿚙™™™™é?@ÍÌÌÌÌÌü?à?š™™™™™Ù?ð?333333Ó?ÍÌÌÌÌÌô?à¿333333ã?ffffffæ¿à?š™™™™™¹?333333Ó¿333333Ó?ø¿333333㿚™™™™™É¿š™™™™™é?š™™™™™ù?ÍÌÌÌÌÌ@š™™™™™Ù?š™™™™™ñ¿333333ӿ࿚™™™™™É?à?š™™™™™É?ffffffæ?š™™™™™Ù¿ffffff濚™™™™™¹¿š™™™™™É?ð?š™™™™™Ù¿ffffffæ?ffffffæ¿ÍÌÌÌÌÌì¿333333ã?š™™™™™@ffffffæ?š™™™™™ À333333ã¿ÍÌÌÌÌÌô¿ð?à?à¿333333ã?ÀÍÌÌÌÌÌ쿚™™™™™Ù¿š™™™™™@ffffff濚™™™™™Ù?333333ó¿à¿333333ó¿š™™™™™ù?š™™™™™É?š™™™™™ñ¿ffffffþ?333333㿚™™™™™É¿ffffffæ?333333Ó¿333333ã?š™™™™™ù?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?333333Ó¿ð?ffffffæ?333333û?333333Ó¿ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü¿ð?š™™™™™ñ¿333333Ó?à?ÍÌÌÌÌÌô¿@333333ã¿333333ã¿ÍÌÌÌÌÌì?š™™™™™ù?š™™™™™É?ffffffö?𿚙™™™™É?333333Ó?ffffffæ?š™™™™™Ù?š™™™™™Ù¿333333ã?ð¿à?ÍÌÌÌÌÌô?š™™™™™Ù¿š™™™™™ù?š™™™™™É? @š™™™™™ù?ffffff@333333ó?333333û?333333㿚™™™™™¹?ÍÌÌÌÌÌ@š™™™™™Ù?ø?š™™™™™¹?š™™™™™é¿ð?à?à?333333ó¿ø?š™™™™™É?333333ã¿ÍÌÌÌÌÌ@š™™™™™ù?š™™™™™É?š™™™™™ù?333333Ó?ð?š™™™™™é¿333333ã?š™™™™™ù¿ð¿š™™™™™ù¿ffffffö?š™™™™™ñ¿ffffffþ?š™™™™™é¿333333@333333ó?ÍÌÌÌÌÌô?@333333ã¿à?ffffffæ?š™™™™™ñ¿ffffffæ?ÍÌÌÌÌÌì?333333@࿚™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌì?333333Ó¿ffffffæ?@ø?à¿ð?333333㿚™™™™™É?𿚙™™™™É?š™™™™™Ù?š™™™™™é?333333ã¿ø¿š™™™™™Ù?š™™™™™¹?ÍÌÌÌÌÌü?ffffff濚™™™™™Ù¿333333ó?š™™™™™ñ?ffffff@333333Ó¿š™™™™™É?à¿ffffffæ?š™™™™™é?ffffffþ¿š™™™™™Ù?ÍÌÌÌÌÌô?š™™™™™é?333333ã¿333333Ó?š™™™™™ñ?ffffffæ?š™™™™™É?333333ó¿š™™™™™é?࿚™™™™™ñ¿333333Ó?š™™™™™É¿ÍÌÌÌÌÌô?à?š™™™™™Ù¿š™™™™™ù?ÍÌÌÌÌÌü¿š™™™™™É?š™™™™™ù?ffffff%Àø¿33333³8@ø¿ÍÌÌÌÌÌÀÍÌÌÌ̬P@ffffff(@ffffff&@333333@ÍÌÌÌÌÌ=@š™™™™™ @333333û¿,Àš™™™™™5@ffffff)@š™™™™™ù?ffffff1@š™™™™™@ÍÌÌÌÌÌ&@3333337ÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ0@ffffff+@ffffff!À#@33333sCÀ33333³<@ÀÍÌÌÌÌÌ3À@FÀ3333338ÀÀ @&Àš™™™™™@š™™™™™)@ffffff@333333@2À33333sGÀÍÌÌÌÌÌ@333333 @ÍÌÌÌÌÌ@š™™™™™2@+À3333330ÀÀ333333Ó?ÍÌÌÌÌÌ!@š™™™™™4ÀÍÌÌÌÌÌÀø?Àš™™™™™;@$@ÍÌÌÌÌÌô¿š™™™™™@,ÀÍÌÌÌÌÌ@š™™™™™8@3333330@š™™™™™@)@ffffff-@ÀB@ÍÌÌÌÌÌ@333333Ó¿2@ÍÌÌÌÌL0@'Àš™™™™™É¿ÍÌÌÌÌÌô?333333&@fffffæ0Àffffff)@333333/@3333333@ÍÌÌÌÌÌ@33333³<@333333(@ffffff,@ffffff-@@ÍÌÌÌÌÌÀ<@ÍÌÌÌÌÌ.@ffffff?@ÍÌÌÌÌÌ'@&@ffffff/Àffffffö¿333333#Àš™™™™™À33333³3Àš™™™™™¹¿333333ó¿ffffffþ?Àffffff Àš™™™™™é¿š™™™™™#ÀÍÌÌÌÌ B@ffffff @ffffff@333333 @ÍÌÌÌÌÌÀš™™™™™Àffffffö?@A@ffffffö¿333333*À@@ÍÌÌÌÌŒ@@333333)@À333333#@ffffff0À333333$Àffffff!@333333 À333333Ó¿€8ÀÍÌÌÌÌÌ@š™™™™™"@"Àš™™™™™@š™™™™™é¿333333@À€4@ÍÌÌÌÌÌ @š™™™™™'@33333³8@@ÍÌÌÌÌŒ@@š™™™™™0@@š™™™™™'@š™™™™™É¿š™™™™™9@ffffff!@š™™™™™+À2@ÍÌÌÌÌÌ À333333 Àš™™™™<@ÍÌÌÌÌÌô¿@š™™™™™!@ÍÌÌÌÌÌ,@ffffffÀš™™™™™)Àš™™™™™@3333330@À#@5@333333"@333333%Àš™™™™™)@ffffff.ÀÍÌÌÌÌÌ @333333Àffffff4@ffffff"@333333&Àffffff'ÀÍÌÌÌÌÌ@333333Àffffff,Àš™™™™™:Àffffff濚™™™™™É?.@"ÀÍÌÌÌÌÌ6@ÍÌÌÌÌÌ&@33333³6À-À33333³8@333333À333333Àš™™™™™ @ÍÌÌÌÌÌ3ÀÍÌÌÌÌÌ!ÀÍÌÌÌÌÌ,@ffffff@ffffffþ¿ÍÌÌÌÌ CÀ0Àffffff2@3333337Àffffff!@333333û?š™™™™™@€D@333333+Àfffff¦F@ÍÌÌÌÌÌ@Àš™™™™™#Àffffff:À@š™™™™C@š™™™™™>@ÍÌÌÌÌÌ%À*ÀÍÌÌÌÌÌ;@ÍÌÌÌÌL2@š™™™™™#@š™™™™™@ÍÌÌÌÌÌÀš™™™™J@š™™™™™@333333À!@š™™™™™É¿ffffffö?š™™™™™5@à¿ffffff,ÀÍÌÌÌÌÌ+@ffffffö?333333#@ffffff!@ÍÌÌÌÌÌ@Àš™™™™™#Àš™™™™™:@š™™™™™É?ÍÌÌÌÌÌÀ333333&@š™™™™=@33333³F@š™™™™™"À3333335@š™™™™™ @ffffff À@$@š™™™™™ Àš™™™™™Àffffff#@333333 @333333$À5@ffffffæ¿ffffff+À333333Àš™™™™8Àffffff)ÀÍÌÌÌÌL8Àš™™™™™?Àffffff4À@š™™™™™"ÀÍÌÌÌÌÌ@ffffff"@š™™™™™¹?š™™™™9Àffffff ÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ&@333333!@š™™™™™@ffffff4@!ÀÍÌÌÌÌÌ$Àffffffæ¿ÍÌÌÌÌÌ1@333333)@ÍÌÌÌÌÌ@@ffffffÀ333333ÀÍÌÌÌÌÌ/ÀÍÌÌÌÌÌ.@333333@&@š™™™™™)À33333³=ÀffffffÀ*ÀÍÌÌÌÌÌ/ÀÍÌÌÌÌÌ"Àfffffæ5@š™™™™™'@ÍÌÌÌÌÌ @ffffff"À33333³4À%@ffffff @%Àffffff)@ffffffB@333333:À33333³3@ffffff!Àš™™™™™#Àffffff@ffffff'À.À3333338À@ÍÌÌÌÌÌ;@ÍÌÌÌÌÌ@(À@ÍÌÌÌÌL0@ÍÌÌÌÌÌì?š™™™™™Àà?š™™™™™ @ÍÌÌÌÌÌÀ'@ÍÌÌÌÌL@@@ffffff @ÀÍÌÌÌÌÌô?š™™™™YB@333333'Àš™™™™™3À3333332Àffffff@333333Àš™™™™GÀÀffffff#@$Àš™™™™™.@š™™™™ÙI@ÍÌÌÌÌÌ@333333À333333Àffffff/@ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ)@333333ó?ÍÌÌÌÌÌ3@ÍÌÌÌÌÌ @à?ø?š™™™™™$@š™™™™™+@333333#@333333,ÀÍÌÌÌÌŒ@@ffffffÀš™™™™™,Àš™™™™™ù?ÍÌÌÌÌÌ!@š™™™™™3À(ÀÍÌÌÌÌÌ5@ÍÌÌÌÌŒGÀ#@ffffffÀ/@2@333333@š™™™™™"Àš™™™™™#@š™™™™™!ÀÀš™™™™™$À€;@ffffff@333333@.Àš™™™™™ñ?ÍÌÌÌÌÌ@*Àffffffæ¿ffffff1ÀÍÌÌÌÌÌ6Àš™™™™™ ÀÍÌÌÌÌÌ#@š™™™™™/@ÍÌÌÌÌÌ@ffffff8@ÍÌÌÌÌÌ#@š™™™™™(À33333³;@ÍÌÌÌÌÌ@333333#@š™™™™™(@€RÀÀEÀffffff@333333=@ÍÌÌÌÌÌ/@fffff¦@@ffffff%@š™™™™™Ù?@.@fffff¦DÀš™™™™™É?ffffff@fffffæ9@333333À*@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ4@ÍÌÌÌÌÌ!@ÍÌÌÌÌÌÀÍÌÌÌÌÌ+@ÍÌÌÌÌÌ/À333333@š™™™™™.À€4@33333³0@š™™™™™:@š™™™™™/@€<@ÀÍÌÌÌÌÌ@š™™™™™ù¿ffffff&@š™™™™™ À/@š™™™™™Àfffffæ;Àffffff@ÍÌÌÌÌÌ @š™™™™™ù?ffffffö¿š™™™™1@š™™™™™A@ffffff@ffffff!À3333339À7Àffffff!@333333ó¿ÍÌÌÌÌÌ.@ffffff&Àffffff>@ÍÌÌÌÌÌ-@ÍÌÌÌÌL2@ÍÌÌÌÌÌ0À333333@ø¿fffffæ0@@ÍÌÌÌÌÌ.Àš™™™™™=@ffffffGÀÍÌÌÌÌÌ'@ffffffÀ333333/ÀÍÌÌÌÌÌô?333333:@ffffff"ÀÍÌÌÌÌÌ*À33333óW@.@š™™™™™@fffffæ1@@@š™™™™™0@333333㿚™™™™™*À€9@š™™™™™@@ÍÌÌÌÌL@@333333@@33333³7Àffffff(@ÍÌÌÌÌÌ7@š™™™™2@333333&Àffffff@-Àš™™™™Ù@@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ+ÀÍÌÌÌÌÌ;ÀÍÌÌÌÌÌ<ÀffffffÀ@333333Àffffff@š™™™™™#@333333ã¿ffffff$@@À33333sJÀffffffþ?š™™™™™ñ?333333"@š™™™™™8@ÍÌÌÌÌÌ2À333333)Àš™™™™™!À333333Àffffff/@ÍÌÌÌÌL3ÀÍÌÌÌÌÌ쿚™™™™™@&À€?@-@333333Ó¿333333Àffffff%Àš™™™™™É?€;@333333-@333333ã¿33333³8@ffffff:@ÍÌÌÌÌLE@333333Àffffff@ÍÌÌÌÌL6@ffffff2@À333333ã?333333Àš™™™™™*@ÍÌÌÌÌL3ÀÍÌÌÌÌL5@333333B@š™™™™™6@š™™™™™ù¿ffffffB@333333"@"@ÍÌÌÌÌL6@333333@࿚™™™™C@š™™™™™'@ÍÌÌÌÌŒB@ÍÌÌÌÌÌ"@š™™™™™*Àš™™™™™@ffffff#Àš™™™™™.Àffffff"Àš™™™™™9Àffffff@ÍÌÌÌÌÌ$@ÀÍÌÌÌÌÌü?š™™™™™&Àø?ÍÌÌÌÌL4ÀffffffC@#@fffffæ3@@š™™™™™Àš™™™™™À333333û¿ÍÌÌÌÌÌ8@š™™™™™ @š™™™™1À333333@33333³1@ÍÌÌÌÌLH@%@ffffffÀÍÌÌÌÌÌ@ffffff3Àš™™™™™,À333333.@333333û¿š™™™™™Ù?333333*À@@333333'@ÀÍÌÌÌÌÌ @ @€>Àš™™™™YC@š™™™™™#@š™™™™™%@€;@333333&@fffff&D@ffffff9@ffffff.@)@ð?33333³<@ø¿Àffffff.@@333333$ÀÍÌÌÌÌL>@ÀÍÌÌÌÌÌ@333333$@ÍÌÌÌÌÌ;@ÍÌÌÌÌÌÀffffff.À333333ó?33333³3@ÍÌÌÌÌÌÀÍÌÌÌÌÌ@.@333333"@333333ÀÍÌÌÌÌL3@€1Àš™™™™™:@š™™™™™ @33333³<@ffffff%@À333333 ÀÍÌÌÌÌÌ@ø?333333Àš™™™™™7Àffffff+@š™™™™™@33333³4@ffffff&ÀÍÌÌÌÌÌ3@ffffff"@š™™™™™7Àš™™™™™)Àš™™™™™0@0@š™™™™™¹?š™™™™™ÀÍÌÌÌÌÌ Àš™™™™™@ffffff#@ÍÌÌÌÌÌ@š™™™™™À333333 À(Àfffffæ1@š™™™™™3Àš™™™™™Àð¿333333#@ÀH@3333331À+À333333@333333ÀÀ3333330ÀÍÌÌÌÌÌ@333333N@ÍÌÌÌÌ B@!ÀÍÌÌÌÌÌÀffffff4@š™™™™YI@33333S@8@ÍÌÌÌÌÌÀ33333ó@@ÍÌÌÌÌÌÀffffffÀ-@(Àffffff @ÍÌÌÌÌL3@š™™™™™@š™™™™3À33333³0@ffffff'@š™™™™™-@ffffff'@333333Ó¿333333+ÀÍÌÌÌÌÌ2À€?@333333 @333333@;@333333@@€M@ @š™™™™:@/@ÍÌÌÌÌÌÀÀš™™™™™-@333333û?š™™™™™)Àš™™™™™ @ÍÌÌÌÌÌ-@333333,ÀÍÌÌÌÌL8@333333@ÍÌÌÌÌÌ#ÀÍÌÌÌÌÌ À3À€8À333333,À,ÀÍÌÌÌÌÌÀ#@š™™™™™Àš™™™™™'@š™™™™™%@ffffff%@333333<Àffffffþ?Àš™™™™™.@š™™™™™"@333333 @33333³6@3ÀÍÌÌÌÌÌ-ÀffffffÀffffff7@33333³8@6@333333&@ffffff$À Àfffffæ3Àffffff0@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ(@ÍÌÌÌÌÌÀÍÌÌÌÌ @Àš™™™™™!Àffffff+À.Àš™™™™™Ù¿fffffæ3@š™™™™2@ø¿ffffff0À.ÀfffffæA@ÍÌÌÌÌÌ @ÍÌÌÌÌ BÀ@š™™™™?@š™™™™™)Àš™™™™9@š™™™™™'ÀÍÌÌÌÌÌÀ333333!À3333331À3333333ÀffffffÀÍÌÌÌÌÌü?ÍÌÌÌÌL?@ffffffþ¿ffffff.À333333À33333³=@@&Àš™™™™™.@š™™™™™é¿À/@€>@š™™™™™@š™™™™1@.@ÍÌÌÌÌÌ@33333ó@@š™™™™™À333333&ÀffffffÀffffffæ?@€:Àfffffæ:Àš™™™™8@š™™™™2À€1@33333³K@ÍÌÌÌÌÌ Àfffff¦A@333333+Àš™™™™™+@š™™™™™1@33333³7@š™™™™™@€:@333333 Àffffffö?š™™™™™ @0@š™™™™™0@š™™™™™@&À€<@ÍÌÌÌÌÌÀÀš™™™™™é¿š™™™™4@ÍÌÌÌÌÌ0À33333³>@33333ó@@fffffæ:Àfffffæ0@ÍÌÌÌÌÌ@š™™™™™"@3333333@š™™™™™<@333333Àš™™™™™4@!ÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌÀ@A@+@ÍÌÌÌÌÌô¿ffffff@333333@333333û¿š™™™™™ @,@š™™™™0ÀÍÌÌÌÌÌÀffffffþ¿fffffæ0@ÍÌÌÌÌÌ$@ffffff*@ÍÌÌÌÌÌ @ @ÍÌÌÌÌÌ7ÀÀA@ø¿ÍÌÌÌÌL5@ÍÌÌÌÌÌ@333333'À333333AÀÍÌÌÌÌÌ)@ffffffG@333333(@€B@ÍÌÌÌÌÌ-@ÍÌÌÌÌL0Àffffff@š™™™™™3@33333³4Àš™™™™™ ÀÍÌÌÌÌÌ$@ÍÌÌÌÌL@@ffffffþ?ÍÌÌÌÌÌ%@ffffff@fffff¦E@ffffff3@ffffff%Àš™™™™6@fffffæ2À@€8À3333337@ÍÌÌÌÌÌ3@33333³>@333333'@š™™™™™"@ÍÌÌÌÌÌô¿š™™™™™ @ÍÌÌÌÌÌ(@ @ÍÌÌÌÌÌÀ3333336@š™™™™™@€J@š™™™™™ @@333333À@33333³5@ffffff*@ffffff@333333û?ÍÌÌÌÌÌ3Àš™™™™™;ÀÍÌÌÌÌÌ%@@ffffff2@!ÀÍÌÌÌÌÌ6@€3@ffffff.@33333³8Àš™™™™™@ÍÌÌÌÌÌÀ€:@333333@ÍÌÌÌÌÌ1À333333D@ÍÌÌÌÌLMÀffffff+@ffffff@š™™™™™Ù?š™™™™™¹?𿚙™™™™Ù¿š™™™™™¹¿š™™™™™É¿š™™™™™¹?š™™™™™¹?ffffff濚™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™É?š™™™™™¹¿š™™™™™¹¿š™™™™™É?š™™™™™É¿š™™™™™¹?333333㿚™™™™™¹¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹?à?š™™™™™É¿š™™™™™¹¿ffffffæ?ÍÌÌÌÌÌì?š™™™™™¹¿š™™™™™Ù?š™™™™™É?š™™™™™¹¿š™™™™™É¿333333Ó?š™™™™™É?š™™™™™É¿š™™™™™Ù?š™™™™™¹?š™™™™™¹?š™™™™™Ù?š™™™™™¹?š™™™™™¹¿š™™™™™Ù?š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™¹¿333333Ó?š™™™™™¹¿š™™™™™Ù?š™™™™™É?333333Ó¿š™™™™™¹¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹?à?333333Ó?š™™™™™Ù?à?š™™™™™Ù¿š™™™™™É?à?š™™™™™É¿333333ã?š™™™™™¹¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹?à?š™™™™™É?š™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™É?ffffffþ?š™™™™™ñ?š™™™™™¹¿š™™™™™é?ffffffþ?š™™™™™¹¿š™™™™™Ù¿à?š™™™™™É?š™™™™™¹?š™™™™™é?š™™™™™É?š™™™™™¹?š™™™™™Ù?š™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™ñ?š™™™™™¹?š™™™™™Ù?š™™™™™É¿333333㿚™™™™™É¿333333Ó?š™™™™™¹¿š™™™™™¹?š™™™™™¹¿ffffffæ?š™™™™™Ù?ð?š™™™™™É?š™™™™™¹¿š™™™™™É?š™™™™™¹?à?š™™™™™¹¿š™™™™™É?š™™™™™¹¿š™™™™™¹¿333333ó¿333333ã?š™™™™™¹¿ffffffæ?ð¿à¿š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿à¿333333Ó?š™™™™™É?š™™™™™É?š™™™™™¹¿š™™™™™¹¿š™™™™™É¿š™™™™™É?333333Ó?š™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™Ù¿š™™™™™Ù?š™™™™™É¿š™™™™™¹¿š™™™™™¹?š™™™™™¹?ffffffæ?š™™™™™¹?š™™™™™É?ÍÌÌÌÌÌì?š™™™™™É?ffffffö?ð?š™™™™™¹?š™™™™™¹?333333Ó?š™™™™™¹¿š™™™™™¹¿š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™¹¿333333ã?ffffffæ?š™™™™™ù?š™™™™™É¿š™™™™™¹?à?š™™™™™¹¿333333Ó?š™™™™™É?š™™™™™Ù?š™™™™™Ù¿š™™™™™Ù?š™™™™™É?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™¹¿ð?š™™™™™Ù?ð?࿚™™™™™Ù?š™™™™™¹¿š™™™™™¹?࿚™™™™™¹?333333ã?š™™™™™Ù¿à?š™™™™™Ù?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹¿š™™™™™É?š™™™™™É?š™™™™™Ù?à?š™™™™™¹?š™™™™™é?š™™™™™¹¿š™™™™™ñ?š™™™™™É¿ffffffæ?š™™™™™Ù?ð?à?333333ã?333333ã?š™™™™™Ù?333333Ó¿š™™™™™É?š™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™¹?ffffffæ?à¿ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™¹?š™™™™™¹?ffffffæ?š™™™™™Ù¿à?š™™™™™¹?š™™™™™Ù¿333333Ó?š™™™™™¹?333333ã?š™™™™™¹¿š™™™™™É?š™™™™™Ù¿à?š™™™™™¹?ffffffæ?š™™™™™¹?ffffffæ?š™™™™™É¿š™™™™™¹?à?š™™™™™Ù¿š™™™™™¹?š™™™™™¹¿š™™™™™É¿š™™™™™¹?š™™™™™É?š™™™™™É?š™™™™™Ù?ÍÌÌÌÌÌô?š™™™™™Ù¿ffffffæ?333333ã?à?ÍÌÌÌÌÌ쿚™™™™™¹¿à?࿚™™™™™¹?333333Ó?333333ã?š™™™™™¹¿333333ã¿ø?š™™™™™É¿š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿ffffff濚™™™™™¹?š™™™™™¹¿à¿š™™™™™É¿ð?ffffffö?š™™™™™Ù¿333333ã?š™™™™™É¿š™™™™™¹?š™™™™™ñ?ffffffæ?š™™™™™É?333333㿚™™™™™Ù?š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?š™™™™™Ù?š™™™™™Ù¿š™™™™™É?š™™™™™¹?à?š™™™™™¹¿333333ã¿333333Ó?333333Ó?ffffff濚™™™™™É?333333ã?333333ã?š™™™™™é?333333Ó?š™™™™™¹¿š™™™™™Ù?333333Ó?ffffffæ?ffffffö?ffffffæ?š™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™Ù?ffffffæ?333333ó?ð?333333ã?š™™™™™Ù?à?à?š™™™™™É?š™™™™™¹¿333333Ó¿333333Ó?333333Ó?š™™™™™¹?ffffffæ?š™™™™™¹¿333333ã?333333Ó?à?š™™™™™@š™™™™™¹?š™™™™™Ù?š™™™™™Ù¿333333Ó?à?ffffffö?333333Ó?š™™™™™É¿š™™™™™É?š™™™™™¹¿š™™™™™É¿š™™™™™¹?ffffffþ¿ð¿š™™™™™É¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹?ÍÌÌÌÌÌì?333333ã?š™™™™™Ù?š™™™™™Ù¿š™™™™™¹?š™™™™™¹?š™™™™™Ù?à?š™™™™™É?ffffffæ¿333333ã?š™™™™™¹¿š™™™™™¹¿333333Ó¿ÍÌÌÌÌÌ쿚™™™™™¹¿š™™™™™Ù?š™™™™™¹?š™™™™™¹?333333Ó¿333333Ó?333333㿚™™™™™¹?š™™™™™Ù¿š™™™™™¹?š™™™™™É¿333333Ó?š™™™™™¹?š™™™™™Ù?š™™™™™É?333333Ó?š™™™™™Ù¿š™™™™™É¿333333Ó?š™™™™™¹?š™™™™™é¿š™™™™™Ù¿š™™™™™É?333333Àš™™™™™À333333Àffffff À333333 À"ÀffffffÀ333333À333333ÀffffffÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀ À'ÀÍÌÌÌÌÌÀÀffffffÀ333333Àš™™™™™À333333ÀÍÌÌÌÌÌÀ333333À333333À333333ÀffffffÀ333333!ÀÀ Àš™™™™™ù¿333333Àš™™™™™ÀÍÌÌÌÌÌ!Àš™™™™™Àš™™™™™À333333Àš™™™™™"ÀffffffÀš™™™™™À333333ÀffffffÀ333333ÀffffffÀ333333À!ÀffffffÀ333333Àš™™™™™!ÀffffffÀš™™™™™ Àš™™™™™ÀÍÌÌÌÌÌÀš™™™™™ÀffffffÀÍÌÌÌÌÌÀÀ333333ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌ"ÀÍÌÌÌÌÌÀffffffÀš™™™™™"ÀÀ333333À333333 Àš™™™™™ÀÍÌÌÌÌÌÀffffffÀš™™™™™ÀffffffÀš™™™™™Àš™™™™™Àš™™™™™ÀffffffÀ333333Àš™™™™™)Àffffff ÀffffffÀÍÌÌÌÌÌÀÀÀÀffffff ÀffffffÀ333333$ÀÀ333333$Àš™™™™™À333333ÀffffffÀÍÌÌÌÌÌÀffffffÀÀffffffÀ"Àš™™™™™ ÀÀÍÌÌÌÌÌÀš™™™™™À!ÀÍÌÌÌÌÌ!Àffffff ÀffffffÀ333333 Àffffff ÀffffffÀffffffÀffffff ÀffffffÀÍÌÌÌÌÌ ÀÀš™™™™™À333333ÀÀ!ÀffffffÀš™™™™™À ÀÀffffffÀÍÌÌÌÌÌ Àš™™™™™ÀffffffÀffffff ÀÀffffffÀÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀffffffÀš™™™™™!ÀÍÌÌÌÌÌÀš™™™™™Àš™™™™™ÀffffffÀÍÌÌÌÌÌ À333333ÀÀffffffÀš™™™™™À333333Àš™™™™™ÀÀÀš™™™™™Àš™™™™™ÀÀÀffffffÀ Àš™™™™™Àš™™™™™ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333À333333Àš™™™™™Àffffffö¿ÀffffffÀš™™™™™"ÀÍÌÌÌÌÌÀÀÀ333333,Àš™™™™™ÀffffffÀš™™™™™&ÀÍÌÌÌÌÌÀÀffffffÀffffffÀÀffffff)ÀffffffÀffffff!ÀÀš™™™™™ÀÍÌÌÌÌÌÀffffffÀ333333À333333Àø¿333333ÀffffffÀš™™™™™Àš™™™™™ÀÀ333333ÀÀÍÌÌÌÌÌÀffffffÀ333333%ÀffffffÀÍÌÌÌÌÌÀ&À333333%À333333ÀffffffÀffffffÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333 Àš™™™™™ÀffffffÀffffffÀ333333$À333333À333333À333333ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀÀ333333ÀffffffÀÍÌÌÌÌÌÀffffff ÀffffffÀš™™™™™#Àš™™™™™ÀffffffÀ333333Àš™™™™™ÀffffffÀÀš™™™™™À333333ÀÀ333333Àš™™™™™À ÀÀÍÌÌÌÌÌ)Àš™™™™™ÀffffffÀÀ333333À333333À333333Àš™™™™™ÀÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌ!À333333ÀffffffÀffffffÀ333333ÀffffffÀ%Àš™™™™™ ÀÍÌÌÌÌÌ!À333333#À333333ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™!ÀÀš™™™™™À#À333333ÀÀš™™™™™#ÀÀÀffffffÀ333333ÀÀffffffÀ ÀÀ333333À333333'Àš™™™™™À333333Àš™™™™™ÀffffffÀš™™™™™ÀÀ333333Àš™™™™™ÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌ!ÀÀ333333ÀÀÀ333333Àš™™™™™ÀÍÌÌÌÌÌÀffffff Àš™™™™™ÀÍÌÌÌÌÌÀš™™™™™Àš™™™™™ÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀffffffÀffffff%À333333ÀÍÌÌÌÌÌÀš™™™™™ÀÀÀffffffÀ333333!Àffffff#ÀffffffÀš™™™™™ù¿ÍÌÌÌÌÌÀffffff!ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌÀš™™™™™Àffffff!Àffffff ÀÍÌÌÌÌÌ#ÀffffffÀffffff,À333333ÀÍÌÌÌÌÌÀÀffffffÀš™™™™™"ÀÍÌÌÌÌÌ ÀffffffÀÀffffffÀÍÌÌÌÌÌÀš™™™™™Àš™™™™™$ÀÍÌÌÌÌÌ Àffffff Àš™™™™™ÀÀ0Àš™™™™™À333333À333333Àffffff"ÀÍÌÌÌÌÌÀffffffÀ333333#ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333Àš™™™™™ ÀÍÌÌÌÌÌÀffffffÀffffffÀÍÌÌÌÌÌÀ333333À!Àš™™™™™ÀffffffÀ333333ó¿333333À333333À333333 Àš™™™™™ À333333À333333À333333Àš™™™™™À333333ÀÀÀš™™™™™À ÀffffffÀffffffÀÀ"ÀÍÌÌÌÌÌü?ÍÌÌÌÌÌü?š™™™™™é?š™™™™™ @333333ã?ÍÌÌÌÌÌô?à?š™™™™™ñ?ÍÌÌÌÌÌô?š™™™™™ù?333333ã?333333ó?š™™™™™ñ?š™™™™™Ù?ÍÌÌÌÌÌì?ð?ð?š™™™™™Ù?ffffffþ?@š™™™™™é?š™™™™™Ù?ffffffþ?š™™™™™ù?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ð?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?ð?ÍÌÌÌÌÌô?ffffffæ?333333ã?ø?ÍÌÌÌÌÌì?ffffffæ?333333@333333û?š™™™™™Ù?š™™™™™ù?ffffffþ?ffffffö?333333ã?ÍÌÌÌÌÌ@à?ÍÌÌÌÌÌì?ffffffþ?ÍÌÌÌÌÌì?š™™™™™é?333333ó?ffffffþ?ffffffæ?ø?š™™™™™ñ?ÍÌÌÌÌÌô?ffffff @š™™™™™é?ffffffæ?š™™™™™Ù?ø?à?š™™™™™ñ? @š™™™™™é?à?š™™™™™ñ?333333ó?ffffffö?ffffffæ?ffffffö?@333333ã?333333ó?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@333333û?ÍÌÌÌÌÌô?š™™™™™Ù?ffffffæ?ø?ð?à?333333ã?ÍÌÌÌÌÌ@à?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ð?š™™™™™Ù?333333@š™™™™™@ffffff@@333333û?š™™™™™ñ?333333û?ÍÌÌÌÌÌô?ø?333333ã?š™™™™™@ÍÌÌÌÌÌô?š™™™™™Ù?333333@ffffffö?à?ÍÌÌÌÌÌô?š™™™™™é?ÍÌÌÌÌÌü?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?333333ó?ffffffö?š™™™™™ñ?ffffffæ?@š™™™™™é?š™™™™™ù?š™™™™™é?ffffffö?à?ffffffö?ffffff@333333 @ÍÌÌÌÌÌô?333333ã?ffffffþ?ð?ÍÌÌÌÌÌü?š™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌü?à?š™™™™™ñ?ÍÌÌÌÌÌô?ð?ø?ffffffæ?š™™™™™ñ?333333ã?333333û?š™™™™™é?š™™™™™ù?333333û?ÍÌÌÌÌÌ@ð?ffffffö?333333@@ÍÌÌÌÌÌü?333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?@ÍÌÌÌÌÌô?333333û?ÍÌÌÌÌÌü?@š™™™™™ù?à?333333ó?š™™™™™Ù?333333ó?ø?š™™™™™Ù?333333ó?333333ó?š™™™™™Ù?ð?ð?ÍÌÌÌÌÌü?ffffffæ?š™™™™™é?ffffffþ?333333ó?333333ã?ffffff@à?333333@ð?à?ÍÌÌÌÌÌô?333333ó?333333ã?@š™™™™™ù?à?@@333333ó?à? @š™™™™™Ù?ffffff@š™™™™™ @333333@333333@ffffffæ?š™™™™™ù?à?š™™™™™é?ffffffæ?ffffff@ffffffæ?333333û?ø?ø?333333ã?ÍÌÌÌÌÌü?ffffffö?ÍÌÌÌÌÌì?ø?ø?ÍÌÌÌÌÌü?ffffffæ?ÍÌÌÌÌÌì?à?333333ã?š™™™™™é?ffffffþ?ð?š™™™™™ñ?333333ó?à?333333ó?ð?333333ã?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ð?š™™™™™Ù?ÍÌÌÌÌÌô?š™™™™™Ù?ÍÌÌÌÌÌô?333333@ÍÌÌÌÌÌì?š™™™™™é?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?š™™™™™ù?š™™™™™Ù?š™™™™™ù?ÍÌÌÌÌÌ@š™™™™™é?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?à?333333@ffffffæ?š™™™™™ñ?ffffffæ?š™™™™™'@à?333333ó?333333ã?š™™™™™ù?ÍÌÌÌÌÌì?ffffffæ?ð?š™™™™™@à?ÍÌÌÌÌÌô?š™™™™™ù?š™™™™™ù?š™™™™™ñ?ffffff@š™™™™™Ù?š™™™™™@333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@ffffff@ð?à?333333ó?š™™™™™ñ?š™™™™™é?@ffffff@š™™™™™ñ?ø?š™™™™™é?š™™™™™ñ?š™™™™™ñ?333333%@ð?ÍÌÌÌÌÌÀš™™™™™ Àš™™™™™À'Àš™™™™™Àffffff(ÀÀÀð¿"À333333ÀÀÍÌÌÌÌÌÀ333333"Àš™™™™™ÀÀÍÌÌÌÌÌ%À333333 À333333À333333À ÀÍÌÌÌÌÌÀÀffffff"Àš™™™™™ ÀÍÌÌÌÌÌÀ333333%ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌÀ333333À333333 Àš™™™™™Àš™™™™™Àš™™™™™Àffffff"Àš™™™™™ÀffffffÀ333333'À333333À333333Àš™™™™™"Àš™™™™™ ÀÀš™™™™™'ÀffffffÀš™™™™™)ÀÀ À333333À333333Àš™™™™™ÀÀÍÌÌÌÌÌÀš™™™™™À!À333333À%À333333%ÀÍÌÌÌÌÌÀ#À333333ÀffffffÀffffffÀš™™™™™!À333333%ÀÍÌÌÌÌÌÀ333333$À#Àš™™™™™é¿ffffffÀ333333À Àš™™™™™Àš™™™™™!À333333ó?ffffffö?@ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ø?š™™™™™@333333ó?à?@333333ã?333333@ @ð?ð?š™™™™™é?333333ã?333333û?š™™™™™é?š™™™™™Ù?ffffff@333333ã?ÍÌÌÌÌÌü?š™™™™™é?š™™™™™é?ffffffæ?333333ã?š™™™™™@333333ã?ð?@à?š™™™™™ñ?š™™™™™é?333333@@š™™™™™Ù¿š™™™™™é¿333333Ó¿à¿ffffffæ¿ffffffö¿š™™™™™ñ¿š™™™™™Ù¿333333㿚™™™™™ñ¿333333ã¿ð¿š™™™™™ñ¿š™™™™™ñ¿333333ã¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ffffff濚™™™™™ñ¿ffffff濚™™™™™ñ¿ð¿à¿š™™™™™¹?š™™™™™É?ffffffö¿333333ã¿ÍÌÌÌÌÌì¿à¿š™™™™™é¿ffffffæ¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿ffffffæ¿ð¿ffffffö¿ÍÌÌÌÌÌô¿333333ã¿ffffffæ¿ffffffæ¿333333ó¿ÍÌÌÌÌÌì¿333333ã¿ÍÌÌÌÌÌô¿333333ó¿à¿à¿ffffffö¿ffffffæ¿ÍÌÌÌÌÌì¿ð¿à¿333333ã¿ð¿333333Ó¿ffffffæ¿ð¿ÍÌÌÌÌÌì¿ø¿š™™™™™ñ¿ð¿333333ã¿333333Ó¿š™™™™™é¿ÍÌÌÌÌÌì¿333333ã¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿à¿333333ó¿ÍÌÌÌÌÌô¿š™™™™™é¿š™™™™™é¿ÍÌÌÌÌÌ쿚™™™™™é¿ÍÌÌÌÌÌô¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™ù¿š™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™Ù¿333333ã¿333333ã¿ð¿333333ó¿š™™™™™é¿š™™™™™é¿š™™™™™ñ¿š™™™™™é¿š™™™™™ñ¿š™™™™™é¿ÍÌÌÌÌÌì¿ffffffæ¿à¿ð¿333333ó¿ÍÌÌÌÌÌ쿚™™™™™ñ¿333333ã¿à¿š™™™™™ñ¿333333Ó¿ÍÌÌÌÌÌì¿333333Ó?ffffffæ¿333333ã¿ð¿ð¿à¿333333㿚™™™™™ñ¿š™™™™™É?333333ã¿à¿ffffff濚™™™™™é¿ffffffæ¿ð¿333333Ó¿333333ã¿ffffffæ¿333333ó¿ÍÌÌÌÌÌì¿à¿ÍÌÌÌÌÌ쿚™™™™™ñ¿ffffff濚™™™™™Ù?333333ó¿ffffff濚™™™™™ñ¿333333ó¿333333㿚™™™™™ñ¿ffffff濚™™™™™ù¿à¿š™™™™™ñ¿š™™™™™é¿à¿ÍÌÌÌÌÌì¿333333ó¿à¿ð¿š™™™™™ñ¿à¿š™™™™™é¿333333ã¿à¿333333ã¿ð¿ÍÌÌÌÌÌ쿚™™™™™é¿š™™™™™é¿ÍÌÌÌÌÌ쿚™™™™™ñ¿ð¿š™™™™™é¿š™™™™™é¿333333㿚™™™™™Ù¿ffffffö¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿333333ã¿ÍÌÌÌÌÌì¿ffffffæ¿à¿š™™™™™É?š™™™™™É¿ffffffæ¿333333Ó?ð¿à¿š™™™™™ñ¿š™™™™™é¿ÍÌÌÌÌÌì¿ffffffæ¿ffffffö¿ÍÌÌÌÌÌì¿ð¿š™™™™™é¿ÍÌÌÌÌÌì¿333333Ó¿š™™™™™¹?333333ã¿ð¿333333ã¿ÍÌÌÌÌÌì¿à¿ffffffæ¿333333ã¿ffffffÀš™™™™™¹?333333Ó¿š™™™™™é¿ÍÌÌÌÌÌì¿333333㿚™™™™™Ù¿ffffffæ¿ffffff濚™™™™™Ù¿š™™™™™é¿š™™™™™é¿ÍÌÌÌÌÌô¿š™™™™™ñ¿š™™™™™é¿š™™™™™ñ¿ð¿ffffff濚™™™™™é¿333333ó¿333333ã¿ÍÌÌÌÌÌì¿333333㿚™™™™™é¿š™™™™™Ù¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ð¿333333㿚™™™™™¹?ffffffö¿ð¿š™™™™™é¿333333㿚™™™™™ñ¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™é¿333333ó¿ÍÌÌÌÌÌô¿š™™™™™ñ¿ð¿ð¿š™™™™™É?333333ӿ𿚙™™™™ñ¿š™™™™™é¿š™™™™™ñ¿ÍÌÌÌÌÌì¿ffffffæ¿ffffffæ¿333333㿚™™™™™Ù?ð¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™é¿š™™™™™ñ¿ffffffö¿ÀÍÌÌÌÌÌì¿333333ã¿333333ã¿333333㿚™™™™™ñ¿ÍÌÌÌÌÌ쿚™™™™™ñ¿š™™™™™é¿333333Ó¿333333ã¿333333Ó?ffffffæ¿333333Ó¿333333ã¿ð¿ffffffö¿à¿š™™™™™é¿333333Ó¿ffffff濚™™™™™é¿à¿ÍÌÌÌÌÌ쿚™™™™™Ù?à¿ffffffæ¿ffffffæ¿à¿à¿ð¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™é¿ffffffæ¿333333㿚™™™™™Ù¿333333ó¿à¿ffffff濚™™™™™Ù¿ð¿ÍÌÌÌÌÌì¿333333ó¿ÍÌÌÌÌÌì¿333333ã¿ffffff濚™™™™™É?ÍÌÌÌÌÌ쿚™™™™™¹?333333ó¿š™™™™™é¿ð¿š™™™™™ñ¿333333ó¿š™™™™™É¿333333ó¿š™™™™™é¿š™™™™™ñ¿ffffffö¿š™™™™™ñ¿ffffffæ¿ð¿ð¿333333ã¿333333㿚™™™™™é¿ø¿à¿š™™™™™ñ¿ffffffæ¿333333ó¿333333ã¿333333ã¿ð¿ð¿333333ó¿à¿š™™™™™é¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ쿚™™™™™ñ¿ffffffæ¿ffffffæ¿ffffffæ¿Àffffffæ¿à?333333Ó¿š™™™™™Ù¿š™™™™™ù¿333333ó¿š™™™™™é¿š™™™™™Ù¿š™™™™™ñ¿ø¿à¿ffffffæ¿333333㿚™™™™™é¿à¿333333ã¿ffffff濚™™™™™ñ¿š™™™™™Ù¿ð¿à¿ffffffæ¿333333㿚™™™™™é¿333333㿚™™™™™Ù¿š™™™™™ñ¿ffffffö¿ffffffæ¿ð¿ÍÌÌÌÌÌô¿š™™™™™é¿š™™™™™é¿à¿ÍÌÌÌÌÌì¿à?š™™™™™ñ¿ÍÌÌÌÌÌì¿à¿ð¿š™™™™™é¿š™™™™™é¿333333㿚™™™™™é¿333333㿚™™™™™é¿333333ó¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿333333㿚™™™™™Ù¿ÍÌÌÌÌÌô?ÍÌÌÌÌÌ쿚™™™™™é¿ð¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™¹¿ÍÌÌÌÌÌì¿ffffff濚™™™™™É¿333333ó¿ffffff濚™™™™™é¿ÍÌÌÌÌÌô¿à¿š™™™™™ñ¿ffffff濚™™™™™ñ¿à¿333333ó¿ÍÌÌÌÌÌô¿š™™™™™ñ¿ffffffæ¿333333ã¿ð¿š™™™™™é¿ffffff濚™™™™™é¿333333ã¿ÍÌÌÌÌÌ쿚™™™™™ñ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿333333㿚™™™™™é¿ffffff濚™™™™™é¿ffffffæ¿333333ã¿ÍÌÌÌÌÌì¿333333㿚™™™™™é¿ÍÌÌÌÌÌì¿à¿š™™™™™¹¿333333㿚™™™™™é¿333333û¿š™™™™™ù¿À333333û¿ø¿333333ÀÍÌÌÌÌÌ쿚™™™™™é¿ffffffþ¿333333ó¿ð¿ÍÌÌÌÌÌü¿333333ã¿ffffffö¿ÀÍÌÌÌÌÌü¿š™™™™™ù¿333333㿚™™™™™Ù¿333333û¿333333Àš™™™™™ñ¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™ù¿ð¿333333û¿š™™™™™Àš™™™™™¹¿333333ó?š™™™™™ñ¿333333ó¿ffffffÀ333333ó¿ÍÌÌÌÌÌì¿333333ó¿ÍÌÌÌÌÌô¿333333 ÀffffffÀš™™™™™ñ¿š™™™™™ñ¿ÍÌÌÌÌÌì¿ffffffö¿ffffffö¿ð¿š™™™™™ñ¿333333ó¿š™™™™™Ù¿ÍÌÌÌÌÌì¿à¿š™™™™™ñ¿ÍÌÌÌÌÌô¿ð¿ÍÌÌÌÌÌü¿à¿ffffffÀø¿333333㿚™™™™™É¿ÍÌÌÌÌÌô¿ffffffæ¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌÀffffff濚™™™™™¹¿À333333ó¿ffffffö¿333333ÀffffffÀ333333ó¿ø¿š™™™™™À333333À333333ó?ffffffö¿š™™™™™é¿ffffffæ¿ÍÌÌÌÌÌÀš™™™™™Ù¿š™™™™™Ù¿š™™™™™ù¿333333ó?ffffffö?333333ã¿@ø?𿚙™™™™é?ffffffþ¿ð¿ÀÍÌÌÌÌÌü¿ffffffö¿Àš™™™™™ñ¿š™™™™™é?ð¿ffffff濚™™™™™é¿ÍÌÌÌÌÌÀš™™™™™ñ¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™Àð?ffffffæ?ø¿ffffffö¿ø¿333333À333333û¿š™™™™™Àffffffþ¿š™™™™™Àš™™™™™ù¿š™™™™™é¿ÍÌÌÌÌÌô¿š™™™™™é¿ø¿š™™™™™ù¿š™™™™™é¿š™™™™™ÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿ð¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀš™™™™™é¿333333ó¿ Àš™™™™™Àš™™™™™ù¿ffffffÀš™™™™™ñ¿ÍÌÌÌÌÌì¿ø¿ffffffþ¿ð¿ÍÌÌÌÌÌü¿š™™™™™ÀÀš™™™™™Àš™™™™™Àffffffþ¿š™™™™™ñ¿ø¿ø¿ffffffþ¿ÍÌÌÌÌÌÀø¿ÍÌÌÌÌÌÀffffffÀÍÌÌÌÌÌô¿à¿333333û¿333333ó¿ffffffÀ333333ã¿333333ÀÍÌÌÌÌÌÀffffffþ¿à¿ffffffö¿333333 ÀÍÌÌÌÌÌì¿333333㿚™™™™™Ù¿š™™™™™ñ¿ÍÌÌÌÌÌÀð¿ð¿š™™™™™ñ¿ffffffö¿ÀÍÌÌÌÌÌü¿š™™™™™Ù?ÍÌÌÌÌÌì¿333333À333333Ó¿333333Ó?ÍÌÌÌÌÌì?333333Ó¿à¿333333ã¿333333ã?333333û?333333û¿ffffff濚™™™™™ñ¿333333ó?ffffffö¿333333ó¿š™™™™™ù¿ffffffö¿š™™™™™Ù?ffffffÀš™™™™™É?š™™™™™é¿ø?ÍÌÌÌÌÌÀ333333ó¿ÍÌÌÌÌÌÀÍÌÌÌÌÌü¿333333ó?š™™™™™ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀ333333Ó?333333ó¿333333Ó¿š™™™™™Àð?š™™™™™é¿à¿š™™™™™Ù?ÍÌÌÌÌÌô¿ø¿ffffffþ¿ffffffþ¿š™™™™™ñ¿à¿ø¿ø¿333333ÀÍÌÌÌÌÌô¿š™™™™™é¿à?333333À333333Ó¿Àš™™™™™é?ÍÌÌÌÌÌü¿ÍÌÌÌÌÌì?š™™™™™é¿š™™™™™À𿚙™™™™ù?ÍÌÌÌÌÌÀ333333ã?š™™™™™Àš™™™™™É?š™™™™™ù¿333333ó¿333333ã¿ÍÌÌÌÌÌô¿À࿚™™™™™Ù¿š™™™™™é?ÍÌÌÌÌÌô¿333333ÀÍÌÌÌÌÌü?š™™™™™É?š™™™™™Ù¿ffffffæ¿ÍÌÌÌÌÌì¿À333333Àffffffæ¿ø¿333333ã¿333333ó¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌü¿Àš™™™™™ù¿š™™™™™ÀÀffffff濚™™™™™é¿š™™™™™Ù¿š™™™™™Àš™™™™™é¿ÍÌÌÌÌÌü¿ffffffþ¿ffffffæ¿ffffffþ¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ø¿š™™™™™Ù¿ø¿ø¿ffffffæ¿ÍÌÌÌÌÌô¿à¿ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿À333333ã?š™™™™™ù¿ffffffþ?ffffffÀø¿ÍÌÌÌÌÌÀ333333Ó¿ffffffÀð¿333333ã¿ÍÌÌÌÌÌì?333333À333333ã¿ffffffö¿š™™™™™ù?À333333Àš™™™™™ñ¿333333û¿š™™™™™ñ¿ffffffþ¿š™™™™™é¿333333û¿333333û¿š™™™™™ñ¿ð¿š™™™™™¹¿š™™™™™ñ¿333333Ó?š™™™™™ù?ffffffþ¿š™™™™™ñ¿ð¿š™™™™™Ù¿š™™™™™¹¿ÍÌÌÌÌÌô?Àà¿ffffffþ¿ffffffö¿ffffff ÀÍÌÌÌÌÌü¿ffffffæ¿333333ó¿Àš™™™™™É¿ð¿333333ó¿ð¿ð¿ÀÀ333333û¿333333ó¿š™™™™™é¿š™™™™™é¿ø¿š™™™™™Ù?š™™™™™Ù¿ffffff濚™™™™™ñ?333333ã¿ÍÌÌÌÌÌô¿333333Ó?ÍÌÌÌÌÌÀÍÌÌÌÌÌô?333333Ó¿ð¿ÍÌÌÌÌÌì¿ffffffö¿š™™™™™ù¿š™™™™™Àš™™™™™Ù?333333㿚™™™™™Ù¿ÍÌÌÌÌÌì¿@333333㿚™™™™™É¿ÍÌÌÌÌÌÀš™™™™™ Àš™™™™™É?ÍÌÌÌÌÌì¿333333ó¿š™™™™™ÀÀ333333Ó¿ÍÌÌÌÌÌô?ffffffæ¿ð¿ÍÌÌÌÌÌÀð¿ð¿š™™™™™ù¿š™™™™™@š™™™™™é?š™™™™™ñ¿š™™™™™ À333333ó¿ffffffþ¿š™™™™™É¿333333ó?ffffffþ¿ø¿š™™™™™ñ¿ÍÌÌÌÌÌô¿š™™™™™ÀffffffÀš™™™™™É¿À333333ÀffffffÀffffffæ¿333333Àš™™™™™É¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌì?ffffffþ¿ÍÌÌÌÌÌ쿚™™™™™É¿ffffff濚™™™™™¹?ÍÌÌÌÌÌô¿š™™™™™Àffffffö?࿚™™™™™À333333û¿š™™™™™ ÀffffffÀø¿ÍÌÌÌÌÌì¿333333Ó¿ø¿333333ó¿š™™™™™ñ¿š™™™™™Àš™™™™™É¿ÍÌÌÌÌÌü¿ð¿š™™™™™À333333û¿à?ð¿ffffffþ¿333333ó¿ð¿à¿Àø¿Àffffffæ¿333333û¿š™™™™™ñ¿ÍÌÌÌÌÌÀ333333ó¿333333$Àš™™™™™.À Àffffff7ÀÀ333333,Àffffff#À333333*ÀÀ/À333333"ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌ$Àffffff6ÀGÀ'ÀÍÌÌÌÌÌ+Àffffff%À&ÀÍÌÌÌÌÌ4ÀÍÌÌÌÌÌ+Àš™™™™™(Àš™™™™™6À3333332Àfffffæ8À3333335Àš™™™™™$Àš™™™™0À;À(À333333û¿š™™™™™-À€0À333333>À$À7ÀÍÌÌÌÌÌ(À333333&À5À333333Àffffff(À3333332Àffffff*À€=Àš™™™™™$Àfffffæ0À3333332À3333335À'ÀÍÌÌÌÌL4Àš™™™™™&Àffffff!Àffffff4À33333s@À.À€:À€4ÀÍÌÌÌÌÌ(Àš™™™™™.Àffffff6À333333㿚™™™™™+À333333&À3Àš™™™™ÙAÀš™™™™™ÀÍÌÌÌÌÌ!ÀÍÌÌÌÌL6Àš™™™™™3Àš™™™™™Àš™™™™™6Àš™™™™™0À333333)À+Àfffffæ5ÀÍÌÌÌÌÌ#Àš™™™™™é¿333333.Àš™™™™1À2À33333³7À,ÀÍÌÌÌÌÌ&Àš™™™™™é¿ÍÌÌÌÌL2À€2Àš™™™™BÀ333333'ÀÍÌÌÌÌÌ1Àš™™™™CÀš™™™™™À3333330ÀÍÌÌÌÌÌÀffffff&Àffffff @333333À333333,ÀÍÌÌÌÌÌ;ÀÍÌÌÌÌÌÀ333333<À333333 À33333óBÀffffffþ?fffff&@Àš™™™™™0Àffffff:À$À33333³2À33333³2ÀÍÌÌÌÌL5Àffffff*À333333À)Àš™™™™™/Àš™™™™3ÀÀ333333.À333333#ÀÀ€4Àš™™™™7ÀÀš™™™™™À333333 ÀÀ33333³0Àfffffæ0À333333*ÀÍÌÌÌÌÌ&Àš™™™™ÙBÀ333333-Àš™™™™3ÀÍÌÌÌÌÌ,Àš™™™™™.À'Àffffff=Àfffff&@ÀÀš™™™™™?Àffffff'Àffffff>À333333À333333"Àš™™™™™AÀÍÌÌÌÌÌÀš™™™™™À"Àffffff,ÀffffffÀš™™™™™#À#Àš™™™™™)Àš™™™™™ñ¿š™™™™™"À333333+À333333/Àš™™™™™8Àš™™™™™)ÀÍÌÌÌÌŒ@Àš™™™™™#ÀÍÌÌÌÌL0ÀÍÌÌÌÌÌ1Àš™™™™7À333333À3333335ÀÍÌÌÌÌÌ/Àš™™™™™é¿š™™™™™%Àffffff&À/ÀÍÌÌÌÌÌ0ÀÍÌÌÌÌÌ Àš™™™™™+Àš™™™™5À333333)À(Àš™™™™8À333333/Àfffffæ6À33333³4À33333³8ÀÍÌÌÌÌÌ,ÀÍÌÌÌÌÌ(À€0Àffffff'ÀÍÌÌÌÌÌÀ'À333333Àffffff6À33333sEÀ𿚙™™™1Àš™™™™™/Àš™™™™8Àš™™™™1À333333Àš™™™™2ÀÀ333333/ÀOÀffffff*À333333/À333333CÀÍÌÌÌÌÌ,À0À1À333333ÀÍÌÌÌÌÌDÀÍÌÌÌÌLDÀš™™™™™/ÀÍÌÌÌÌÌÀ333333û¿ÍÌÌÌÌÌÀš™™™™™>ÀÍÌÌÌÌÌÀ$Àš™™™™™1ÀÍÌÌÌÌL0Àffffff濚™™™™™-ÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌ*À€4Àš™™™™™ Àš™™™™1Àš™™™™™À/À333333.À333333EÀfffffæ5ÀÍÌÌÌÌL3À333333*ÀÍÌÌÌÌÌCÀš™™™™™=À2Àffffff4Àffffff"À€9Àfffff&AÀš™™™™™Àffffff1ÀÍÌÌÌÌÌ(À'ÀÍÌÌÌÌL3À'ÀÍÌÌÌÌÌ<Àffffff7Àffffff$À ÀÍÌÌÌÌÌÀffffff/Àš™™™™™É?+ÀffffffÀš™™™™™'À€7À333333ÀÍÌÌÌÌÌ(Àš™™™™™5Àfffffæ6Àffffff À€5Àš™™™™™-Àš™™™™™/Àffffff'À333333$À333333ÀÍÌÌÌÌÌÀš™™™™™'À+ÀÍÌÌÌÌÌ*À333333"ÀffffffÀ333333-Àš™™™™™5Àš™™™™™DÀfffffæ1ÀÍÌÌÌÌÌ<À€0Àffffff*Àš™™™™™&Àffffff Àš™™™™™#Àš™™™™™#ÀÀÍÌÌÌÌL5Àffffff%À333333 ÀÍÌÌÌÌL0À333333,À5À%ÀÍÌÌÌÌÌÀfffffæ1ÀÍÌÌÌÌŒEÀfffffæBÀÍÌÌÌÌÌ!@fffffæ3À333333:Àffffff Àffffff5ÀffffffÀš™™™™™Àš™™™™™!Àffffff1À333333+Àš™™™™<ÀCÀÍÌÌÌÌÌ!ÀÍÌÌÌÌÌ.Àš™™™™™AÀš™™™™™ Àffffff@ÍÌÌÌÌÌ,Àš™™™™™(À333333@*ÀÍÌÌÌÌÌ"Àš™™™™™ÀÍÌÌÌÌÌü¿333333ÀÍÌÌÌÌL0À333333GÀø?ÍÌÌÌÌÌ-Àffffff*À333333ÀÍÌÌÌÌÌ:À333333$Àš™™™™™,Àš™™™™:À5Àfffffæ2À333333,À333333'Àš™™™™™À€3ÀÍÌÌÌÌÌ/Àffffff5Àš™™™™™,ÀÍÌÌÌÌÌ$À333333$À333333+Àffffff(ÀÍÌÌÌÌÌ(Àffffff0À333333+ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ&Àš™™™™™5ÀÍÌÌÌÌÌ*À€5Àš™™™™™#À@ffffff'Àš™™™™™À€7À.Àfffff¦AÀš™™™™AÀ3333337ÀÍÌÌÌÌÌ<À'À333333#Àš™™™™8À333333ÀÍÌÌÌÌÌ5ÀffffffÀ333333)ÀEÀš™™™™™ À€4Àffffff3À3333337À)À@À3333333Àð¿333333ó¿ffffff&Àš™™™™:Àfffffæ2À€2Àš™™™™™?Àš™™™™™!À3333336À XÀ33333³5ÀÍÌÌÌÌÌ À33333³1ÀÍÌÌÌÌÌ3Àš™™™™8ÀffffffÀ33333s@ÀÍÌÌÌÌÌ,ÀÀffffff"Àffffff!À7ÀÍÌÌÌÌÌ/À33333s@ÀÍÌÌÌÌL:@ÍÌÌÌÌÌ;ÀÍÌÌÌÌÌ&Àffffff4ÀPÀ)À€:À333333:ÀÍÌÌÌÌÌ0ÀÍÌÌÌÌŒ@Àš™™™™™1À33333³6Àffffff+Àfffffæ7Àš™™™™™À333333 Àš™™™™™'À€1À33333³5ÀÍÌÌÌÌL5À333333 À%À33333³6À333333+ÀÍÌÌÌÌÌ@3333331Àš™™™™™,Àš™™™™™Àš™™™™™)À333333À333333@333333'À<ÀÍÌÌÌÌÌ2ÀÍÌÌÌÌÌAÀÍÌÌÌÌÌ1ÀÍÌÌÌÌÌ,Àš™™™™™1À/Àš™™™™™Àš™™™™™ ÀÍÌÌÌÌÌ%ÀffffffÀ1ÀffffffÀš™™™™™,Àš™™™™™2@;À3333334À6À€9À€<ÀÍÌÌÌÌÌAÀÍÌÌÌÌÌ1À€CÀffffff-Àfffffæ1Àffffff4Àš™™™™8À/Àš™™™™7À333333-À?Àš™™™™™MÀÍÌÌÌÌÌ8ÀÍÌÌÌÌL:À333333,Àš™™™™™*Àfffff¦@ÀDÀš™™™™™2Àffffff>ÀÍÌÌÌÌÌ6À33333³AÀffffff?À3333336Àš™™™™™@À=ÀÍÌÌÌÌÌ@%À6Àfffff&CÀ33333³BÀffffff.Àfffffæ>À3333336Àš™™™™ÙDÀš™™™™ÙDÀ(Àš™™™™2À33333³7À5ÀÍÌÌÌÌ CÀ€0ÀÍÌÌÌÌÌ6ÀÍÌÌÌÌÌ9Àš™™™™7À3333331Àš™™™™7ÀÍÌÌÌÌL2ÀÍÌÌÌÌÌ3À333333;À33333sEÀfffffæ1Àš™™™™™GÀÍÌÌÌÌÌ=ÀÍÌÌÌÌL0À33333³0Àš™™™™@À333333À€@ÀÍÌÌÌÌÌ9ÀÀBÀÍÌÌÌÌÌIÀ&ÀÍÌÌÌÌÌ"ÀffffffDÀ€;Àš™™™™™,Àš™™™™YEÀÍÌÌÌÌÌ@À€4ÀÍÌÌÌÌL8À333333EÀ@@Àffffff@333333.ÀÍÌÌÌÌL:Àš™™™™™6À33333³>À333333<Àš™™™™™+ÀffffffÀffffff>À À;Àffffff3À333333!@3333334ÀÍÌÌÌÌÌ/ÀÍÌÌÌÌÌ"À3333334À3333332ÀÍÌÌÌÌÌ'Àš™™™™™5Àfffffæ7Àfffff¦GÀ333333+Àffffff5Àš™™™™8ÀfffffæDÀ333333û¿fffff&MÀ3333337À€;Àš™™™™™)À@AÀÍÌÌÌÌÌ!À-Àfffffæ5Àffffff2Àš™™™™6Àfffff&EÀš™™™™™>ÀÍÌÌÌÌÌ6À33333³<À9À2À33333³9ÀÍÌÌÌÌÌ=Àffffff%Àš™™™™™,À333333+À Àš™™™™YAÀfffffæ5ÀÍÌÌÌÌL4À2Àfffff¦GÀ€>À€7ÀÍÌÌÌÌL6ÀÍÌÌÌÌlPÀÍÌÌÌÌL;ÀÍÌÌÌÌLDÀš™™™™IÀš™™™™™'ÀÍÌÌÌÌLBÀ3333336À33333sEÀfffffæ1À€7Àfffff&JÀ33333³6À€5Àffffff8À€;Àffffff)À4ÀÍÌÌÌÌL4ÀÍÌÌÌÌÌ:Àfffffæ1À2ÀÍÌÌÌÌL<À333333AÀÍÌÌÌÌÌ?ÀÍÌÌÌÌÌ/ÀHÀš™™™™3ÀÍÌÌÌÌŒAÀfffffæ4ÀÍÌÌÌÌ GÀffffff5ÀÍÌÌÌÌ AÀ€2Àffffff#Àfffff¦CÀffffff0ÀÍÌÌÌÌL2ÀÍÌÌÌÌÌ2ÀÍÌÌÌÌÌ(Àš™™™™™;À€;À2Àš™™™™2À@@Àš™™™™™9ÀAÀš™™™™1ÀÍÌÌÌÌL?ÀÀAÀ333333.Àffffff-Àš™™™™™ÀffffffÀ333333+À"Àfffffæ0À3333336Àffffff(À€5À33333³7Àffffff#ÀÍÌÌÌÌL:Àffffff*Àš™™™™™<À33333³0Àš™™™™™(ÀffffffSÀÍÌÌÌÌÌ&Àš™™™™™3À3333330ÀÍÌÌÌÌ AÀÍÌÌÌÌÌ9Àfffff¦@À3333334Àš™™™™™8ÀÍÌÌÌÌLKÀš™™™™YAÀfffff¦BÀ3333331ÀÀÀEÀ333333Àfffffæ8Àffffff"Àfffffæ3ÀffffffÀš™™™™™'Àffffff-À1Àfffffæ9À@AÀ/À33333³5Àfffffæ4À33333³9Àfffff&AÀš™™™™YIÀÍÌÌÌÌL;À333333/Àfffff&BÀš™™™™™DÀÍÌÌÌÌÌIÀ333333 Àš™™™™@ÀffffffÀ33333³?ÀÀGÀš™™™™™,Àš™™™™™é?š™™™™™=Àš™™™™™À333333BÀ#À33333sDÀš™™™™™=ÀÍÌÌÌÌÌ)Àš™™™™™&ÀÍÌÌÌÌÌ2À3333332ÀÀffffffÀffffff*Àffffff?Àš™™™™™ Àš™™™™™ Àffffff0Àfffffæ9Àffffff;ÀÍÌÌÌÌÌ3Àš™™™™DÀ€2À€8ÀÍÌÌÌÌÌ+Àffffff3À33333³5À€4Àš™™™™™:À8À€AÀš™™™™7Àffffff0Àš™™™™™2Àffffff8À33333³LÀ3333336À€DÀfffffæ;Àš™™™™2Àfffffæ8ÀÍÌÌÌÌL2À333333-À3333335ÀÍÌÌÌÌÌÀ33333³=À3333335Àš™™™™™'Àš™™™™™9Àš™™™™™8ÀÍÌÌÌÌÌBÀ.Àffffff3ÀÍÌÌÌÌÌ#ÀJÀ333333*À333333.Àfffffæ<Àš™™™™ÙDÀÍÌÌÌÌÌ$À€BÀš™™™™™1Àš™™™™™#Àš™™™™™À€BÀš™™™™™3À@BÀ333333:À€8À€AÀ333333EÀ€4ÀÍÌÌÌÌÌü¿fffffæ:Àffffff1À333333"À€7Àffffff0À33333³0ÀÀ,ÀÍÌÌÌÌÌ-Àfffffæ:Àffffff+À3333335ÀÍÌÌÌÌÌ1ÀÍÌÌÌÌÌÀš™™™™™<À@€=Àfffffæ<ÀÍÌÌÌÌLAÀÍÌÌÌÌÌ;ÀfffffæEÀÍÌÌÌÌÌCÀš™™™™™À33333³9À33333³AÀffffff6Àfffffæ3ÀÍÌÌÌÌL2Àš™™™™0Àš™™™™™3À333333?À3333339Àš™™™™™<À33333³5ÀÀ0Àffffff=Àffffff"ÀÍÌÌÌÌL8Àffffff-ÀÍÌÌÌÌÌ"@333333.À0ÀÍÌÌÌÌL6À33333³=À€9Àš™™™™™AÀ?Àfffff&AÀÍÌÌÌÌL5ÀÍÌÌÌÌÌ3À33333óCÀÍÌÌÌÌÌô¿333333>ÀÍÌÌÌÌÌ%Àš™™™™2ÀÍÌÌÌÌÌ(À'Àš™™™™™5Àfffff¦AÀJÀ333333"Àš™™™™™¹?ÍÌÌÌÌÌ-ÀÍÌÌÌÌ AÀ333333)À333333À@=Àfffffæ7ÀffffffDÀBÀš™™™™™-À333333>À333333(À/Àš™™™™™/À33333óEÀ;Àš™™™™YAÀ333333Àffffff-Àš™™™™™=À0Àš™™™™™/À€1Àš™™™™™CÀfffff&CÀ333333AÀffffff%@@GÀÍÌÌÌÌÌ<À:ÀÍÌÌÌ̬TÀš™™™™™,ÀÍÌÌÌÌÌ>À333333<ÀÍÌÌÌÌL8À3333339À€>À;À333333/Àš™™™™Y@À333333ÀÍÌÌÌÌÌ/Àš™™™™™=À333333"ÀÍÌÌÌÌÌ8Àš™™™™YBÀ33333³7À333333IÀš™™™™ÙCÀÍÌÌÌÌL6À333333ó¿ÍÌÌÌÌL3Àfffffæ7À3À4ÀÍÌÌÌÌÌ:Àffffff @3333339À333333AÀš™™™™AÀfffffæFÀffffff$ÀÍÌÌÌÌÌ3À333333@À33333³6Àffffff(À%Àffffff?Àffffff/Àš™™™™™>ÀÍÌÌÌÌÌ$Àfffffæ;À%@fffffæEÀš™™™™;À333333Ó¿à?ÍÌÌÌÌÌì?š™™™™™¹¿à?š™™™™™É?š™™™™™¹?à¿à¿š™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™¹¿à¿333333Ó¿š™™™™™¹¿š™™™™™É?à?š™™™™™¹?333333Ó?š™™™™™¹¿ÍÌÌÌÌÌì¿333333Ó?ÍÌÌÌÌÌì¿ffffffö¿š™™™™™É¿333333㿚™™™™™¹¿š™™™™™Ù¿š™™™™™Ù?š™™™™™é¿š™™™™™¹¿š™™™™™¹?š™™™™™É¿ffffff濚™™™™™Ù¿š™™™™™É?š™™™™™é¿ffffff濚™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™¹?333333Ó?333333Ó¿š™™™™™É¿š™™™™™É¿à?š™™™™™¹?š™™™™™Ù¿333333Ó¿333333㿚™™™™™É¿333333ó¿š™™™™™¹?333333㿚™™™™™¹?š™™™™™¹¿à?š™™™™™É?333333Ó¿š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹¿š™™™™™É¿ffffff濚™™™™™¹?š™™™™™Ù¿š™™™™™ñ?ÍÌÌÌÌÌÀš™™™™™¹?࿚™™™™™Ù?š™™™™™¹?š™™™™™¹?š™™™™™¹¿ð¿š™™™™™¹?࿚™™™™™¹?š™™™™™É¿333333ã?࿚™™™™™¹¿š™™™™™É?š™™™™™É¿š™™™™™É?š™™™™™É?š™™™™™Ù¿š™™™™™É¿ffffffæ¿333333Ó¿š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?š™™™™™É?à¿333333ã¿ffffffæ¿333333㿚™™™™™¹?333333㿚™™™™™É?ÍÌÌÌÌÌ@š™™™™™¹?š™™™™™Ù¿š™™™™™¹?333333㿚™™™™™¹?333333Ó¿ffffffæ?š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™¹¿à¿š™™™™™¹?333333ã?š™™™™™¹¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿333333㿚™™™™™Ù¿š™™™™™¹¿š™™™™™É¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?à¿333333ã¿À333333Ó¿333333㿚™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™¹?333333Ó¿ÍÌÌÌÌÌô?š™™™™™Ù¿š™™™™™¹¿333333ó¿š™™™™™É¿š™™™™™Ù?333333ã¿ffffff濚™™™™™¹?š™™™™™¹?š™™™™™É¿ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™¹?333333Ó?333333Ó¿š™™™™™Ù?š™™™™™¹?š™™™™™É¿š™™™™™¹?š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?333333ã¿333333㿚™™™™™Ù?š™™™™™Ù¿à¿333333ó¿š™™™™™¹¿à¿ffffff濚™™™™™¹¿š™™™™™¹¿333333ã¿333333Ó¿š™™™™™É¿à¿š™™™™™É?š™™™™™É¿333333Ó¿š™™™™™é¿š™™™™™é¿ð¿š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹¿ÍÌÌÌÌÌì¿333333ã¿à¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™¹?333333Ó¿š™™™™™É?333333ã¿333333Ó¿333333Ó¿333333Ó¿š™™™™™É¿š™™™™™¹¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹¿š™™™™™Ù¿š™™™™™¹¿ffffff濚™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™É¿š™™™™™É?š™™™™™é¿š™™™™™É¿š™™™™™¹?š™™™™™Ù?333333Ó¿š™™™™™¹?𿚙™™™™Ù?š™™™™™¹¿š™™™™™Ù¿à?š™™™™™¹¿333333㿚™™™™™Ù¿š™™™™™É?333333ã?š™™™™™Ù?š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™É?š™™™™™¹?š™™™™™¹¿š™™™™™¹?ffffffæ?š™™™™™É¿333333㿚™™™™™Ù¿š™™™™™Ù¿ð¿š™™™™™¹¿š™™™™™¹¿ÍÌÌÌÌÌì¿à¿š™™™™™Ù¿š™™™™™¹?š™™™™™É?ÍÌÌÌÌÌì¿ffffffæ¿333333Ó?š™™™™™É?š™™™™™Ù¿š™™™™™¹¿š™™™™™É?š™™™™™¹?333333Ó¿š™™™™™¹?š™™™™™¹?š™™™™™¹¿à¿š™™™™™Ù?š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™¹¿š™™™™™é¿š™™™™™¹¿š™™™™™ñ¿š™™™™™É?š™™™™™É¿ÍÌÌÌÌÌì¿333333Ó¿š™™™™™¹?333333Ó¿š™™™™™¹¿š™™™™™ñ¿š™™™™™¹?ÍÌÌÌÌÌì¿ffffff濚™™™™™Ù¿à¿š™™™™™¹¿ffffff濚™™™™™Ù¿š™™™™™¹?333333ã?š™™™™™Ù¿333333ã?š™™™™™É¿š™™™™™É?š™™™™™¹?š™™™™™É¿š™™™™™É?333333Ó¿ffffffÀš™™™™™ù¿š™™™™™¹¿š™™™™™¹?333333Ó¿š™™™™™É¿š™™™™™¹¿333333ã¿à¿š™™™™™É?ÍÌÌÌÌÌì¿333333Ó¿333333㿚™™™™™É?š™™™™™Ù?š™™™™™ñ?à¿333333Ó¿š™™™™™Ù¿š™™™™™É¿š™™™™™¹¿š™™™™™é¿š™™™™™¹¿š™™™™™É¿š™™™™™É¿à?š™™™™™É¿š™™™™™Ù?š™™™™™¹?333333ӿ࿚™™™™™¹?ø¿š™™™™™Ù¿à?š™™™™™Ù¿333333ó?š™™™™™¹?š™™™™™É¿š™™™™™É?333333Ó?š™™™™™¹¿š™™™™™É?š™™™™™É?à?ffffffæ¿333333ã¿ffffffæ?š™™™™™Ù¿ffffff濚™™™™™É¿š™™™™™É¿ÍÌÌÌÌÌì¿à¿à¿333333Ó¿ÍÌÌÌÌÌÀš™™™™™é¿333333ã¿ÍÌÌÌÌÌÀš™™™™™Ù¿333333 Àà¿ð¿š™™™™™ñ¿333333ã¿ð¿ð¿š™™™™™Ù¿333333ã¿333333ã¿333333û¿à¿ÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀ333333Àà¿à¿333333ã¿ffffffæ¿à¿333333ã¿ffffffÀš™™™™™ù¿ffffffþ¿ffffff Àffffffö¿333333㿚™™™™™Ù¿š™™™™™Ù¿ffffffæ¿à¿à¿š™™™™™ñ¿š™™™™™ñ¿333333ã¿ffffffæ¿333333ó¿333333ó¿ffffffÀ333333û¿ÀÍÌÌÌÌÌÀffffffö¿ð¿š™™™™™é¿à¿š™™™™™Ù¿ffffffæ¿à¿ÍÌÌÌÌÌì¿ð¿333333ó¿ÍÌÌÌÌÌÀš™™™™™ñ¿ffffffæ¿333333ã¿333333ã¿333333㿚™™™™™Ù¿333333ã¿Àð¿333333ã¿ffffffÀffffffÀš™™™™™Ù¿ÀÍÌÌÌÌÌì¿À333333ã¿ffffffæ¿ø¿333333ã¿ffffffæ¿à¿ÍÌÌÌÌÌ쿚™™™™™ñ¿333333ã¿ø¿à¿š™™™™™Àš™™™™™Ù¿Àà¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿š™™™™™Ù¿š™™™™™Ù¿ÍÌÌÌÌÌô¿333333ã¿333333㿚™™™™™Ù¿ffffffö¿ÍÌÌÌÌÌô¿š™™™™™Ù¿š™™™™™ÀÍÌÌÌÌÌì¿ffffff!Àš™™™™™é¿š™™™™™Ù¿ffffff濚™™™™™ñ¿333333ó¿ð¿ÍÌÌÌÌÌÀš™™™™™ù¿š™™™™™Ù¿333333ó¿à¿333333À333333ã¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀ࿚™™™™™é¿š™™™™™ñ¿ffffffö¿ø¿ð¿ffffff濚™™™™™é¿à¿š™™™™™ Àffffffæ¿Àš™™™™™ÀÍÌÌÌÌÌÀ࿚™™™™™ñ¿333333Àš™™™™™Ù¿ffffffæ¿333333û¿à¿Àš™™™™™é¿š™™™™™Ù¿š™™™™™Ù¿à¿ffffffæ¿333333ã¿ffffffö¿ÍÌÌÌÌÌÀà¿ffffffÀð¿333333ã¿ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333û?š™™™™™ @š™™™™™@ÍÌÌÌÌÌü?333333ã?ø?ffffff@ffffff@ffffff@ffffff @333333û?š™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™@333333ó?@@ø?333333!@ÍÌÌÌÌÌì?ffffff@333333"@ffffff&@š™™™™™@ffffffþ?ffffff@ÍÌÌÌÌÌ@š™™™™™ñ?ð?333333@333333û? @š™™™™™&@ÍÌÌÌÌÌü?š™™™™™@ffffff@ÍÌÌÌÌÌì?333333@333333@ÍÌÌÌÌÌ@š™™™™™@333333ã?333333!@ffffff@@@ffffffæ?š™™™™™@š™™™™™@ÍÌÌÌÌÌ@333333@@š™™™™™@ffffffþ?š™™™™™Ù?š™™™™™ù?@333333@ÍÌÌÌÌÌô?@ÍÌÌÌÌÌ@š™™™™™ @š™™™™™é?333333#@š™™™™™é?333333@ÍÌÌÌÌÌô?ø?ø?š™™™™™ù?ø? @š™™™™™é?ÍÌÌÌÌÌ@š™™™™™Ù?ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?333333@ÍÌÌÌÌÌ@@333333ó?333333@ÍÌÌÌÌÌ@@ð?ffffff@ÍÌÌÌÌÌ@š™™™™™Ù?333333ã?ffffff @ÍÌÌÌÌÌ@š™™™™™$@333333@333333@ffffff @333333@@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333ó?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@š™™™™™@ÍÌÌÌÌÌ @ffffff@333333û?š™™™™™@ffffffþ?ÍÌÌÌÌÌì?@š™™™™™@š™™™™™é?ÍÌÌÌÌÌü?š™™™™™$@š™™™™™@ffffff@š™™™™™é?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?@ø?š™™™™™(@ @š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™Ù?ÍÌÌÌÌÌ @à?333333@š™™™™™@š™™™™™@@ð?333333ã?š™™™™™ @333333û?@ffffff @š™™™™™@333333ã?@@ÍÌÌÌÌÌ@333333ã?ø?@@ð?@ÍÌÌÌÌÌ@ffffff@@ffffff@333333@ffffff@š™™™™™ñ?š™™™™™(@333333@ÍÌÌÌÌÌì?ffffff@@ÍÌÌÌÌÌ@333333 @333333@š™™™™™é?@333333@#@@š™™™™™Ù?š™™™™™ @ @@ÍÌÌÌÌÌì?333333ã?ÍÌÌÌÌÌ@ffffffö? @333333@ffffff@333333)@š™™™™™ñ?ø?ffffff@š™™™™™@š™™™™™Ù?š™™™™™é?ð?333333@à?@333333 @š™™™™™é?@333333 @ffffff@ffffff@š™™™™™ @ffffffæ?ffffff@š™™™™™Ù?ffffffþ?š™™™™™ @333333 @š™™™™™@š™™™™™@333333@ÍÌÌÌÌÌ@333333@@ÍÌÌÌÌÌ@à?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌü?@333333@333333ó?ffffffþ?š™™™™™@@@ffffff@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ$@ffffff@ffffff @š™™™™™@š™™™™™ù?ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ"@š™™™™™@@à?ffffffþ?333333û?à?ffffff@š™™™™™@ÍÌÌÌÌÌ@ð?ffffffþ?ø?ffffff@ffffff@š™™™™™!@333333@333333ó?ffffffþ?333333@333333@ @ÍÌÌÌÌÌ@š™™™™™@@š™™™™™é?ffffffæ?ffffffæ?@š™™™™™ @š™™™™™ñ?@ÍÌÌÌÌÌ@@@ffffff+@ffffff@ÍÌÌÌÌÌ@333333 @š™™™™™@333333@333333@ffffff#@333333ó?š™™™™™é?ffffffþ?ÍÌÌÌÌÌ@ffffff @š™™™™™Ù?ffffff@"@ffffff@š™™™™™@š™™™™™@ffffffæ?š™™™™™ @š™™™™™ñ?333333@ÍÌÌÌÌÌ@@@333333+@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333*@ÍÌÌÌÌÌ @@š™™™™™@@ð?š™™™™™ @333333@ÍÌÌÌÌÌ@š™™™™™é?š™™™™™ù?š™™™™™ @š™™™™™ñ?ffffffþ?333333 @333333@š™™™™™Ù?ffffff @@ffffff@333333 @š™™™™™@š™™™™™@@@ÍÌÌÌÌÌô?333333&@š™™™™™é?ÍÌÌÌÌÌ@#@ffffff@ø?š™™™™™@ffffff@333333@@333333@š™™™™™é?š™™™™™@ÍÌÌÌÌÌü?š™™™™™@ø?@š™™™™™@ÍÌÌÌÌÌ@ffffff@ffffff@ÍÌÌÌÌÌü?ø?ffffff@ffffff@@š™™™™™@š™™™™™é?š™™™™™ @ffffffæ?š™™™™™@333333/@ÍÌÌÌÌÌ(@333333ó?ÍÌÌÌÌÌì?ffffffö?ÍÌÌÌÌÌü?@š™™™™™Ù?ffffff%@š™™™™™ @@ÍÌÌÌÌÌì?ffffff@@ffffff@ffffffæ?333333(@ffffff@ffffff@ÍÌÌÌÌÌ@š™™™™™@ð?ffffffþ?333333û?š™™™™™Ù?ð?ffffffþ?ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ@š™™™™™ñ?ffffff@333333@333333û?333333û?ffffffö?333333@ð?ffffff@ÍÌÌÌÌÌ@ffffff@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?š™™™™™ @š™™™™™ñ?ÍÌÌÌÌÌ@š™™™™™ñ?à?š™™™™™@š™™™™™@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ%@333333ó?š™™™™™@ð¿ÍÌÌÌÌÌ쿚™™™™™Àš™™™™™Ù¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ)ÀffffffÀffffffþ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿ÀffffffÀš™™™™™ À𿚙™™™™ñ¿333333ó¿ÍÌÌÌÌÌÀ333333ó¿š™™™™™Àš™™™™™ù¿ffffffþ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿š™™™™™À333333ÀÍÌÌÌÌÌÀffffffþ¿Àffffffþ¿š™™™™™Àð¿333333Àffffffö¿ÍÌÌÌÌÌü¿ð¿š™™™™™ ÀÀš™™™™™é¿š™™™™™Ù¿š™™™™™é¿ÍÌÌÌÌÌì¿333333ã¿333333ã¿333333û¿š™™™™™ ÀffffffÀ333333û¿ ÀÍÌÌÌÌÌü¿333333(À𿚙™™™™ñ¿ffffffö¿ffffffÀø¿ffffffþ¿333333ã¿ffffffæ¿Àð¿ffffffþ¿š™™™™™Àffffffæ¿ÍÌÌÌÌÌÀÀš™™™™™Ù¿ffffffþ¿333333Àš™™™™™*Àš™™™™™Àš™™™™™Àffffffö¿š™™™™™ÀÀffffffö¿š™™™™™Àš™™™™™Ù¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌ쿚™™™™™ù¿ø¿š™™™™™ÀffffffÀffffffö¿ÍÌÌÌÌÌÀÍÌÌÌÌÌ Àš™™™™™ñ¿ø¿ÍÌÌÌÌÌÀš™™™™™Àš™™™™™Àffffffö¿333333Àffffff*ÀÍÌÌÌÌÌ쿚™™™™™ñ¿ÍÌÌÌÌÌô¿333333ÀffffffÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀffffffþ¿ÍÌÌÌÌÌô¿333333ó¿ÍÌÌÌÌÌÀffffffæ¿ÍÌÌÌÌÌô¿š™™™™™ù¿š™™™™™Ù¿À333333㿚™™™™™ ÀÀš™™™™™é¿ffffff ÀÍÌÌÌÌÌÀ333333û¿333333 ÀÍÌÌÌÌÌì¿ffffffÀ333333ÀÀffffffö¿š™™™™™ñ¿333333Àà¿ffffffþ¿ffffffÀš™™™™™ù¿š™™™™™é¿ffffffÀffffffþ¿ffffffæ¿À333333ó¿š™™™™™ñ¿ÍÌÌÌÌÌÀffffffö¿š™™™™™ñ¿š™™™™™é¿š™™™™™ ÀÀš™™™™™ñ¿333333ÀÍÌÌÌÌÌü¿š™™™™™Ù¿ffffff濚™™™™™é¿ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿Àš™™™™™Àš™™™™™ù¿ÀÀð¿ÍÌÌÌÌÌÀ333333û¿À333333Àš™™™™™é¿ÍÌÌÌÌÌü¿ð¿ÍÌÌÌÌÌÀffffff À333333Àš™™™™™!ÀÍÌÌÌÌÌÀš™™™™™ñ¿À333333ÀÍÌÌÌÌÌÀš™™™™™Àffffffö¿333333û¿333333 Àà¿À333333Àø¿333333 À333333ÀÍÌÌÌÌÌì¿333333 ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ Àš™™™™™ñ¿ÍÌÌÌÌÌì¿à¿ffffffÀffffffÀffffffö¿ffffffÀ333333ó¿ffffffÀÍÌÌÌÌÌÀÀffffffÀø¿Àffffffö¿333333"À333333㿚™™™™™Àš™™™™™ñ¿ffffffÀ333333À333333û¿ÍÌÌÌÌÌÀø¿ÍÌÌÌÌÌ쿚™™™™™À#À𿚙™™™™ñ¿š™™™™™ÀffffffÀÍÌÌÌÌÌô¿ø¿ÍÌÌÌÌÌì?333333û?ffffff@š™™™™™é?333333ã?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌì?ð?ø?ffffffö?š™™™™™¹¿š™™™™™Ù?š™™™™™É¿ffffffæ?š™™™™™¹¿š™™™™™Ù?333333Ó¿333333ã?333333㿚™™™™™¹¿š™™™™™Ù¿333333Ó?333333ã?š™™™™™¹¿ffffffæ?š™™™™™¹?š™™™™™Ù¿333333Ó?š™™™™™É¿š™™™™™¹¿š™™™™™É?š™™™™™¹¿333333Ó?š™™™™™¹¿š™™™™™Ù¿š™™™™™É¿š™™™™™¹?š™™™™™Ù?à?š™™™™™Ù?333333Ó?š™™™™™É¿ffffffæ?š™™™™™¹?࿚™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌì?à?333333Ó¿š™™™™™Ù?š™™™™™É¿333333Ó¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿333333ã¿333333Ó?333333Ó¿š™™™™™É¿333333Ó¿š™™™™™¹¿333333Ó?š™™™™™¹¿š™™™™™É?à?š™™™™™¹¿š™™™™™É¿š™™™™™É¿333333Ó?š™™™™™¹?333333Ó¿š™™™™™Ù¿š™™™™™É¿š™™™™™Ù?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?333333Ó?š™™™™™É¿š™™™™™Ù?à?š™™™™™Ù¿ÍÌÌÌÌÌì?333333Ó?š™™™™™¹¿333333Ó?š™™™™™Ù?à¿à¿š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™¹¿333333Ó?333333Ó¿333333Ó¿š™™™™™É¿333333ã?à?š™™™™™¹¿š™™™™™¹¿š™™™™™é?333333ã¿à?à?š™™™™™É¿à?à?333333Ó¿š™™™™™É?š™™™™™É?š™™™™™¹¿ÍÌÌÌÌÌì?š™™™™™É?š™™™™™¹¿333333ã?333333ã?à?à?š™™™™™É¿333333Ó?333333Ó?333333Ó?š™™™™™¹¿333333Ó?š™™™™™É¿š™™™™™¹¿š™™™™™É¿š™™™™™É¿š™™™™™¹¿333333ã¿333333Ó¿à?333333ã?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™é?333333Ó¿333333ã¿à¿333333Ó¿ffffffæ?333333ã?à¿333333Ó¿š™™™™™¹?š™™™™™é?333333ã?š™™™™™¹¿š™™™™™¹¿333333ã?š™™™™™¹¿š™™™™™¹¿š™™™™™Ù?š™™™™™Ù¿š™™™™™¹¿š™™™™™É¿333333Ó?š™™™™™¹¿š™™™™™¹?333333ã?š™™™™™¹¿333333Ó¿333333ã?ffffffæ?š™™™™™É¿à?333333Ó¿333333Ó?š™™™™™¹¿š™™™™™¹¿à?࿚™™™™™Ù?š™™™™™¹¿333333Ó?ffffff濚™™™™™É¿à?š™™™™™É?š™™™™™Ù?333333Ó¿š™™™™™Ù¿š™™™™™Ù?333333Ó?š™™™™™É¿š™™™™™É¿333333ã?š™™™™™¹¿ffffffæ?š™™™™™¹¿333333Ó¿š™™™™™É?š™™™™™É?333333Ó?š™™™™™¹¿š™™™™™Ù¿333333ã?š™™™™™É?š™™™™™¹¿š™™™™™É¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿333333ã?à?ffffffæ?333333ã?333333Ó?š™™™™™É?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?à?š™™™™™¹¿š™™™™™É?333333㿚™™™™™é?š™™™™™é¿š™™™™™¹¿à?š™™™™™É¿š™™™™™é?333333Ó¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™Ù¿333333Ó?à?ffffffæ?š™™™™™¹?333333ã?š™™™™™¹¿š™™™™™É¿ffffffæ?333333Ó?š™™™™™Ù¿333333Ó¿š™™™™™É¿š™™™™™¹¿à¿ð?š™™™™™¹¿š™™™™™¹¿š™™™™™Ù?333333Ó¿333333ã?333333Ó¿š™™™™™Ù?š™™™™™Ù¿333333Ó¿ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™É?š™™™™™É¿š™™™™™É¿š™™™™™¹¿333333Ó?333333Ó¿à¿ÍÌÌÌÌÌì?š™™™™™Ù¿š™™™™™¹¿š™™™™™¹¿à?š™™™™™¹¿š™™™™™É¿š™™™™™É¿š™™™™™É¿š™™™™™É?š™™™™™é?š™™™™™¹?š™™™™™Ù?š™™™™™É¿333333ã?à?š™™™™™É?ffffffæ?333333Ó¿333333Ó?ÍÌÌÌÌÌì?333333Ó¿š™™™™™Ù?333333ã?à¿à?333333Ó?š™™™™™¹¿333333Ó¿š™™™™™Ù?š™™™™™¹¿š™™™™™Ù¿š™™™™™Ù?š™™™™™Ù?š™™™™™¹¿š™™™™™É¿š™™™™™É?š™™™™™¹¿333333Ó?à?š™™™™™¹¿š™™™™™¹¿333333Ó¿š™™™™™¹¿š™™™™™Ù?š™™™™™É¿333333Ó?333333ã?š™™™™™Ù¿š™™™™™¹?š™™™™™é?ffffffæ?š™™™™™É¿š™™™™™¹¿333333Ó¿ð?š™™™™™Ù?š™™™™™¹¿333333ã?à?š™™™™™¹¿š™™™™™¹?333333ã?š™™™™™Ù¿333333Ó¿š™™™™™¹¿š™™™™™É¿333333Ó?š™™™™™¹¿š™™™™™Ù?ffffffæ?ð¿333333Ó¿š™™™™™É¿333333Ó¿333333Ó?š™™™™™É¿š™™™™™É¿333333Ó?š™™™™™é¿à?333333ã?333333Ó¿š™™™™™¹?333333Ó¿š™™™™™É¿š™™™™™Ù?š™™™™™É?š™™™™™é?࿚™™™™™É¿333333Ó?333333Ó¿š™™™™™Ù¿š™™™™™é?š™™™™™É¿ð?333333ã?333333Ó?à?š™™™™™É¿š™™™™™¹¿š™™™™™¹?à?333333Ó¿š™™™™™Ù¿š™™™™™¹?333333Ó¿ffffffæ?š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿à?ffffffæ?š™™™™™É¿333333Ó¿š™™™™™Ù?333333Ó?à¿333333Ó?š™™™™™Ù?š™™™™™É?ffffffæ?à?ffffffæ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™Ù?333333Ó¿š™™™™™É¿š™™™™™¹¿ffffffæ?š™™™™™¹?ð?333333ã?š™™™™™¹¿š™™™™™Ù?333333Ó?ffffffö¿333333Ó?333333Ó?333333ã¿ffffffæ?š™™™™™É¿à?š™™™™™É¿à?333333ã?333333Ó?333333Ó¿à?š™™™™™Ù¿à?š™™™™™Ù?š™™™™™¹¿š™™™™™¹¿333333Ó¿š™™™™™É¿à?š™™™™™¹¿š™™™™™É¿š™™™™™É¿š™™™™™Ù?ffffffæ?š™™™™™¹¿š™™™™™É¿333333ã?333333Ó¿333333ӿ࿚™™™™™É?š™™™™™¹¿š™™™™™É¿š™™™™™¹¿333333Ó?š™™™™™É¿š™™™™™Ù?333333ã?à?333333Ó¿š™™™™™Ù?š™™™™™¹?@ffffffæ?š™™™™™É?333333ó?ffffffæ?ð?333333ó?š™™™™™ñ?š™™™™™Ù¿š™™™™™Ù?ffffffæ?࿚™™™™™é?š™™™™™É¿ffffffæ?š™™™™™¹?š™™™™™É?ð?à?ffffff濚™™™™™¹?š™™™™™é?š™™™™™é?š™™™™™ñ?š™™™™™Ù¿š™™™™™É?333333ã?š™™™™™ñ?ÍÌÌÌÌÌü¿š™™™™™Ù?š™™™™™é?333333ó¿ffffffö¿ffffffæ?333333ã?ffffffö?š™™™™™¹?333333Ó?à?ð?ÍÌÌÌÌÌü?š™™™™™@ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™ñ?333333Ó?333333Ó?ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™ñ?333333ã?š™™™™™Ù¿š™™™™™É?š™™™™™É¿ffffffæ?š™™™™™é?à?š™™™™™É¿ÍÌÌÌÌÌô?ð?à?š™™™™™Ù?š™™™™™É?š™™™™™Ù?ø?š™™™™™¹¿š™™™™™É¿š™™™™™é?š™™™™™Ù?š™™™™™É?ffffffæ?š™™™™™Ù?š™™™™™É?333333ó?ÍÌÌÌÌÌô?š™™™™™Ù?ð?à?ffffff濚™™™™™é¿à?333333Ó¿à?ffffffæ?à?š™™™™™É?à¿ffffffæ¿333333Ó¿ffffffö¿š™™™™™Ù?à¿333333û¿ÍÌÌÌÌÌô?333333ó¿š™™™™™ù?š™™™™™ñ?333333Ó?š™™™™™¹¿ÍÌÌÌÌÌì?š™™™™™¹¿ÍÌÌÌÌÌô?ffffff濚™™™™™ù?š™™™™™¹¿333333Ó¿à?ffffffæ?š™™™™™É?š™™™™™Ù¿ÍÌÌÌÌÌô?š™™™™™Ù?ÍÌÌÌÌÌì¿ð?333333ã?š™™™™™¹¿š™™™™™ñ?333333ó?333333ó?ð?ffffffö?333333ó?333333Ó¿š™™™™™É?š™™™™™Ù?333333ã?š™™™™™É¿š™™™™™É?ÍÌÌÌÌÌì?333333Ó¿š™™™™™¹¿š™™™™™É?à?ÍÌÌÌÌÌì¿à?333333ã?@333333ã?à?š™™™™™Ù?š™™™™™ñ¿š™™™™™Ù?333333ã?ffffffæ?ffffffæ¿333333Ó?ÍÌÌÌÌÌô?š™™™™™Ù?333333û?š™™™™™é?š™™™™™ù?333333ã?š™™™™™ñ?333333Ó¿333333ó?ÍÌÌÌÌÌô?333333ó?š™™™™™é?333333ó?ð?333333Ó?ÍÌÌÌÌÌì?š™™™™™¹¿š™™™™™é?ffffffæ?333333Ó¿à?š™™™™™É?š™™™™™Ù?ffffffæ?ffffffæ?࿚™™™™™Ù¿š™™™™™É?š™™™™™Ù¿š™™™™™¹¿š™™™™™Ù¿š™™™™™é?ffffffæ?ÍÌÌÌÌÌô?ð?333333Ó¿333333ó?š™™™™™Ù¿š™™™™™¹¿ffffffæ?ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™é?š™™™™™é¿š™™™™™Ù?Àš™™™™™é?š™™™™™Ù?333333Ó?à?ÍÌÌÌÌÌì?333333Ó?à?ÍÌÌÌÌÌì?ffffff@ð?š™™™™™¹¿333333Ó?𿚙™™™™ñ?š™™™™™À333333Ó?ffffffæ?ð¿ÍÌÌÌÌÌô?333333û¿Àffffffæ¿ø?ð?ÍÌÌÌÌÌì?ð¿ffffffþ?š™™™™™é¿š™™™™™¹¿š™™™™™É¿333333ó?š™™™™™é?š™™™™™¹¿š™™™™™¹¿š™™™™™É¿š™™™™™ñ?š™™™™™é?ÍÌÌÌÌÌ@š™™™™™É¿à?𿚙™™™™¹?š™™™™™Ù¿à¿ÍÌÌÌÌÌô¿à?š™™™™™ñ¿333333Ó?ffffffþ?š™™™™™Ù?ffffffö¿ÍÌÌÌÌÌü?š™™™™™É¿ffffffæ?333333Ó?à?333333Ó¿333333Ó¿ÍÌÌÌÌÌì?à?ffffffæ?ffffffæ¿ffffffö¿ÍÌÌÌÌÌì¿333333ó?š™™™™™ñ¿ð¿333333ã¿à¿ffffffæ?ffffffæ?333333û?333333Ó?ffffffæ?š™™™™™É?š™™™™™é?ÍÌÌÌÌÌü?ø?š™™™™™ñ?à?š™™™™™¹¿à¿š™™™™™É¿333333ó?š™™™™™É?ø?333333ã?333333ã?ÍÌÌÌÌÌô?333333㿚™™™™™ñ?ÍÌÌÌÌÌì?à?333333Ó?š™™™™™Ù¿š™™™™™é?š™™™™™Ù?š™™™™™é?333333ó?333333Ó¿ÍÌÌÌÌÌì¿à?ffffff @š™™™™™É¿š™™™™™ñ?ÍÌÌÌÌÌü¿ÍÌÌÌÌÌô?š™™™™™ñ?ÍÌÌÌÌÌì?ø?ÍÌÌÌÌÌü?à?ÍÌÌÌÌÌô¿333333Ó?š™™™™™ñ?ÍÌÌÌÌÌì?š™™™™™ñ?333333ã?š™™™™™É¿š™™™™™ñ?ÍÌÌÌÌÌô?ð¿ÍÌÌÌÌÌì?ð?š™™™™™¹¿ð?333333ã?ffffff濚™™™™™é¿333333Ó¿š™™™™™ñ?š™™™™™é¿š™™™™™ñ¿š™™™™™É¿ffffffæ?ffffffæ¿ÍÌÌÌÌÌô¿ø?š™™™™™ñ¿ÍÌÌÌÌÌü?š™™™™™Ù?ð?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü¿š™™™™™¹¿333333ã?333333Ó?à?333333ã¿333333ó?š™™™™™Ù?333333Ó?ð?ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™¹¿š™™™™™Ù?š™™™™™É¿333333Ó¿š™™™™™É¿š™™™™™ñ¿š™™™™™Ù?ÍÌÌÌÌÌÀffffffæ¿333333ó¿ÍÌÌÌÌÌì¿333333Ó¿ð¿333333Ó?ÍÌÌÌÌÌô¿š™™™™™Ù?࿚™™™™™é?ÍÌÌÌÌÌü?š™™™™™é¿333333ã?š™™™™™Ù?ffffffö?ÍÌÌÌÌÌô¿333333Ó?333333Ó¿š™™™™™ñ¿333333Ó?š™™™™™É¿š™™™™™ñ¿š™™™™™Ù?š™™™™™Ù?333333ó?à¿ð?ð?ø?333333ã?š™™™™™é?ð¿333333ó?ÍÌÌÌÌÌÀffffffö¿à¿š™™™™™¹?ÍÌÌÌÌÌì?ffffffæ?333333Ó¿š™™™™™Ù?š™™™™™Ù?à?ffffffö¿ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™ñ?333333ã¿333333û?š™™™™™é?ffffffæ¿ffffffæ¿ffffffö?š™™™™™é¿š™™™™™ñ?š™™™™™¹¿ÍÌÌÌÌÌô?333333Ó¿š™™™™™é?š™™™™™É¿ffffffæ?š™™™™™é?333333Ó¿333333ã?333333Ó¿333333ã¿à?à?333333Ó? À333333ó?ffffffæ?š™™™™™é?š™™™™™É¿š™™™™™É?ø?333333Ó?š™™™™™Ù?à¿333333ó?333333ã?š™™™™™É?š™™™™™Ù?š™™™™™Ù¿š™™™™™ñ?333333ã?ÍÌÌÌÌÌì?ffffffö?333333Ó?ÍÌÌÌÌÌô?333333Ó¿333333ã?333333ã?333333Ó¿ffffffþ?333333ó?ffffffæ¿333333,@š™™™™™ @ÍÌÌÌÌÌ"ÀÍÌÌÌÌÌ"@ffffff @š™™™™YCÀš™™™™™ ÀÀ333333%ÀffffffÀ @ffffff5@ffffffÀ333333@ffffff@ffffffÀÍÌÌÌÌÌì?333333Àš™™™™6@333333@š™™™™™Àš™™™™™é¿333333(@ffffffæ?ÍÌÌÌÌL?@'À,@fffffæ>@fffffæA@€7@333333@ffffff!@33333³2@333333Ó?333333Ó¿ÍÌÌÌÌÌü?š™™™™™@ffffff6@fffff¦@@ffffffæ?ÍÌÌÌÌÌü¿š™™™™™ù?ÍÌÌÌÌÌì?3333330@€2@ÍÌÌÌÌÌ&@@ffffffæ?€;@ÍÌÌÌÌÌ"@š™™™™™@0@š™™™™™é¿š™™™™™@ÍÌÌÌÌÌ(@š™™™™™ @333333'@@ffffff(À333333&Àffffff@ffffff ÀÀffffff)Àà¿ffffff@ÍÌÌÌÌÌü¿ÍÌÌÌÌÌ*@333333#@š™™™™™@ø¿33333³1@ÍÌÌÌÌÌô¿Àffffff.À!@33333³0À333333û?333333ó¿š™™™™™À@@š™™™™™ Àffffffæ?ffffffÀš™™™™™Ù¿š™™™™™ñ¿š™™™™A@ @2@@ffffff*@333333ã?ÍÌÌÌÌÌ@$@ÍÌÌÌÌÌ @š™™™™™ù?ÍÌÌÌÌL0@333333%@š™™™™™%ÀffffffÀ333333$@333333@š™™™™™1@ffffff!@š™™™™™@š™™™™™/Àš™™™™™(@š™™™™™/@š™™™™™@333333Ó?Àà¿@ÍÌÌÌÌÌ @333333&@%@333333@333333,@ÍÌÌÌÌÌ@š™™™™™5@@š™™™™™É¿ffffffö?ÍÌÌÌÌL0@ @ÍÌÌÌÌÌ @€?@š™™™™™#Àš™™™™™@š™™™™™ Àš™™™™™À333333Ó?333333)À333333ó?ffffffö¿ÀÍÌÌÌÌÌü?ffffffÀÍÌÌÌÌÌÀš™™™™™)@ÍÌÌÌÌÌì¿333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ"ÀÍÌÌÌÌÌ%@Àš™™™™™¹¿ffffff ÀÍÌÌÌÌÌ@333333)@š™™™™™@ÀÍÌÌÌÌÌ!@333333 @333333)Àš™™™™™@1@ À33333³2@ffffff@333333#@ffffffþ¿š™™™™™ñ?"@š™™™™™)@333333㿚™™™™™@ÍÌÌÌÌÌ/@š™™™™5@@ffffff.@ÍÌÌÌÌÌ!À333333(@ÍÌÌÌÌÌÀ333333ã¿ÍÌÌÌÌÌ>@3333335@#ÀÍÌÌÌÌÌ$@ffffff@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ5@ÍÌÌÌÌÌ&@š™™™™™ Àš™™™™™¹?ÍÌÌÌÌÌ(@33333sM@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌÀ<@ffffff@ffffff@À33333³0Àš™™™™™#@ffffff=Àš™™™™™4@ÍÌÌÌÌÌ*@-@š™™™™™B@š™™™™™@5ÀÍÌÌÌÌÌ!Àš™™™™™%@=@À333333 ÀÍÌÌÌÌÌÀš™™™™™À333333@ÍÌÌÌÌÌ;À@ÍÌÌÌÌÌ)@ffffff@ÍÌÌÌÌÌ@333333Ó?ÍÌÌÌÌÌÀffffff @ffffff)@ffffff Àš™™™™™ @333333 Àð?š™™™™™É?ÍÌÌÌÌÌ!@š™™™™™(@š™™™™™ À@ÍÌÌÌÌÌ0@š™™™™™ù¿ffffffö¿ffffff*ÀÍÌÌÌÌL3@ÍÌÌÌÌÌÀš™™™™™ À333333.@%@š™™™™™À5@š™™™™™@ffffffÀš™™™™™ù?333333)@@333333'@š™™™™™)@ffffff @š™™™™™(@@ffffff2@ffffff:@€3@333333 @33333³2@ÍÌÌÌÌÌÀÀš™™™™™!@ÍÌÌÌÌL6@333333@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌÀÍÌÌÌÌÌ@@ÍÌÌÌÌÌÀ&@š™™™™™.@ÍÌÌÌÌÌ@333333ÀffffffÀš™™™™™¹?ÍÌÌÌÌÌì?š™™™™™#@ffffff)@š™™™™™?@Àffffff@š™™™™™À€0@333333<@@š™™™™™1@4@ffffff.@333333Àš™™™™™Àš™™™™™é?2@3333337@š™™™™™ @š™™™™™@š™™™™™@333333ó?š™™™™™ ÀÍÌÌÌÌŒD@)À33333³4@ÍÌÌÌÌÌ6@à¿fffffæ5@0@ffffff4@ÍÌÌÌÌÌÀš™™™™™@ÍÌÌÌÌÌ>@ÍÌÌÌÌÌ@333333Àš™™™™™@€1@ffffff@ffffff À%@Àš™™™™:À333333@š™™™™™ ÀÍÌÌÌÌÌ@ÀÍÌÌÌÌL9À(@š™™™™=@ffffff,@@.@ÍÌÌÌÌLB@š™™™™™/@ø¿333333,@ÍÌÌÌÌÌÀ€8À333333㿚™™™™™,@š™™™™™ @ffffff"À333333ó¿À333333#@ÍÌÌÌÌÌ!À333333û?š™™™™™@%@ÍÌÌÌÌÌ Àš™™™™™Àffffff @6@š™™™™™6Àš™™™™™#@333333+@š™™™™™@š™™™™™@ÍÌÌÌÌÌ1@ffffff!@ffffff!Àš™™™™™B@333333ó?333333,@ffffff@@ffffff @š™™™™™7@ÍÌÌÌÌÌÀ333333-@33333³7@ÍÌÌÌÌÌ&@Àš™™™™™ñ¿ø?š™™™™A@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ0@š™™™™™@333333-@š™™™™2@ffffff@à?š™™™™™'Àffffff À)Àš™™™™™@€5@&Àffffff@ÍÌÌÌÌÌÀ333333@š™™™™Ù\@ÍÌÌÌÌLF@ÍÌÌÌÌÌ@Àš™™™™™À.À333333À333333&@@ffffff(ÀC@333333@š™™™™™@333333*Àš™™™™™2@33333³7ÀÍÌÌÌÌÌ$@ÍÌÌÌÌÌÀš™™™™™ñ?š™™™™<@à?33333³4@&@3333333@à?ffffffÀÍÌÌÌÌÌÀffffffæ¿$Àffffff@333333û?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?ffffff'@ÍÌÌÌÌÌü¿ffffff!@š™™™™8@š™™™™™ @š™™™™™@ffffffþ¿š™™™™™@š™™™™™&Àfffffæ7Àffffff@ffffff"@ÍÌÌÌÌL1@ffffff4@ÍÌÌÌÌÌü?š™™™™™$@ÍÌÌÌÌÌÀ3@š™™™™™,Àffffff濚™™™™™Ù?ÍÌÌÌÌL1@ÍÌÌÌÌÌ@ÍÌÌÌÌÌÀš™™™™™ @ÍÌÌÌÌL0@333333(À€8@ffffff@š™™™™™!@€6@š™™™™™(@333333ó¿2@€0@ÍÌÌÌÌ EÀffffffö¿333333@333333 Àš™™™™™ÀffffffÀffffff+@ÍÌÌÌÌL6@ÀÍÌÌÌÌÌ#@š™™™™™!@$Àffffffö?333333@<@ffffff,@š™™™™™"Àš™™™™™Ù?ÍÌÌÌÌÌ.@š™™™™™@*@ffffff#À3333334@ÍÌÌÌÌÌ@,@€<@ÍÌÌÌÌÌ$@š™™™™™3@ÍÌÌÌÌL3@ffffffþ?@ÍÌÌÌÌÌ"@fffffæ2@ÍÌÌÌÌ G@ÍÌÌÌÌÌD@š™™™™™@š™™™™™@ffffff @@š™™™™6@3333331@fffffæ3@š™™™™™&@ÍÌÌÌÌÌü¿fffffæ<@333333 @ÍÌÌÌÌÌ&@ffffff5@@š™™™™™ @ffffff4@333333.@333333.@ÍÌÌÌÌÌ@ffffff%Àš™™™™™ ÀÍÌÌÌÌÌ-@ffffffÀ333333 ÀÍÌÌÌÌÌÀš™™™™™ù?ÍÌÌÌÌÌ@ffffff @ÍÌÌÌÌÌ@š™™™™™,@š™™™™™3@1@š™™™™™ñ?š™™™™9@@ÍÌÌÌÌÌ&ÀÍÌÌÌÌL4Àš™™™™™&@fffffæ2ÀÍÌÌÌÌÌ@333333@ð¿ffffff@š™™™™™ù?333333+Àffffffþ¿ÍÌÌÌÌÌ,ÀÍÌÌÌÌÌ@333333Àš™™™™™+@ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ@š™™™™0@6@@ffffff@ÍÌÌÌÌL0@333333@ÍÌÌÌÌÌ%@š™™™™™$@š™™™™B@ffffff&ÀÀš™™™™™,@333333@š™™™™2@ÍÌÌÌÌÌ@ffffff-@š™™™™™'À333333@š™™™™™6@ÍÌÌÌÌÌ@š™™™™™¹¿š™™™™™¹¿ffffff@333333)@$@š™™™™5@ÍÌÌÌÌL4@š™™™™™.@@ffffff:@ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ffffff!@.@333333@š™™™™™@š™™™™™A@ffffff.À!@š™™™™™É?8@ffffff@333333"À333333@333333"À333333û¿@Àffffff(ÀÍÌÌÌÌÌ-@ffffff"@333333@fffffæ3@ffffffÀš™™™™6@ÍÌÌÌÌÌô?333333@š™™™™™À,@7@333333"@ÍÌÌÌÌÌ@33333³2@š™™™™™%@%Àffffff(@€0@@7@š™™™™™é?ffffff*@333333㿚™™™™™ñ?š™™™™™'@š™™™™™2@@ffffff@333333*@ffffff6@333333@333333-@'À€0@Àš™™™™™@š™™™™™B@š™™™™™2@š™™™™™Ù¿š™™™™™@š™™™™™@ffffff@(@333333@ø?333333À0@ÍÌÌÌÌL6@š™™™™™0@ÍÌÌÌÌÌÀš™™™™>@333333&@*@333333ã¿,Àffffff/@ÍÌÌÌÌÌ*@ffffff;@š™™™™™)@/@ffffff6@%@š™™™™Ù@À333333À3333330@š™™™™™0@333333@33333³2ÀÍÌÌÌÌÌ@À%À333333@333333'Àffffff$@3@ffffffÀÍÌÌÌÌL2@š™™™™™ÀffffffÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ4@ffffffþ?ÍÌÌÌÌÌ@ffffffÀø¿333333$@š™™™™™,@š™™™™=@š™™™™™À!@ÍÌÌÌÌÌ"@š™™™™™ñ¿ÍÌÌÌÌÌ Àš™™™™™/Àš™™™™™ @ffffffö¿333333&Àfffffæ0@:@333333ã?ffffff@š™™™™™6@ffffffÀ@ffffff-@333333@ffffff#@š™™™™™%@ffffff!@ffffff/@&@š™™™™™"@333333'@š™™™™™(@ffffff*@ffffff@ffffff!ÀÍÌÌÌÌÌÀ@333333:@!@ÍÌÌÌÌL;@À@@fffffæ;@ffffff:@š™™™™™,@ ÀÍÌÌÌÌÌÀ333333 ÀffffffÀffffff1@+@š™™™™YE@333333Ó?ffffff@ffffff@'@ÍÌÌÌÌ B@)@4@3333336@*@š™™™™™Ù¿š™™™™™¹?ÍÌÌÌÌÌ@š™™™™™:@fffffæ0@333333Àš™™™™™@333333@@ffffffö¿333333ã¿33333³1@ffffffö¿ÍÌÌÌÌL;@ffffff;@333333%@ÍÌÌÌÌ A@5@š™™™™™#@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ffffff'@33333³B@š™™™™™(@ffffffÀÍÌÌÌÌÌ-@:@333333Àffffff@š™™™™1@333333Àš™™™™3À!@ÍÌÌÌÌÌÀš™™™™™ÀÀ1Àš™™™™™@ffffff/@š™™™™™)@333333"@ÍÌÌÌÌÌ&@3333338@fffff&F@ffffff0À33333³;@š™™™™™É?ÍÌÌÌÌL1ÀÍÌÌÌÌÌ@ffffffþ¿š™™™™™@š™™™™™Àš™™™™™É?à?333333#@333333)Àš™™™™™$@ÍÌÌÌÌÌ@š™™™™™(@@ffffffæ¿ffffff&@€5@ffffff4À"@š™™™™™'@š™™™™™@ÍÌÌÌÌÌô¿š™™™™™4@À333333,Àffffff;@ÍÌÌÌÌÌÀÍÌÌÌÌÌ'@ø¿š™™™™™@333333À3333339@ffffffÀ33333³3@ÍÌÌÌÌLB@333333@ÍÌÌÌÌÌì?š™™™™™ @ÍÌÌÌÌÌ$@.@ffffff@333333@š™™™™™ @333333$@(@š™™™™™"@š™™™™™!@333333@š™™™™™ À/ÀffffffÀš™™™™™"@€>@ÍÌÌÌÌÌÀffffff#@333333,À333333"@;@š™™™™Ù@@333333û¿ÍÌÌÌÌÌÀ@ffffff%Àš™™™™™ÀÍÌÌÌÌL0@ffffff"@ffffff!ÀÍÌÌÌÌÌ:@$@)@333333À333333,@ÍÌÌÌÌÌ-Àš™™™™0@)ÀffffffÀ€C@ffffffÀÍÌÌÌÌL;@ÍÌÌÌÌÌ$@333333;@ÍÌÌÌÌÌÀffffffö¿ÍÌÌÌÌÌÀ@ÍÌÌÌÌÌ@š™™™™™@ffffff@ffffffæ¿ÍÌÌÌÌÌ-@333333û?š™™™™™&@ÍÌÌÌÌÌ,À)@333333$@ @@333333$À333333ÀÍÌÌÌÌÌ@ffffff(@š™™™™™-@€=@ffffff@ffffff'@š™™™™™É¿333333)@À333333@333333@ÍÌÌÌÌÌ:@š™™™™™ù?ÍÌÌÌÌL0@ffffff ÀÍÌÌÌÌÌ@š™™™™5@-ÀÍÌÌÌÌ C@333333-@@š™™™™™É?š™™™™™Ù?š™™™™™¹¿333333ã?š™™™™™¹¿š™™™™™É¿š™™™™™É¿š™™™™™ñ?š™™™™™É¿š™™™™™Ù?š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹¿š™™™™™Ù?à?š™™™™™É¿š™™™™™É?š™™™™™É¿š™™™™™É?š™™™™™É¿š™™™™™¹¿à?š™™™™™¹?š™™™™™É¿š™™™™™¹?š™™™™™Ù?š™™™™™É?š™™™™™¹¿š™™™™™Ù¿š™™™™™¹?š™™™™™é?š™™™™™¹?š™™™™™¹¿š™™™™™É?š™™™™™¹¿š™™™™™Ù?š™™™™™¹?š™™™™™¹¿à¿š™™™™™¹¿à?š™™™™™¹?š™™™™™É¿ffffffæ?š™™™™™É?š™™™™™¹?š™™™™™¹¿333333Ó¿š™™™™™¹¿š™™™™™Ù¿š™™™™™¹¿š™™™™™É?š™™™™™Ù?š™™™™™É?333333Ó¿š™™™™™¹¿333333㿚™™™™™¹?š™™™™™¹?ffffffæ?š™™™™™É¿š™™™™™Ù?š™™™™™¹?š™™™™™É?š™™™™™Ù?š™™™™™¹¿333333Ó¿š™™™™™¹¿š™™™™™É¿š™™™™™¹¿à?š™™™™™¹?š™™™™™ñ¿ÍÌÌÌÌÌô¿à?333333û¿š™™™™™¹?š™™™™™É¿333333ã?š™™™™™Ù?à¿333333Ó¿š™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹¿š™™™™™Ù?333333û¿š™™™™™Ù?š™™™™™¹¿š™™™™™¹¿ð?333333Ó¿š™™™™™Ù¿à¿333333㿚™™™™™É¿š™™™™™¹¿333333ã?š™™™™™¹?š™™™™™É¿š™™™™™Ù¿ffffffæ?š™™™™™¹?ÍÌÌÌÌÌì?333333ã?š™™™™™¹¿š™™™™™É?š™™™™™Ù¿ffffffö¿à¿š™™™™™Ù?š™™™™™¹¿ffffffæ?à?š™™™™™Ù¿š™™™™™¹?š™™™™™¹¿š™™™™™¹?à?333333Ó¿š™™™™™É¿à¿š™™™™™¹?333333Ó¿š™™™™™É?࿚™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿š™™™™™Ù?š™™™™™¹?333333Ó¿š™™™™™É¿š™™™™™¹¿š™™™™™¹?࿚™™™™™¹?š™™™™™¹?š™™™™™É?ÍÌÌÌÌÌô¿333333㿚™™™™™¹¿š™™™™™¹?333333Ó¿š™™™™™É?š™™™™™É?š™™™™™É¿333333Ó?333333Ó?š™™™™™¹?š™™™™™¹¿š™™™™™É¿333333ã?š™™™™™¹?š™™™™™Ù?š™™™™™É?š™™™™™Ù¿à¿š™™™™™Ù¿š™™™™™É¿ÍÌÌÌÌÌ쿚™™™™™É?š™™™™™É?š™™™™™É?࿚™™™™™¹?à?333333ã¿à¿ÍÌÌÌÌÌì¿333333Ó?š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹?à¿333333Ó¿š™™™™™É?࿚™™™™™¹¿š™™™™™¹¿333333Ó?š™™™™™Ù¿š™™™™™É¿š™™™™™Ù?š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿š™™™™™Ù?š™™™™™É?š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™é¿š™™™™™Ù?333333㿚™™™™™É¿333333Ó¿š™™™™™Ù¿š™™™™™¹¿à?333333ã?š™™™™™¹¿333333ã?š™™™™™É?à?ffffffæ?333333Ó?ffffffæ?š™™™™™é?š™™™™™Ù?š™™™™™É¿š™™™™™¹?࿚™™™™™É¿333333ã?š™™™™™É¿333333Ó¿š™™™™™¹?š™™™™™É¿333333Ó?š™™™™™Ù?š™™™™™¹?š™™™™™¹?࿚™™™™™É¿š™™™™™Ù¿š™™™™™¹?š™™™™™É¿ffffffæ?333333ã?š™™™™™Ù?š™™™™™¹¿š™™™™™É?š™™™™™¹¿333333㿚™™™™™É?š™™™™™ñ¿š™™™™™Ù¿ÍÌÌÌÌÌì¿333333Ó?š™™™™™Ù?š™™™™™¹?š™™™™™¹?š™™™™™ñ¿333333ã?š™™™™™É?š™™™™™É¿ffffff濚™™™™™É?ffffffö¿à?š™™™™™¹?࿚™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™é?š™™™™™É?à?š™™™™™É¿333333Ó¿333333ã?š™™™™™Ù¿š™™™™™Ù?à?333333ó¿š™™™™™Ù¿š™™™™™Ù?š™™™™™É?š™™™™™Ù¿ffffffö?š™™™™™ñ?š™™™™™Ù¿333333Ó¿š™™™™™É?š™™™™™É¿š™™™™™Ù¿š™™™™™É¿333333ã?š™™™™™Ù?࿚™™™™™Ù?š™™™™™¹¿à¿333333㿚™™™™™É¿š™™™™™¹?à?333333Ó?ffffffæ?š™™™™™Ù¿š™™™™™Ù¿à¿š™™™™™¹¿š™™™™™¹¿à?š™™™™™¹¿š™™™™™É?à?š™™™™™é¿333333Ó¿š™™™™™É¿š™™™™™É¿š™™™™™¹¿š™™™™™Ù¿š™™™™™Ù?ffffffæ?š™™™™™¹?333333Ó¿š™™™™™Ù¿š™™™™™¹?à¿333333Ó?ÍÌÌÌÌÌ쿚™™™™™¹¿333333㿚™™™™™¹¿ffffffæ?ffffffö?š™™™™™É¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™é¿š™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌì?333333ã?333333㿚™™™™™é?ð?ffffffæ?š™™™™™É?š™™™™™Ù?š™™™™™É?š™™™™™¹¿š™™™™™é¿š™™™™™¹¿š™™™™™Ù¿333333ã?š™™™™™¹?ffffffæ¿333333Ó¿333333㿚™™™™™É?š™™™™™¹¿333333ã?š™™™™™¹¿@333333ã?ÍÌÌÌÌÌ쿚™™™™™É¿š™™™™™¹?š™™™™™é¿š™™™™™Ù¿š™™™™™Ù?š™™™™™¹?࿚™™™™™¹?333333Ó¿š™™™™™É¿š™™™™™¹¿ÍÌÌÌÌÌô¿š™™™™™Ù¿š™™™™™É?š™™™™™¹¿š™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™¹¿š™™™™™é?ffffffö?ð?š™™™™™¹?Àš™™™™™ù¿š™™™™™ù¿à¿333333Àffffffþ¿š™™™™™Ù¿À333333Àffffffö¿š™™™™™Ù¿ffffffæ¿333333Àš™™™™™é¿333333ó¿ð¿ffffffæ¿ð¿š™™™™™é¿333333ó¿ffffffæ¿333333ã¿ÍÌÌÌÌÌì¿ø¿333333Àš™™™™™ñ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ÍÌÌÌÌÌÀÍÌÌÌÌÌ쿚™™™™™é¿333333Àffffff Àffffff濚™™™™™ñ¿š™™™™™Ù¿333333ã¿ð¿š™™™™™ñ¿333333ã¿ffffffæ¿333333㿚™™™™™Ù¿š™™™™™Ù¿š™™™™™Ù¿333333Àš™™™™™Ù¿333333Àš™™™™™À333333ÀffffffÀffffffæ¿333333À333333ã¿333333ã¿ÍÌÌÌÌÌÀffffffæ¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™ù¿333333ã¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿333333 Àffffffæ¿Àffffffæ¿ÍÌÌÌÌÌì¿ffffffö¿ÍÌÌÌÌÌü¿ffffffö¿ffffff À333333Àš™™™™™Ù¿ÍÌÌÌÌÌü¿À333333ó¿š™™™™™ñ¿333333ó¿333333ó¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿š™™™™™Ù¿à¿ffffffæ¿333333ã¿à¿ÍÌÌÌÌÌÀ333333ã¿333333ã¿333333㿚™™™™™Ù¿ÍÌÌÌÌÌì¿à¿333333ã¿ffffffö¿333333ó¿š™™™™™Ù¿333333ã¿333333Àffffffæ¿333333ã¿333333û¿š™™™™™Ù¿333333 Àð¿à¿333333ã¿333333Àš™™™™™Ù¿š™™™™™Ù¿à¿š™™™™™é?š™™™™™@š™™™™™@ffffff@fffffæ3@333333@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ!@š™™™™™@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ @333333@333333!@š™™™™™@š™™™™™ @333333ã?333333@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ!@333333û?š™™™™™!@ffffff@ffffffæ?@ffffff@@333333 @ÍÌÌÌÌÌ@ffffff @š™™™™™@ffffffö?š™™™™™é?ÍÌÌÌÌÌ@š™™™™™@@š™™™™™*@š™™™™™ @333333û?333333@š™™™™™@š™™™™™Ù? @ffffff@333333@ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@ffffff@333333@333333@333333!@š™™™™™@333333@š™™™™™@š™™™™™&@333333+@ffffff @333333@ÍÌÌÌÌÌ#@333333@š™™™™™Ù?ÍÌÌÌÌÌ @š™™™™™@@ffffffæ?ÍÌÌÌÌÌ@#@@@333333'@@@š™™™™™#@@š™™™™™@ffffff@ffffff@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@ffffff@333333ó?š™™™™™é?ÍÌÌÌÌÌì?ffffff@333333@ffffff @333333@ÍÌÌÌÌÌ @š™™™™™@@ffffff)@ffffff"@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff @ffffff @ÍÌÌÌÌÌ3@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@ffffff$@ffffff@ffffff@ffffff@ÍÌÌÌÌÌô?ø?333333@333333@ÍÌÌÌÌÌü?à?ffffff@333333@š™™™™™@@333333@ffffffþ?ffffffþ?ÍÌÌÌÌÌ!@š™™™™™@ÍÌÌÌÌÌ@ffffff@333333@š™™™™™"@#@333333@ffffff$@ÍÌÌÌÌÌ@#@@ø?!@ffffff@ @ffffff#@š™™™™™@ffffff@@@ffffff@ffffffæ?š™™™™™ @ffffff@@@ÍÌÌÌÌÌ@@š™™™™™ñ?333333@333333@ffffff@333333@š™™™™™!@ÍÌÌÌÌÌ@š™™™™™Ù?@š™™™™™@ffffff@ð?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@š™™™™™@š™™™™™@š™™™™™ñ?333333@@š™™™™™@@š™™™™™@š™™™™™ @ffffff@š™™™™™é?333333ó?ffffff@ÍÌÌÌÌÌü?@ÍÌÌÌÌÌì?@š™™™™™Ù?š™™™™™@š™™™™™@ÍÌÌÌÌÌ@*@ffffff@€1@ffffff@ð?š™™™™™é?ffffffæ?š™™™™™@)@ffffff#@š™™™™™Ù?333333@š™™™™™,@@ÍÌÌÌÌÌ'@"@ÍÌÌÌÌÌü?/@@ÍÌÌÌÌÌ@ffffff@ø?ÍÌÌÌÌÌ@@š™™™™™ñ?333333û?ÍÌÌÌÌÌ@š™™™™™ñ?@333333 @š™™™™™@ÍÌÌÌÌÌ@š™™™™™@ffffff$@ffffff@š™™™™™@@$@333333-@ffffffö?333333@333333@@š™™™™™@š™™™™™@@ÍÌÌÌÌÌô?333333 @ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?333333$@@333333ó?ffffff @ÍÌÌÌÌÌ @ÍÌÌÌÌÌü?ø?333333 @333333@š™™™™™@ÍÌÌÌÌÌ@ffffff@333333ó?ffffffþ?@ffffff@š™™™™™@ffffff@š™™™™™@ffffffæ?ffffff@ffffff @ÍÌÌÌÌÌ@š™™™™™ @ @š™™™™™@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ffffffæ?333333ó?@ffffffþ?333333$@333333@š™™™™™@ø?(@ÍÌÌÌÌÌ@š™™™™™é?@š™™™™™,@à?ffffff @@š™™™™™ù? @š™™™™™Ù?ÍÌÌÌÌÌü?š™™™™™@š™™™™™@ffffff$@@ÍÌÌÌÌÌì?ffffff@š™™™™™@ÍÌÌÌÌÌ @@š™™™™™@@333333 @ÍÌÌÌÌÌ@333333@ffffff@333333û?ffffff@ÍÌÌÌÌÌ$@š™™™™™@à?ÍÌÌÌÌÌ @ð?š™™™™™Ù?333333@@ffffffæ?@š™™™™™&@ÍÌÌÌÌÌ@333333@ø?ffffff@ffffff"@š™™™™™ @@333333!@š™™™™™@333333@ffffff@333333@ffffff@@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@à?ffffff@š™™™™™@š™™™™™@š™™™™™@333333ã?@à?š™™™™™@ffffff@333333 @š™™™™™ @333333@ffffffþ?333333@š™™™™™é?333333@à?ffffff)@ÍÌÌÌÌÌ@ffffff@ffffff@š™™™™™@ffffff@š™™™™™ @š™™™™™@333333ã?š™™™™™ù?@333333@333333 @ÍÌÌÌÌÌ@š™™™™™#@@@ÍÌÌÌÌÌ&@ffffff@ffffff$@333333@ffffff @@š™™™™™@ffffff @*@š™™™™™@š™™™™™@š™™™™™ @š™™™™™@š™™™™™ñ?333333û?ffffff@ÍÌÌÌÌL0@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@333333@333333@333333@333333@333333û?ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?333333(@š™™™™™@š™™™™™!@ @333333-@333333@š™™™™™@š™™™™™ @ffffff@333333 @ÍÌÌÌÌÌ%@333333@š™™™™™@š™™™™™@333333@š™™™™™ @š™™™™™@š™™™™™@š™™™™0@š™™™™™ @š™™™™™ @ffffffæ?š™™™™™@š™™™™™@ffffff@333333@š™™™™™@ÍÌÌÌÌÌ@ @333333@333333@@@@333333@š™™™™™Ù?333333#@ffffffö?ffffff@ÍÌÌÌÌÌ@Àffffffæ¿ÍÌÌÌÌÌì¿333333ó¿à¿ffffffæ¿ffffffþ¿ø¿ffffffÀ333333ó¿333333ó¿333333ÀÍÌÌÌÌÌô¿333333ÀffffffÀÀ333333ó¿ÍÌÌÌÌÌ À333333ó¿ð¿š™™™™™#ÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿Àš™™™™™ Àš™™™™™ñ¿Àš™™™™™ÀffffffÀš™™™™™ù¿333333㿚™™™™™ À333333 À333333ÀÀš™™™™™ñ¿š™™™™™ñ¿ð¿333333 Àš™™™™™ÀÍÌÌÌÌÌÀš™™™™™Àffffffþ¿ffffffö¿ÍÌÌÌÌÌ À333333ó¿333333ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀ333333ã¿ÀffffffÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀš™™™™™Àffffffþ¿ffffffÀffffffö¿333333À ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌ Àffffffþ¿ffffffÀš™™™™™Ù¿ffffffÀ𿚙™™™™é¿ÍÌÌÌÌÌÀ ÀÍÌÌÌÌÌÀffffff À333333ó¿ÍÌÌÌÌÌÀø¿à¿ÍÌÌÌÌÌÀÀ333333À333333ó¿š™™™™™ ÀÍÌÌÌÌÌü¿ÍÌÌÌÌÌì¿ffffffÀš™™™™™ ÀÀš™™™™™ Àø¿ffffffö¿ffffff Àš™™™™™ÀÍÌÌÌÌÌì¿333333ÀffffffÀffffff À333333ó¿ffffffÀš™™™™™À333333ã¿333333Àð¿333333ó¿ÍÌÌÌÌÌÀš™™™™™ Àð¿ÀffffffÀffffff Àš™™™™™Ù¿ÍÌÌÌÌÌô¿Àš™™™™™ñ¿ÍÌÌÌÌÌô¿ffffffæ¿ð¿š™™™™™Àø¿ffffffö¿ÀÀÀ333333ã¿333333ã¿ffffffþ¿š™™™™™ÀÍÌÌÌÌÌô¿š™™™™™é¿ð¿ð¿ffffffþ¿ffffffþ¿ffffff濚™™™™™ ÀffffffÀffffffÀ333333 ÀffffffÀffffffæ¿ÍÌÌÌÌÌ ÀffffffÀ333333ã¿333333ÀÍÌÌÌÌÌÀ333333À333333 Àø¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀš™™™™™ÀÀš™™™™™é¿š™™™™™ù¿333333㿚™™™™™ù¿ø¿š™™™™™é¿ÀÀÍÌÌÌÌÌü¿ffffff'ÀÀš™™™™™ÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌü¿š™™™™™Àš™™™™™é¿ð¿333333û¿333333ó¿ÍÌÌÌÌÌÀ333333㿚™™™™™ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333ó¿333333$ÀÍÌÌÌÌÌÀš™™™™™é¿ffffffæ¿ffffffö¿ffffffö¿š™™™™™Ù¿ÍÌÌÌÌÌì¿333333ÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌì¿333333 Àø¿333333ó¿ÍÌÌÌÌÌü¿ø¿ð¿ffffff Àš™™™™™Àš™™™™™ÀÍÌÌÌÌÌÀš™™™™™#À333333ó¿ffffffÀÍÌÌÌÌÌô¿ffffffþ¿à¿š™™™™™ù¿ Àš™™™™™ñ¿ffffffÀÀffffff%ÀÍÌÌÌÌÌÀš™™™™™Ù¿š™™™™™Ù¿ÍÌÌÌÌÌü¿333333À!À333333À333333ó¿ffffff ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™ñ¿333333㿚™™™™™ÀÍÌÌÌÌÌ.Àà?333333"@š™™™™™é?ÍÌÌÌÌÌü?à?š™™™™™ù?š™™™™™Ù?ð?333333@ffffffæ?ÍÌÌÌÌÌô?à?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì¿Àffffff Àð?š™™™™™É¿333333ã?333333Ó?ffffffö?ð?ffffffæ?š™™™™™É?š™™™™™ù?š™™™™™é?ÍÌÌÌÌÌô?š™™™™™é?ffffffæ?333333ã?à?š™™™™™é?ÍÌÌÌÌÌô?š™™™™™¹?ffffffæ?š™™™™™é?à?š™™™™™ñ?ffffffæ?333333ã?ffffffæ?š™™™™™Ù?333333ó?š™™™™™Ù?333333ã?333333Ó?š™™™™™é?ffffffæ?à?ffffffæ?š™™™™™ù?ffffffæ?ffffffæ?š™™™™™É¿š™™™™™é?ffffffæ?à?ÍÌÌÌÌÌì?ffffffö?ÍÌÌÌÌÌì?333333ã?š™™™™™ñ?ÍÌÌÌÌÌô?333333Ó?ffffffæ?ffffffö?ffffffæ?ffffffæ?ð?333333ã?333333ã?š™™™™™Ù?333333Ó?ÍÌÌÌÌÌì?š™™™™™ñ?333333ã?ø?333333ó?ffffffö?ffffffæ?š™™™™™¹¿ð?ÍÌÌÌÌÌì?š™™™™™É?333333ã?ÍÌÌÌÌÌì?ffffffæ?š™™™™™É?ffffffö?333333ã?ffffffæ?š™™™™™é?š™™™™™é?333333ã?ÍÌÌÌÌÌü?š™™™™™ñ?ð?š™™™™™ù?à?š™™™™™é?š™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌì?ð?ffffffæ?à?ffffffæ?ð?333333ã?š™™™™™ù?333333Ó?ffffffæ?ffffffæ?333333Ó?ffffffæ?ffffffö?ffffffæ?š™™™™™ñ?š™™™™™é?333333ã?ffffffæ?š™™™™™É?ffffffæ?𿚙™™™™Ù?š™™™™™Ù?š™™™™™é?ð?š™™™™™¹?333333Ó?š™™™™™é?š™™™™™¹¿333333Ó?333333ã?ffffffæ?ffffffæ?ffffffæ?333333ã?ð?š™™™™™é?333333Ó?ffffffæ?ð?š™™™™™Ù?š™™™™™é?š™™™™™é?ð?ffffffæ?ø?š™™™™™ñ?š™™™™™é?ffffffæ?ð?333333ó?333333ã?ð?š™™™™™Ù?ð?š™™™™™é?š™™™™™¹?ÍÌÌÌÌÌì?ð?333333Ó?ø?š™™™™™ñ?š™™™™™Ù?à?ffffffæ?333333ã?333333Ó?š™™™™™é?š™™™™™ñ?à?š™™™™™Ù?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™é?333333ã?š™™™™™é?333333ã?333333Ó?333333û?à?š™™™™™é?š™™™™™Ù?ÍÌÌÌÌÌô?š™™™™™é?š™™™™™É?š™™™™™Ù¿š™™™™™Ù¿š™™™™™é?333333Ó?ffffffæ?š™™™™™Ù?š™™™™™ñ?ÍÌÌÌÌÌì?ffffffæ?333333ã?š™™™™™ñ?333333ã?ÍÌÌÌÌÌô?333333ã?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™¹¿š™™™™™ñ?ffffffæ?š™™™™™Ù?š™™™™™é?333333ã?ÍÌÌÌÌÌì?333333ã?š™™™™™@ffffffæ¿à¿š™™™™™É?333333ã?š™™™™™Ù?š™™™™™Ù?š™™™™™é?ffffffæ?333333㿚™™™™™Ù?ÍÌÌÌÌÌì?333333ó?š™™™™™ù?š™™™™™É?ø?š™™™™™ñ?š™™™™™É?ÍÌÌÌÌÌì?ð?333333ã?ð?à?š™™™™™é?à?š™™™™™ù?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™¹¿š™™™™™¹¿333333ó?š™™™™™ñ?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌô?333333ã?333333ó?š™™™™™ñ?š™™™™™ñ?333333ó?ÍÌÌÌÌÌô?š™™™™™Ù?š™™™™™é?š™™™™™É¿š™™™™™É?ÍÌÌÌÌÌô?ð?ð?ð?ÍÌÌÌÌÌô?ð?333333ã?ffffffæ?š™™™™™Ù¿š™™™™™ñ?š™™™™™ñ?ÍÌÌÌÌÌì?š™™™™™Ù?ÍÌÌÌÌÌì?ffffffö?ø?ð?333333Ó?ffffffæ?š™™™™™¹?333333ó?š™™™™™ñ?à?š™™™™™é?š™™™™™¹¿333333Ó?š™™™™™É¿333333ã?š™™™™™Ù?š™™™™™Ù?š™™™™™é?333333ó?333333Ó?š™™™™™é?š™™™™™É¿š™™™™™É?š™™™™™é?333333Ó?š™™™™™Ù?333333Ó¿à?333333ã?333333ã?š™™™™™é?333333Ó?à?ø?ÍÌÌÌÌÌì?333333ã?š™™™™™é?ffffffæ?š™™™™™Ù?š™™™™™ñ?š™™™™™É?š™™™™™Ù?333333Ó?333333ã?š™™™™™ñ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™é?à?ÍÌÌÌÌÌì¿333333ó?š™™™™™¹¿š™™™™™é?à?ÍÌÌÌÌÌì?ð?ÍÌÌÌÌÌô?ffffffæ¿333333ó?ffffffæ?š™™™™™é?ÍÌÌÌÌÌì?333333ã?333333ã?š™™™™™é?ÍÌÌÌÌÌô?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?333333ã?š™™™™™é?š™™™™™Ù¿ffffff@ð?š™™™™™é?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?à?š™™™™™é?š™™™™™ñ?š™™™™™é?š™™™™™Ù?š™™™™™ñ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ð?ffffffæ?ÍÌÌÌÌÌü?333333ã?333333Ó¿š™™™™™Ù?š™™™™™É?@ÍÌÌÌÌÌô?à?à?š™™™™™Ù?ð?š™™™™™¹?333333Ó?š™™™™™é?à?333333Ó?333333Ó?ð?333333ó?333333Ó?ÍÌÌÌÌÌô?š™™™™™¹?ffffffæ?à?333333ã?à?š™™™™™¹?ð?333333ó?à?ð?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù?ffffffæ?š™™™™™ñ¿333333ã?š™™™™™Ù¿à?ð?ffffffæ?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™¹¿ÍÌÌÌÌÌì?š™™™™™é?ÍÌÌÌÌÌì?ffffffæ?š™™™™™Ù?š™™™™™Ù¿š™™™™™É¿333333ã?à?ø?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™Ù¿ð?š™™™™™Ù?333333㿚™™™™™ñ?ð?à?333333û?333333Ó?š™™™™™é?š™™™™™é?š™™™™™ñ?333333ã?333333ó?š™™™™™é?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333ã?š™™™™™¹¿š™™™™™Ù?ffffffæ?ð?333333Ó?š™™™™™ñ?ffffffö?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?à?ÍÌÌÌÌÌì?333333ã?ffffffæ?333333ã?à?à?š™™™™™Ù?ð?à?à?ÍÌÌÌÌÌÀÍÌÌÌÌÌì¿ffffffæ?š™™™™™Ù?š™™™™™é?ffffffö?333333ã?333333Ó?ffffff @ffffffæ?333333Ó?ffffff@ffffffæ?ð?333333ó?š™™™™™Ù?š™™™™™ñ?ð?ffffffö?š™™™™™@š™™™™™Ù?š™™™™™Ù¿š™™™™™é?ÍÌÌÌÌÌü?ffffffö?333333ó?š™™™™™É?333333ã?@ø?ø?333333ã¿à?ffffffæ?à?ffffffö?š™™™™™ñ?ffffffæ?š™™™™™é?š™™™™™Ù?333333û?333333Ó?š™™™™™É¿š™™™™™É?š™™™™™É?š™™™™™ñ?333333ó?š™™™™™É?š™™™™™ñ?š™™™™™¹?333333Ó¿333333ó?333333Ó?333333ó?ffffffæ?333333Ó?ÍÌÌÌÌÌô?333333ã?ø?333333ã?š™™™™™¹?š™™™™™É¿333333ó?ffffffæ?333333ó?š™™™™™@ÍÌÌÌÌÌ@ffffffþ?à?š™™™™™¹?ÍÌÌÌÌÌü?š™™™™™é?š™™™™™ñ?333333ó?ð?ÍÌÌÌÌÌì?à?š™™™™™@ffffff @š™™™™™Ù¿à¿ÍÌÌÌÌÌü?š™™™™™Ù?š™™™™™¹?333333û?333333Ó?ð?ffffff@š™™™™™é¿ffffffæ?š™™™™™É?ø¿333333ó?š™™™™™Ù¿š™™™™™é?à?š™™™™™É?333333û?333333û?à?ffffff@333333㿚™™™™™Ù¿š™™™™™Ù?ffffffæ?š™™™™™ñ?š™™™™™@à?333333Ó?à?š™™™™™é?333333ó¿š™™™™™Ù?à?ð?ø?ffffff@333333ã?ð?š™™™™™é?ffffffæ?à?š™™™™™ñ?ÍÌÌÌÌÌô?ffffffæ?š™™™™™ù?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™é?ð?@333333ã?à?333333û?š™™™™™ù?ffffffö?ÍÌÌÌÌÌ@@à?ÍÌÌÌÌÌì?ffffffö?ð?ffffffö?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@š™™™™™Ù?ffffffö?š™™™™™Ù?à?ffffffæ?ÍÌÌÌÌÌü?ffffffæ?ÍÌÌÌÌÌì?š™™™™™É?ÍÌÌÌÌÌô?š™™™™™ñ?333333Ó?à?333333ó?ÍÌÌÌÌÌô?ø?š™™™™™Ù¿š™™™™™@ø?ÍÌÌÌÌÌü?à?333333ó?ÍÌÌÌÌÌ@333333ã?š™™™™™ñ?š™™™™™é?ÍÌÌÌÌÌì?@333333ó?ÍÌÌÌÌÌô?333333Ó?ffffffæ?ffffffæ?š™™™™™é?š™™™™™É¿š™™™™™Ù¿ffffff@333333ã?ð¿à?ÍÌÌÌÌÌì?à¿333333ã?à¿ø?š™™™™™é?333333Ó?ð?š™™™™™é¿333333ã?š™™™™™ñ?š™™™™™ù?333333ã?ÀÍÌÌÌÌÌô?š™™™™™¹¿š™™™™™é?333333Ó?ø?ffffff @@š™™™™™ñ?ÍÌÌÌÌÌì?ð?ffffff@@š™™™™™@š™™™™™ù¿ffffffæ¿ÍÌÌÌÌÌô?333333Ó?š™™™™™ñ¿š™™™™™ñ?š™™™™™Ù¿ffffffö?à?ÍÌÌÌÌÌô?@333333ó?š™™™™™É?š™™™™™Ù?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌô?š™™™™™Ù?ffffffæ?333333@333333ã?ÍÌÌÌÌÌ@333333ã?ffffffö?333333ã?à?333333Ó?ÍÌÌÌÌÌì?š™™™™™¹?333333Ó?š™™™™™é¿ø?š™™™™™é¿333333ó?ffffffö?ffffffæ?š™™™™™Ù?ø?š™™™™™É¿333333ã?333333ó?@ø?š™™™™™é¿ÍÌÌÌÌÌì?š™™™™™Ù?ÍÌÌÌÌÌì?333333Ó?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?333333ã?à?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™@333333@š™™™™™Ù?࿚™™™™™É?ffffffæ?333333Ó?333333ó?333333ã?ÍÌÌÌÌÌô?š™™™™™é?š™™™™™Ù?š™™™™™Ù?333333ó?š™™™™™é?š™™™™™é?ÍÌÌÌÌÌô?š™™™™™Ù¿š™™™™™¹¿333333ã?ffffff @à?ÍÌÌÌÌÌô¿š™™™™™é¿š™™™™™é?š™™™™™É¿333333û?à?333333ó?ÍÌÌÌÌÌô¿à?à?ffffffþ?ÍÌÌÌÌÌì¿ffffffö?333333Ó?333333Ó?ffffffö¿š™™™™™@ÍÌÌÌÌÌô?ffffffæ¿ffffff@š™™™™™É?š™™™™™é?ÍÌÌÌÌÌì?ffffffæ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?333333Ó?š™™™™™Ù?333333ã?ÍÌÌÌÌÌì¿@à?ÍÌÌÌÌÌô?333333û?š™™™™™É?333333ã?š™™™™™É?333333Ó?ÍÌÌÌÌÌô?š™™™™™Ù?ÍÌÌÌÌÌ@ø?š™™™™™Ù?333333ó?333333@š™™™™™É?ø?333333Ó?ffffffæ?ffffffæ?š™™™™™ù?š™™™™™ñ?333333ã?333333ó?ffffffæ?š™™™™™é?š™™™™™ù?š™™™™™É¿ffffffö?š™™™™™Ù?ð?ffffffö?@š™™™™™é?ÍÌÌÌÌÌ@š™™™™™Ù¿à¿ffffff@à?ffffffþ?š™™™™™é?š™™™™™Ù?333333Ó?333333ã?à?333333ã¿ÍÌÌÌÌÌô?333333Ó?š™™™™™Ù?@ffffff@333333Ó?333333û?š™™™™™é?ffffffþ?š™™™™™é?š™™™™™É?ø¿ffffffæ¿à¿š™™™™™@ffffffæ¿@š™™™™™Ù?ÍÌÌÌÌÌô?ffffffæ?š™™™™™ù?ÍÌÌÌÌÌ@333333Ó?ÍÌÌÌÌÌô?333333ã?ÍÌÌÌÌÌô¿ø?ffffffö?ffffff@à?333333ó?ffffffþ?333333ã?ÍÌÌÌÌÌô?ø?@ÍÌÌÌÌÌô?ð?333333ã?š™™™™™Ù¿š™™™™™Ù?333333ã¿333333ã¿ð?ð?࿚™™™™™ù¿š™™™™™É?ffffffæ?333333@333333ó¿š™™™™™É¿@š™™™™™ù?@ð?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù?ffffffö?333333ó¿ÍÌÌÌÌÌì?ffffff@333333ã?š™™™™™é?à?@333333ó?š™™™™™¹¿š™™™™™É?ø?š™™™™™Ù?333333Ó¿333333Ó?333333ó?š™™™™™ù?ffffffö?š™™™™™¹?ffffffþ?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?333333Àffffff@ÍÌÌÌÌL?@š™™™™™#@ À333333D@ffffff,@ffffff-@ÍÌÌÌÌÌ@33333³:@ffffff2@ffffffæ?ÍÌÌÌÌÌ$À€;@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@fffffæ3@%@š™™™™™%@ffffffÀš™™™™™&@€2@š™™™™™5@š™™™™™Ù?š™™™™™5@33333³3À33333³4@333333 @ÍÌÌÌÌÌÀ3333338À3333331Àø?#@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@ÍÌÌÌÌL3@ÍÌÌÌÌÌ#@&@š™™™™™Ù?fffff&AÀÍÌÌÌÌÌ"@š™™™™™/@ÍÌÌÌÌÌ3@333333?@ÍÌÌÌÌÌÀÍÌÌÌÌÌÀ @ @,@š™™™™™ÀÍÌÌÌÌÌ@š™™™™™@fffff¦B@333333+@333333+@š™™™™™(@333333û¿&@ÍÌÌÌÌ B@333333,@ÍÌÌÌÌÌ @333333-@š™™™™™7@š™™™™YE@š™™™™™@ÍÌÌÌÌÌ@33333³<@333333:@333333ÀÍÌÌÌÌÌ,@333333!@333333*@333333'Àš™™™™8@ÍÌÌÌÌÌ,@ffffff0@333333@33333³?@ÍÌÌÌÌÌ6@ffffff7@1@ffffff$@333333À333333?@š™™™™™4@333333E@333333/@š™™™™™3@š™™™™™*@@ffffffö¿š™™™™™ñ?333333Àð¿333333@!@ÍÌÌÌÌÌ$@333333@,@ÍÌÌÌÌÌ쿚™™™™YF@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ4@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff&@ÍÌÌÌÌÌB@ffffff$@ffffffÀš™™™™™@2@ffffff9@€4@š™™™™™é?,@333333ÀÍÌÌÌÌÌÀ3@š™™™™™@333333Ó?ffffff2À333333û?ÍÌÌÌÌÌ @š™™™™™0@333333ó¿!@333333@ffffffÀ33333³:@333333+@ffffff2@š™™™™1@333333&@š™™™™YE@fffffæ;@ÍÌÌÌÌÌ@€8@333333%@ÍÌÌÌÌ B@ffffff"@Àš™™™™™>@ÍÌÌÌÌÌì?à?3333334@ÍÌÌÌÌÌ@333333@&@"@333333@ffffffÀffffff@ffffff3@@š™™™™™3@ÍÌÌÌÌL6@5@š™™™™™À3333335@ffffffÀ3@ÍÌÌÌÌÌ@š™™™™™8@š™™™™™-@ffffff"Àð¿333333)@ffffff@ð?33333³0ÀÍÌÌÌÌÌ@*@š™™™™™0@ffffffö¿š™™™™Y@@5@ÍÌÌÌÌÌÀ333333ó¿:@š™™™™™@ÍÌÌÌÌÌ@š™™™™™%@ÍÌÌÌÌÌ%ÀÍÌÌÌÌÌÀÍÌÌÌÌL3@@ÍÌÌÌÌÌ#@=À333333 À33333³8@ÍÌÌÌÌÌ%Àš™™™™1@333333#@ÍÌÌÌÌÌ @€D@ÍÌÌÌÌÌÀš™™™™E@33333³7@à?š™™™™™ñ?ÍÌÌÌÌÌÀÍÌÌÌÌÌ)@€C@33333sA@š™™™™™À333333(@33333³I@ÍÌÌÌÌÌ4@š™™™™™@@333333@ÍÌÌÌÌ P@@ø?333333/@@à?33333³4@ÍÌÌÌÌÌ@ffffffÀš™™™™™:@š™™™™™@333333%@ffffff,@š™™™™™&@333333@ð?fffffæA@š™™™™™(@@333333.@E@D@ÍÌÌÌÌÌô?3333339@ÍÌÌÌÌÌ#@ffffff@4@ffffff,@š™™™™™É?333333@š™™™™™%@3333332@ÍÌÌÌÌÌ À333333<@333333#@ffffff Àš™™™™™ñ¿ÍÌÌÌÌÌÀffffff@ffffff4Àš™™™™1À*ÀÍÌÌÌÌÌ@ø?ffffff$@š™™™™™-@ffffff1@ffffff À𿚙™™™™)@ffffff2@333333/@@š™™™™™1@ÍÌÌÌÌÌÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌ@ffffff:@ffffff1@ÍÌÌÌÌÌ$@š™™™™™@333333@š™™™™™ù?@3333335@333333/@ÍÌÌÌÌÌ5@333333ÀÍÌÌÌÌÌ(Àš™™™™™ñ?ffffffÀÍÌÌÌÌÌ À&Àš™™™™=@3333331@ÍÌÌÌÌÌ@š™™™™™À3333331@333333@š™™™™1Àš™™™™1@ÍÌÌÌÌ F@ÍÌÌÌÌÌÀ333333@š™™™™™@š™™™™™"@333333ó¿Àš™™™™™.ÀÍÌÌÌÌÌ@š™™™™<@333333@ÍÌÌÌÌÌì?ÍÌÌÌÌÌ3@ÍÌÌÌÌL2@ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™À@33333³0@333333>@ffffff"@fffffæ1@ÍÌÌÌÌÌ@333333@3333334@ÍÌÌÌÌÌô¿@ÍÌÌÌÌÌ2À$@ffffffö¿ÍÌÌÌÌL@Àš™™™™™$@š™™™™™.@ø?ÍÌÌÌÌL4@ÍÌÌÌÌ L@3@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333(@š™™™™™6@33333³0@333333@€3@ÍÌÌÌÌÌ@333333 @ffffff@ÍÌÌÌÌL0@ÍÌÌÌÌL0@š™™™™1@ffffffÀ333333=@š™™™™™É¿š™™™™™@ÍÌÌÌÌÌ@3333333@$À$Àffffff:@ÍÌÌÌÌÌ@À€4@ÍÌÌÌÌÌì¿9@333333<@ffffff-@@(@ffffffÀš™™™™™@š™™™™™Àffffff?@ÍÌÌÌÌÌ@š™™™™™'@333333 @ÍÌÌÌÌÌ@š™™™™™/@ð¿ÍÌÌÌÌÌ)@š™™™™™É¿ffffff7Àš™™™™™@ÍÌÌÌÌÌ3@"@š™™™™™@ÍÌÌÌÌL;@ffffff7@ÍÌÌÌÌÌ@ÍÌÌÌÌL>@ffffff>@€0@fffffæ0@33333óAÀ33333³3Àffffff@3333339@ffffff6@33333³H@š™™™™™*@-@ffffff$@333333(@ÍÌÌÌÌÌ=Àš™™™™™@ÍÌÌÌÌL0@€<@ @À(@ÍÌÌÌÌL7@.@š™™™™™1@ffffff,@333333 @š™™™™™'@ÍÌÌÌÌÌü¿ÍÌÌÌÌL=@fffffæ9@š™™™™™A@3333335@fffffæ=@š™™™™™Ù¿š™™™™™@333333 @333333/@ÍÌÌÌÌÌ)@ÍÌÌÌÌL7@ffffffÀš™™™™™,À3333331@333333#@333333À@€7@333333-@)@333333Àš™™™™™3À333333#Àš™™™™™1@ÍÌÌÌÌÌ!@33333³;@333333û?ÍÌÌÌÌŒ@@ffffff6@3@š™™™™™"À333333@333333@(@š™™™™™)@ÍÌÌÌÌÌ'À333333B@FÀÍÌÌÌÌÌ4@ÍÌÌÌÌÌ%@š™™™™™Àffffff'@š™™™™YE@333333)@ffffffþ¿š™™™™YP@š™™™™1@ÍÌÌÌÌÌ0@ÍÌÌÌÌÌ6@333333>@3333339@%@Àfffff&A@33333³D@€1@ÍÌÌÌÌ B@333333)@ffffff @ð¿33333³:@š™™™™™:@ÍÌÌÌÌÌ<@333333ó?33333³8@ffffffæ?333333?@333333.@fffffæ2Àffffff0Àš™™™™™*Àš™™™™™@ffffff4@333333%@ÍÌÌÌÌÌ#@€8@š™™™™™(@ÍÌÌÌÌL:@@š™™™™™AÀ333333%@3333331@33333³9@33333óB@333333ÀÍÌÌÌÌÌì?333333@ffffff@š™™™™™5@Àš™™™™™@ffffff$@ÍÌÌÌÌÌ@š™™™™™E@ÍÌÌÌÌL1@33333³8@ÍÌÌÌÌÌ-@333333ó¿š™™™™™#@š™™™™™F@š™™™™3@0@>@š™™™™D@L@#@ÍÌÌÌÌÌ@fffffæC@ÍÌÌÌÌÌ>@ÍÌÌÌÌÌì?ÍÌÌÌÌÌ6@.@š™™™™2@333333 ÀffffffD@ÍÌÌÌÌ D@*@ø?@E@€8@ffffff8@333333;@ffffff'@ÍÌÌÌÌÌ@ÍÌÌÌÌLH@333333+@€G@33333³1@š™™™™™É¿33333³;@ÍÌÌÌÌÌü?š™™™™™@ÍÌÌÌÌÌ@š™™™™™é¿ffffff$@33333³1@ÍÌÌÌÌÌ%@ÍÌÌÌÌL<@ÍÌÌÌÌÌô?333333%@@ÍÌÌÌÌLH@333333@33333³E@fffffæ2@333333@@0@8@.@333333 ÀÍÌÌÌÌÌ&@š™™™™™;@333333F@ÍÌÌÌÌL7@ @33333³3@ÍÌÌÌÌÌì¿ð?ÍÌÌÌÌÌ9@ffffff,@333333@ffffffÀffffff*@ÍÌÌÌÌÌ@333333;@333333@ÍÌÌÌÌL0@ffffff$@š™™™™™@33333sH@1@ffffff5@ÀB@fffffæ5@33333óI@š™™™™F@š™™™™1@<@33333³0@fffff¦F@š™™™™™4@ÍÌÌÌÌÌ@@B@ffffff0@ÍÌÌÌÌÌ @>@333333"@ÍÌÌÌÌÌ#@ffffff/@ÍÌÌÌÌL6@"@š™™™™™¹?!@€<@š™™™™™+@ÍÌÌÌÌÌ5@fffffæ9@ÍÌÌÌÌL?@ÍÌÌÌÌÌ@33333s@@À33333sF@.@ffffffB@ÍÌÌÌÌL1@À3333332@š™™™™™.@ffffff'@š™™™™™@#À3333337@š™™™™™4@ÍÌÌÌÌÌ7@š™™™™™¹?33333óA@€8@š™™™™™ù¿Àš™™™™™7@€?@ÍÌÌÌÌÌ&@333333@ffffffÀffffffþ?0@333333&@ffffff@ffffff%Àš™™™™™ Àffffff:@š™™™™™ À@333333(@333333,@33333óH@333333ã?À@@@š™™™™™Ù¿ÍÌÌÌÌÌ@ffffff ÀÍÌÌÌÌL8@š™™™™YN@š™™™™ÙH@@ÍÌÌÌÌÌ8@33333³M@€K@fffffFQ@fffffæ9@333333@š™™™™™H@š™™™™™é¿š™™™™™$@š™™™™2@ÀÍÌÌÌÌÌ @33333³1@'@333333û¿333333A@3333334@ÍÌÌÌÌÌ1@3333330@ÍÌÌÌÌÌ-@š™™™™™!@333333 @ÍÌÌÌÌ F@-@ffffff#@fffff&C@@F@ffffffO@ffffff!@333333A@ffffff,@333333'@ffffff6@3333333@ÍÌÌÌÌÌô?ffffff@ffffff@š™™™™B@(À€B@3333332@ÍÌÌÌÌÌì?ffffffæ?ffffffþ?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ*Àš™™™™™Àš™™™™™É¿ÍÌÌÌÌL1@333333Àffffff.@ÍÌÌÌÌÌ2@š™™™™™6@ffffffö¿š™™™™™%@4@€7@š™™™™4@ÍÌÌÌÌÌ!@€5@š™™™™™É?333333Àffffff"@š™™™™Y@@€C@333333;@š™™™™™'@š™™™™™ù?@333333(@€6@33333³7@3333339@@ÍÌÌÌÌÌÀffffff @333333Àffffffæ?š™™™™™À33333³@@ÍÌÌÌÌL:@ffffff@à¿ÍÌÌÌÌÌ@š™™™™E@š™™™™™#@fffffæ:Àš™™™™™@š™™™™ÙH@ÍÌÌÌÌÌÀ333333-@ÍÌÌÌÌÌ @'@333333Ó¿š™™™™™@À333333Àš™™™™™ù?š™™™™™D@š™™™™™%@333333@333333@33333³A@š™™™™™2@ÍÌÌÌÌÌ @3333337@ÍÌÌÌÌÌì¿ffffff"@š™™™™6@ÍÌÌÌÌ A@ÍÌÌÌÌÌ/@ffffff=@+@š™™™™™@ÍÌÌÌÌÌ6@333333@š™™™™™ÀffffffÀffffff(@š™™™™™@ÍÌÌÌÌL0À333333,@š™™™™8@@ÍÌÌÌÌL5@33333óO@€5@J@ffffff=@š™™™™™,@š™™™™<@33333³A@š™™™™™@33333³:@š™™™™™"@(@ÍÌÌÌÌÌ%@ffffff<@fffffæ6@5@333333@ÍÌÌÌÌ @@š™™™™™@.@ffffff@š™™™™™;@ ÀÀ33333óA@333333*ÀÍÌÌÌÌL8@.@6@33333³:@ÍÌÌÌÌL@@ffffff@ÍÌÌÌÌL9@ffffff@š™™™™™#@ÀfffffæC@ffffff&@ @š™™™™™6@ÍÌÌÌÌÌ@3333331@ffffff6@ÍÌÌÌÌLC@š™™™™™@š™™™™™#À@š™™™™Y@@ffffff.@ÍÌÌÌÌÌ$@333333,@ÍÌÌÌÌÌ2@š™™™™™@š™™™™ÙH@š™™™™<@ÍÌÌÌÌL=@€3@ÍÌÌÌÌÌ2Àš™™™™™,À33333³2@ÍÌÌÌÌLI@ÍÌÌÌÌL8@ÍÌÌÌÌÌK@€0@ÍÌÌÌÌÌÀ3333336@3333334@ÍÌÌÌÌÌ&ÀÍÌÌÌÌÌ@€7@fffff¦E@š™™™™™(@š™™™™™é¿ffffff7@ÍÌÌÌÌ G@ÍÌÌÌÌÌ6@š™™™™™8@2@ð?ÍÌÌÌÌÌ*@Àš™™™™8@ÍÌÌÌÌ @@š™™™™D@ffffff0@š™™™™™*@ffffffæ?ÍÌÌÌÌÌ#@€6@ÍÌÌÌÌÌ @ffffff'@š™™™™ÙA@ÍÌÌÌÌÌ'@š™™™™YO@š™™™™™8@333333,@ÍÌÌÌÌÌÀš™™™™™"@333333@@ffffff@3333332@+@3333330À333333ÀÍÌÌÌÌÌ3@fffffæ6@33333sA@333333Ó?fffff&A@fffff&@@33333³4@ffffff%Àš™™™™™@333333-@ÍÌÌÌÌÌ6@ÍÌÌÌÌL6@š™™™™™&Àfffff¦H@33333³HÀfffffæ:@6@š™™™™™¹?š™™™™™Ù?š™™™™™é¿š™™™™™ù?š™™™™™Ù?ÍÌÌÌÌÌô?ffffffæ?à?š™™™™™¹?ffffffæ?š™™™™™¹?333333ã?š™™™™™é?333333ã?333333ó?ÍÌÌÌÌÌô?333333ã?ffffffö?š™™™™™Ù?ffffffæ?š™™™™™Ù?à?š™™™™™Ù?333333@à?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™ñ?ð?š™™™™™¹?333333ã?š™™™™™ñ?š™™™™™é?à?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?ð?333333Ó?š™™™™™¹?333333ã?à?š™™™™™É?š™™™™™Ù?š™™™™™É?š™™™™™¹?š™™™™™É?333333ó?š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™É?ÍÌÌÌÌÌì?ffffffæ?š™™™™™ñ?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™Ù?333333Ó?š™™™™™É?333333ã?š™™™™™Ù?à?š™™™™™¹?ffffffæ?333333ã?š™™™™™Ù?š™™™™™¹?š™™™™™Ù?333333ã?333333ã?333333ã?š™™™™™Ù?ffffffæ?š™™™™™¹?š™™™™™É?à?ÍÌÌÌÌÌì?ð?à?š™™™™™é?š™™™™™é?333333ó?@ÍÌÌÌÌÌü?333333ó?š™™™™™@@333333Ó?š™™™™™¹?š™™™™™¹?š™™™™™¹?333333Ó?š™™™™™¹?333333ã?à?š™™™™™Ù?333333ã?ffffffæ?š™™™™™Ù?š™™™™™ñ?à?š™™™™™É¿ffffffö?š™™™™™É?š™™™™™Ù?š™™™™™Ù?ffffffæ?ÍÌÌÌÌÌô?š™™™™™¹?ð?333333ã?ffffffæ?š™™™™™ñ?à?333333ã?š™™™™™É?ð?š™™™™™É?š™™™™™É?ø?š™™™™™¹?ÍÌÌÌÌÌì?à?333333ã?š™™™™™Ù?š™™™™™¹?ÍÌÌÌÌÌì?à?ffffffæ?š™™™™™@š™™™™™¹?à?ð?ð¿ffffffæ?š™™™™™Ù?š™™™™™¹?ffffffæ?ffffffæ?à?š™™™™™¹?š™™™™™¹?333333ã?š™™™™™¹¿à?333333ã?š™™™™™Ù?š™™™™™¹?à?š™™™™™Ù?ÍÌÌÌÌÌ쿚™™™™™¹?ffffffæ?ð?à?š™™™™™É¿š™™™™™Ù?à?š™™™™™É?š™™™™™ñ?š™™™™™¹?š™™™™™ù?ffffffæ?ÍÌÌÌÌÌì?333333ó?ffffffæ?333333ó?š™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌô?š™™™™™Ù?š™™™™™Ù?ffffffö?ÍÌÌÌÌÌô?à?š™™™™™é?š™™™™™¹¿333333ã?š™™™™™é?ð?ffffff@ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ffffffæ?333333ã?ð?333333ã?š™™™™™¹?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™¹?š™™™™™¹?ø?š™™™™™¹?ffffffþ?à?š™™™™™¹?@ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?333333ó¿333333ó?ÍÌÌÌÌÌü?š™™™™™Ù¿š™™™™™¹?ÍÌÌÌÌÌì?à?š™™™™™Ù?ffffffæ?à?š™™™™™É?š™™™™™¹¿š™™™™™Ù?š™™™™™¹?š™™™™™é?š™™™™™ñ?à?333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?à?ffffffö?š™™™™™ñ?ffffffö?333333ã?ÍÌÌÌÌÌô?š™™™™™É?š™™™™™¹?ÍÌÌÌÌÌü?š™™™™™É?š™™™™™É?333333ã?ÍÌÌÌÌÌì?ø?ffffffæ?š™™™™™Ù?š™™™™™Ù?š™™™™™É?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™É?ffffffæ?333333ó?ð?333333ã?ffffffö?ð?š™™™™™É?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?š™™™™™Ù?š™™™™™é?š™™™™™Ù?š™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™¹¿à?š™™™™™Ù?ð?š™™™™™É¿ffffffæ?ÍÌÌÌÌÌì?š™™™™™é?ø?š™™™™™é?333333ã?š™™™™™ñ?š™™™™™É¿ÍÌÌÌÌÌì?333333ã?š™™™™™¹?š™™™™™Ù?333333ó?333333û?š™™™™™Ù?333333ã¿333333ã?ÍÌÌÌÌÌô?š™™™™™ñ?š™™™™™¹¿š™™™™™¹¿333333û?š™™™™™Ù¿š™™™™™Ù?ð?ÍÌÌÌÌÌì?à?š™™™™™é?š™™™™™É¿š™™™™™ù?š™™™™™é?à?š™™™™™é?š™™™™™¹?ÍÌÌÌÌÌÀš™™™™™¹?ÍÌÌÌÌÌì?333333û¿š™™™™™Ù?333333ã?à?333333ó?ø?ÍÌÌÌÌÌì?@à?š™™™™™Ù?à?ÍÌÌÌÌÌô?š™™™™™ñ?à?š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™ñ?333333ã?š™™™™™¹?š™™™™™Ù¿š™™™™™Ù?ð?333333û?ÍÌÌÌÌÌì?à?š™™™™™¹?ÍÌÌÌÌÌì?š™™™™™É?333333Ó?333333ã?š™™™™™Ù?333333Ó?ø?š™™™™™ñ?ÍÌÌÌÌÌì?à?333333û?š™™™™™ñ?@ð?š™™™™™ñ?š™™™™™é?š™™™™™É?š™™™™™ñ?š™™™™™é?š™™™™™É?ð?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@š™™™™™É?š™™™™™é?333333@à?š™™™™™Ù?š™™™™™¹?à?š™™™™™¹?š™™™™™Ù?ffffffö¿š™™™™™Ù?à?ffffffæ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333ã?ÍÌÌÌÌÌì?š™™™™™0@à?š™™™™™¹?ffffffæ?š™™™™™¹¿ð?ð?ffffff@š™™™™™é?š™™™™™¹?ÍÌÌÌÌÌü?š™™™™™¹?333333ã?š™™™™™¹?ffffffæ?Àš™™™™™É¿š™™™™™¹?à?š™™™™™@š™™™™™Ù?š™™™™™É?333333ó?ø?š™™™™™é?š™™™™™¹¿š™™™™™ñ?ð?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?à?š™™™™™é?333333Ó?333333ã?š™™™™™Ù?@ffffffö?š™™™™™Ù?š™™™™™¹?ffffffæ?333333ó?ffffffö¿ffffffæ?࿚™™™™™¹?ffffffþ?š™™™™™é?à?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333ã?333333ã?333333Ó?333333ã?333333Ó?ffffffæ?š™™™™™Ù?š™™™™™É?333333ã?ð?ÍÌÌÌÌÌü¿ð?š™™™™™Ù?ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌ$À333333"ÀÍÌÌÌÌÌÀÍÌÌÌÌL2À333333À333333Àš™™™™™ À333333ÀÍÌÌÌÌÌÀffffffÀÍÌÌÌÌÌÀ333333Àffffff%À"À333333À!Àš™™™™™%ÀÍÌÌÌÌÌÀ%À333333,ÀÍÌÌÌÌÌ'Àš™™™™™À333333ÀÀ ÀÍÌÌÌÌÌÀffffffÀš™™™™™ Àffffff ÀÍÌÌÌÌÌÀÀffffffÀ333333ÀÀÍÌÌÌÌÌÀ333333$Àffffff!Àffffff#Àš™™™™™(À1À333333À333333ÀÀ333333Àš™™™™™À333333 À333333+ÀÀ333333À333333$À3333331ÀffffffÀ333333ÀÍÌÌÌÌÌÀ!ÀÀ333333 Àš™™™™™ÀffffffÀš™™™™™Àffffffþ¿333333ÀÀ333333ó¿ffffffÀÍÌÌÌÌÌ)Àš™™™™™ Àffffffæ¿333333"ÀÀÍÌÌÌÌÌ&ÀÍÌÌÌÌÌÀffffffö¿š™™™™™(Àš™™™™™%Àš™™™™3ÀffffffÀš™™™™™Àš™™™™™!ÀÀš™™™™™%ÀÍÌÌÌÌÌÀffffffÀ333333ÀÍÌÌÌÌÌÀÀð?ffffffæ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?à?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?333333ã?333333ã?à?ffffff @333333 @ffffffö?à?ð?ÍÌÌÌÌÌ@@š™™™™™ù?@ð?š™™™™™ñ?333333ó?333333ã?ø?š™™™™™ñ?333333ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™ñ?ÍÌÌÌÌÌ @333333 @ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?à?à?ffffffæ?ffffff@333333@ÍÌÌÌÌÌü?@š™™™™™ñ?@333333ó?ÍÌÌÌÌÌì?ffffffö?ffffffæ?333333û?ÍÌÌÌÌÌô?333333û?à?ÍÌÌÌÌÌô?ffffff@ffffffö?ffffff@š™™™™™ñ?š™™™™™ù?à?333333ó?333333@@333333ã?š™™™™™ñ?ffffffæ?ffffff@š™™™™™@ffffffþ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333ã?ffffff@333333@ÍÌÌÌÌÌü?š™™™™™ñ?ÍÌÌÌÌÌ@333333@ffffffæ?ffffffæ?ffffffö?š™™™™™é?ÍÌÌÌÌÌü?ffffffþ?333333ã?ffffffö?š™™™™™ñ?ÍÌÌÌÌÌ @ffffff@š™™™™™@333333@ffffff@ÍÌÌÌÌÌ@š™™™™™é?š™™™™™Ù?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™Ù?@ffffffþ?ÍÌÌÌÌÌì?š™™™™™Ù?@333333ã?333333ó?ø?ffffff@ffffff@š™™™™™é?ð?@333333û?@š™™™™™ù?333333û?ffffff@ÍÌÌÌÌÌô?ð?ffffffþ?ffffffæ?333333ó?ffffffþ?333333û?à?ffffffþ?ffffffæ?ffffffö?ÍÌÌÌÌÌü?333333ã?@333333@ffffff@à?à?@š™™™™™ù?333333 @333333@š™™™™™ù?š™™™™™ñ? @à?333333ã?ffffff@š™™™™™ù?ffffffæ?ð?ffffff@@ffffffþ?333333@š™™™™™ù?š™™™™™@333333ó?ffffffæ?ffffffþ?ø?ffffff@š™™™™™ñ?š™™™™™é?@@ð?333333û?ÍÌÌÌÌÌô?š™™™™™ù?ø?ø?ffffffæ?@š™™™™™Ù?333333%@ @333333ã?š™™™™™@ÍÌÌÌÌÌô?ffffff@333333ã?š™™™™™ñ?ÍÌÌÌÌÌ@@š™™™™™@ffffffþ?ÍÌÌÌÌÌü?š™™™™™ù?ffffff@š™™™™™@ð?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@š™™™™™Ù?š™™™™™ù?š™™™™™ @ð?ffffff@š™™™™™é?ffffffæ?ffffff@ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?š™™™™™ñ?š™™™™™ñ?š™™™™™ñ?š™™™™™@š™™™™™ @ÍÌÌÌÌÌì?š™™™™™Ù?ffffff@à?š™™™™™@ffffffæ?ÍÌÌÌÌÌô?@ffffffæ?ffffff@333333ó?333333ó?ffffffö?š™™™™™ù?333333ó?š™™™™™ñ?ffffffö?ÍÌÌÌÌÌü?333333ã?š™™™™™Ù?333333ã?ÍÌÌÌÌÌì?š™™™™™@à?ø?ÍÌÌÌÌÌü?ø?š™™™™™ù?š™™™™™ñ?ÍÌÌÌÌÌô?ffffff@à? @ffffffþ?@š™™™™™ @ffffff@fffffæ8@ffffffþ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?š™™™™™é?š™™™™™Ù?333333@333333û?š™™™™™Ù?333333û?ÍÌÌÌÌÌô?ffffffæ?š™™™™™Ù?ffffff@ffffff$@ð?à?&@ÍÌÌÌÌÌì?333333@ÍÌÌÌÌÌô?ffffffþ?š™™™™™@š™™™™™é?333333û?š™™™™™ñ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?333333ó?ffffff@š™™™™™ @ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@ffffffæ?š™™™™™ñ?ffffffö?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?ffffff@ÍÌÌÌÌÌü?ffffffæ?ffffff @ffffffö?333333ã?@333333û?333333@ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?ð?333333û?ffffffö?333333ó?ÍÌÌÌÌÌ@š™™™™™é?333333 @ffffff@š™™™™™ñ?ÍÌÌÌÌÌ@333333û?à?333333@š™™™™™@ø?à?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@š™™™™™ñ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?333333ã?š™™™™™é?š™™™™™@333333@ffffffæ?š™™™™™@@š™™™™™ù?333333ó?@ffffff@à?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@:@333333ó?š™™™™™Ù?š™™™™™ñ?333333@š™™™™™Ù?š™™™™™é?$@à?ffffff@333333ã?@š™™™™™@@@ffffffþ?š™™™™™Ù?ÍÌÌÌÌÌì?ð?ffffffæ?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?333333ó?ffffffæ?ð?@ffffffö?333333ã?@ffffffæ?@ffffffæ?333333û?š™™™™™ñ?@@ffffffæ?@š™™™™™Ù?ÍÌÌÌÌÌü?š™™™™™é?ÍÌÌÌÌÌ@à?š™™™™™@@š™™™™™!ÀffffffÀffffff8À'À333333ÀÀš™™™™™À!Àš™™™™™Àš™™™™™Àš™™™™™ÀÍÌÌÌÌÌ$Àffffff+Àffffff!ÀÀ333333ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌ$À"ÀÍÌÌÌÌÌ'Àffffff*À"À)À333333"ÀffffffÀ333333À!ÀffffffÀ$À333333-ÀffffffÀÍÌÌÌÌÌÀš™™™™™ ÀÍÌÌÌÌL0Àš™™™™™ÀÀÀ333333)À"ÀÍÌÌÌÌÌ$Àffffff/Àš™™™™™ ÀÀÀš™™™™™é¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀÍÌÌÌÌÌ$ÀffffffÀš™™™™™À333333%ÀÀ$Àš™™™™™ À ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™ÀÀ"ÀÍÌÌÌÌÌ"ÀffffffÀÍÌÌÌÌÌ3ÀffffffÀffffffÀ333333À333333û¿333333Àš™™™™™Àffffff ÀÍÌÌÌÌÌÀš™™™™™À333333,ÀÀ333333'ÀÍÌÌÌÌÌÀffffffÀffffffÀ333333À&ÀffffffÀffffffÀš™™™™™.À$À333333À333333Àš™™™™™ À333333 À333333Àš™™™™™"À333333Àš™™™™™ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ(ÀÍÌÌÌÌÌÀ333333ÀÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀš™™™™™$À333333Àš™™™™™!ÀffffffÀ&Àš™™™™™À333333!ÀÀÍÌÌÌÌÌÀ333333%Àš™™™™™!ÀÀ333333ÀffffffÀÍÌÌÌÌÌÀffffffÀ"ÀffffffÀ333333"Àš™™™™™Àš™™™™™ ÀffffffÀÍÌÌÌÌÌ$À ÀffffffÀÍÌÌÌÌÌÀffffff"À333333ÀÍÌÌÌÌÌÀffffffÀ(ÀÍÌÌÌÌÌ#ÀffffffÀ333333&À333333 À333333ÀÀffffffÀ)Àš™™™™™À333333ÀÍÌÌÌÌL0ÀÍÌÌÌÌÌ!ÀÀffffffÀ!Àš™™™™™ÀÍÌÌÌÌÌ"À€0ÀffffffÀš™™™™™"Àffffff&À333333À333333 Àš™™™™™ÀÍÌÌÌÌÌ(ÀÍÌÌÌÌL2ÀÍÌÌÌÌÌÀ333333Àffffff3À333333ÀÍÌÌÌÌÌ Àš™™™™™ ÀÍÌÌÌÌÌ+À333333À!ÀffffffÀ333333Àffffff+À333333À333333 Àš™™™™™ Àš™™™™™ ÀÍÌÌÌÌÌÀš™™™™™Àffffff Àš™™™™™ À!À333333 ÀÍÌÌÌÌÌÀffffff(Àš™™™™™!Àš™™™™™ÀÍÌÌÌÌÌ0ÀÍÌÌÌÌÌ&À333333&Àš™™™™™ÀffffffÀš™™™™™ÀÍÌÌÌÌÌ%ÀÀÍÌÌÌÌÌ(Àš™™™™™À333333Àš™™™™™!Àffffff#ÀÀ!Àš™™™™™ÀÍÌÌÌÌÌÀš™™™™™À333333,Àffffff(Àš™™™™™&À&Àš™™™™™ù¿š™™™™™ÀÀffffff-ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™ÀÍÌÌÌÌÌÀ333333Àš™™™™™ ÀÀffffffÀš™™™™™*ÀÍÌÌÌÌÌÀ333333"Àš™™™™™"À333333!ÀÀffffffÀÀffffffÀffffff#À"À333333À333333ÀÍÌÌÌÌÌ+ÀffffffÀš™™™™™(À&Àffffff À333333$À333333Àš™™™™™Àš™™™™™%ÀÍÌÌÌÌÌÀ Àffffff7Àš™™™™™Àffffff Àš™™™™™ À333333'Àš™™™™™%ÀffffffÀ333333À À333333ÀÀ333333ÀÍÌÌÌÌÌ#À333333À333333-ÀffffffÀ333333À,ÀffffffÀÍÌÌÌÌÌÀÀÀffffffÀ ÀÀÀ333333#ÀÍÌÌÌÌÌÀ333333)Àffffff!ÀffffffÀš™™™™™À333333"À!À333333ÀffffffÀ À'À333333Àš™™™™™ÀffffffÀ*Àš™™™™6À333333 Àffffff(ÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌ&Àš™™™™™"ÀÍÌÌÌÌL1À333333Àffffff À33333³0ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌ!ÀÀffffffþ¿ffffff À$ÀÍÌÌÌÌÌ À333333ÀÍÌÌÌÌÌ*Àš™™™™™À"ÀfffffæBÀ333333#Àffffff Àš™™™™™ÀffffffÀš™™™™0ÀffffffÀffffffÀffffffÀffffff!ÀÍÌÌÌÌÌÀ333333ÀÀš™™™™™À"À33333³6ÀffffffÀ333333ÀÍÌÌÌÌÌ*À333333)Àffffff$Àffffff Àffffff#Àš™™™™0Àš™™™™™ÀÍÌÌÌÌÌ"À333333ÀÍÌÌÌÌÌ&Àš™™™™™#À3333334Àffffff'ÀÍÌÌÌÌÌÀ333333À333333"Àš™™™™™ Àffffff!ÀÀffffffÀ333333/ÀÀš™™™™™-Àš™™™™™À333333%ÀÍÌÌÌÌÌÀ#Àffffff$À333333û¿ffffffö¿ffffff!À333333À333333Àffffffö¿ffffff(Àffffff À@ffffffþ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?š™™™™™ñ?ffffff@ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?(@š™™™™™ñ?@ffffff @à?š™™™™™ñ?ffffffþ?š™™™™™@ð?à?ffffffæ?ffffffþ?ÍÌÌÌÌÌô?ø?ÍÌÌÌÌÌü?ð?ÍÌÌÌÌÌô?ffffffæ?333333ó?š™™™™™ñ?ÍÌÌÌÌÌ@333333@333333"@333333@ð?ffffffö?ÍÌÌÌÌÌì?ø?š™™™™™@@ffffff @3333333@ÍÌÌÌÌÌü?à?š™™™™™ñ?333333@ @333333@333333@333333)@š™™™™™Ù¿ffffffæ¿333333Àš™™™™™é¿à?333333Ó?š™™™™™Ù¿š™™™™™Ù¿333333ó¿333333Ó¿š™™™™™É?ffffff濚™™™™™É?š™™™™™¹¿š™™™™™Ù¿š™™™™™é¿š™™™™™É?š™™™™™é¿ÍÌÌÌÌÌì¿333333㿚™™™™™É?š™™™™™É¿333333㿚™™™™™É?à¿333333ã¿333333Ó?š™™™™™é?ÍÌÌÌÌÌÀš™™™™™¹?333333ã¿à¿333333㿚™™™™™¹?š™™™™™Ù?࿚™™™™™É?š™™™™™É¿š™™™™™¹?š™™™™™ñ¿333333㿚™™™™™¹¿à¿333333ó¿š™™™™™Ù¿š™™™™™é¿333333㿚™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™Ù¿š™™™™™ñ¿ffffffæ?à?à¿à¿š™™™™™É?š™™™™™é¿333333Ó?࿚™™™™™¹?š™™™™™É?š™™™™™Ù?333333Ó¿333333ã¿333333Ó?ffffffæ?š™™™™™é¿ÍÌÌÌÌÌì¿333333ã¿333333ã¿à¿š™™™™™é¿ffffff濚™™™™™Ù¿š™™™™™é¿333333㿚™™™™™é¿ffffffþ¿ffffff濚™™™™™é¿333333㿚™™™™™é¿š™™™™™Ù¿333333ã¿333333㿚™™™™™Ù¿š™™™™™ñ¿š™™™™™¹¿š™™™™™Ù?š™™™™™¹?333333Ó?š™™™™™Ù?ð¿à¿ÍÌÌÌÌÌì¿333333ó¿š™™™™™ñ¿ffffffæ¿333333ó¿333333㿚™™™™™¹¿š™™™™™ñ¿š™™™™™é¿š™™™™™¹?à?š™™™™™é¿š™™™™™É?š™™™™™¹?š™™™™™¹?333333Ó?࿚™™™™™é¿333333㿚™™™™™¹?š™™™™™É?ffffffæ¿333333ã¿à¿à¿à¿333333Ó¿š™™™™™é¿333333Ó¿333333Ó?333333Ó?š™™™™™é¿ð¿333333Ó¿ffffffþ¿š™™™™™¹?š™™™™™Ù¿333333ã¿333333㿚™™™™™é¿š™™™™™É?š™™™™™é¿ffffff濚™™™™™É?333333ã¿à?š™™™™™é¿š™™™™™Ù?333333Ó¿š™™™™™é¿ffffff濚™™™™™é¿š™™™™™Ù¿333333ã¿333333Ó?ffffff濚™™™™™ñ¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™é¿333333Ó?࿚™™™™™é¿š™™™™™¹?ffffff濚™™™™™¹?š™™™™™É?333333ã¿ÍÌÌÌÌÌì¿333333Ó¿ffffffæ¿ð¿š™™™™™¹?à?ð¿à¿333333Ó?333333㿚™™™™™¹?š™™™™™é¿š™™™™™¹?š™™™™™É?333333ã¿ Àš™™™™™é¿333333Ó¿š™™™™™Ù?š™™™™™¹?š™™™™™Ù¿333333Ó?333333Ó¿ffffffæ¿à¿ð?š™™™™™¹?š™™™™™É?š™™™™™¹¿à¿š™™™™™É?333333㿚™™™™™¹?333333㿚™™™™™¹?š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™é¿š™™™™™¹?333333Ó?333333ã?333333Ó?ÍÌÌÌÌÌô¿š™™™™™é¿š™™™™™Ù¿333333ã¿333333ó¿š™™™™™é¿š™™™™™Ù¿ÍÌÌÌÌÌô¿ffffffæ¿333333Ó?š™™™™™ñ¿333333Ó?333333ó¿ffffff濚™™™™™É¿à¿ð¿333333Ó?š™™™™™É?ffffffæ¿à¿š™™™™™é¿š™™™™™¹?š™™™™™Ù?š™™™™™¹?333333ã¿333333Ó?š™™™™™É?𿚙™™™™É?333333㿚™™™™™Ù?333333Ó¿š™™™™™Ù?ÍÌÌÌÌÌì¿à?࿚™™™™™¹¿š™™™™™É¿333333Ó¿š™™™™™¹?333333Ó?š™™™™™É?333333㿚™™™™™Ù¿š™™™™™¹?š™™™™™Ù¿š™™™™™Ù¿à?à¿333333Ó¿š™™™™™Ù?š™™™™™¹¿333333ӿ࿚™™™™™É?333333Ó¿š™™™™™é¿ÍÌÌÌÌÌ쿚™™™™™Ù?ffffffæ¿333333Ó¿ÍÌÌÌÌÌì¿ffffffö¿ffffff濚™™™™™é?𿚙™™™™Ù?š™™™™™¹?ffffffæ¿333333ã¿ffffffæ?333333ã¿ð?ÍÌÌÌÌÌì¿333333Ó¿ffffffö¿333333㿚™™™™™ñ¿333333ó¿ffffffæ¿333333û¿š™™™™™¹¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿333333ó¿333333ã¿333333ó¿ffffff濚™™™™™É¿333333ã¿ÍÌÌÌÌÌì¿333333ã¿ffffff濚™™™™™É¿à¿ffffffæ¿333333Ó¿333333Ó?š™™™™™É?333333ó¿ffffffæ¿333333Ó¿333333ã¿333333㿚™™™™™¹?ÍÌÌÌÌÌì¿333333ó¿š™™™™™é¿ð¿š™™™™™Ù¿ffffffÀš™™™™™É¿à?à¿ð?ÍÌÌÌÌÌô¿ffffff濚™™™™™é?š™™™™™é¿ð¿333333Ó?š™™™™™Ù¿333333ã?š™™™™™Ù?ÍÌÌÌÌÌô¿š™™™™™ñ¿ð¿š™™™™™Ù¿š™™™™™é¿š™™™™™¹?š™™™™™¹?333333ó¿à?š™™™™™Ù¿ÍÌÌÌÌÌô¿ffffffö¿š™™™™™¹?š™™™™™¹¿š™™™™™É¿ð¿š™™™™™Ù¿š™™™™™¹?š™™™™™Ù¿š™™™™™é¿333333Ó?š™™™™™Ù¿à¿š™™™™™é¿š™™™™™é¿š™™™™™¹?à?š™™™™™ñ¿333333ã¿ffffff濚™™™™™¹?š™™™™™Ù¿ÍÌÌÌÌÌô?333333㿚™™™™™é¿333333ã?š™™™™™é¿333333Ó¿ø¿š™™™™™¹?š™™™™™É?š™™™™™é¿333333Ó?š™™™™™É¿š™™™™™É?333333ó¿š™™™™™Ù?š™™™™™¹?š™™™™™é¿à?ffffffæ¿333333ó¿ffffffæ¿333333ã¿333333ã?333333㿚™™™™™Ù¿š™™™™™ñ?š™™™™™Ù¿ð¿ÍÌÌÌÌÌô¿333333㿚™™™™™É?ÍÌÌÌÌÌì¿333333Ó¿š™™™™™É¿333333ã¿ffffff濚™™™™™Ù?š™™™™™Ù¿333333Ó?š™™™™™¹?333333Ó?š™™™™™¹¿š™™™™™Ù¿ÍÌÌÌÌÌü?ffffffæ?š™™™™™É?333333ã¿333333ã¿ffffff Àš™™™™™Ù¿à¿š™™™™™Ù?333333û?š™™™™™¹¿ð?š™™™™™é?ffffffö¿ÍÌÌÌÌÌü¿š™™™™™Ù¿š™™™™™Ù¿333333Ó¿à?š™™™™™Ù¿à?ffffff@ffffffæ¿Àffffffæ?ffffffæ?ÍÌÌÌÌÌ쿚™™™™™ù¿š™™™™™@333333ã?š™™™™™À@ÍÌÌÌÌÌü?ø?333333Ó¿š™™™™™Ù¿ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?š™™™™™¹¿š™™™™™ñ¿ÍÌÌÌÌÌü¿š™™™™™ù¿ð¿š™™™™™é¿333333ã?š™™™™™é¿ÍÌÌÌÌÌ쿚™™™™™É¿à¿333333ã?š™™™™™É¿š™™™™™é¿š™™™™™é?ffffffö¿333333Ó¿š™™™™™é?ð¿ÍÌÌÌÌÌ@333333Ó¿à¿ffffffæ?ffffffþ?𿚙™™™™ñ?333333ó¿ÍÌÌÌÌÌ쿚™™™™™ù¿š™™™™™ñ¿à?ffffffö?ffffffö¿ffffff濚™™™™™É¿ð¿ÍÌÌÌÌÌì?333333Ó¿ÍÌÌÌÌÌì?š™™™™™ñ¿à¿š™™™™™ù?š™™™™™Ù?à¿ffffffæ?ffffffæ?ffffff濚™™™™™¹¿ø?ð?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™@333333 @ffffffö¿š™™™™™É?ÍÌÌÌÌÌ쿚™™™™™É?š™™™™™ñ¿333333ó?333333û¿333333û¿ÍÌÌÌÌÌô¿š™™™™™É¿ð¿333333ã?ð¿333333û¿333333Ó¿š™™™™™ù?ffffffæ¿ÍÌÌÌÌÌü¿ffffff@ffffff@š™™™™™¹¿ð?š™™™™™É¿ffffffÀffffffþ?333333ó¿ÍÌÌÌÌÌì¿ffffffÀš™™™™™¹¿ø?š™™™™™ñ?š™™™™™Ù?333333û?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿ffffffæ?ffffffæ?333333ã?ÍÌÌÌÌÌì?ð¿à?333333㿚™™™™™É¿333333û¿ð?š™™™™™ñ¿333333ó?š™™™™™ñ¿š™™™™™Ù¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌì¿333333ã¿333333ó¿ffffffæ¿ffffff濚™™™™™ù?ø¿ø¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ쿚™™™™™¹?ø¿ffffffÀø¿ø¿ÍÌÌÌÌÌì?ffffffö¿ð¿Àffffffæ?333333ã¿ÍÌÌÌÌÌü?333333ó¿š™™™™™É¿à?ÍÌÌÌÌÌü¿333333ã?ffffff@ð?ffffff@š™™™™™Ù¿ÍÌÌÌÌÌì¿Àð¿ffffffæ?333333ó¿ÍÌÌÌÌÌü?š™™™™™¹?333333ã?š™™™™™Ù¿ffffff@ffffffæ?333333Ó¿ð?ÍÌÌÌÌÌì¿à¿ÍÌÌÌÌÌô?@333333ó¿š™™™™™É¿ffffffæ?à¿à?333333Ó¿š™™™™™ñ¿ÍÌÌÌÌÌì¿ffffffæ?333333û¿à¿à?ffffffö?ÍÌÌÌÌÌü¿@333333Ó¿333333㿚™™™™™@333333ó¿ÍÌÌÌÌÌì¿ffffff À333333Ó¿@š™™™™™ù?333333ó?333333ó¿ÍÌÌÌÌÌô?š™™™™™é¿š™™™™™ñ?à¿ffffff濚™™™™™É¿333333Ó¿š™™™™™é¿š™™™™™¹¿333333ã¿333333Ó?ð?333333ã¿ø?333333ó?@š™™™™™ù¿@ø?ffffffö?š™™™™™é?š™™™™™ñ?š™™™™™é?ffffffö¿ð¿ffffff@ÍÌÌÌÌÌÀ333333ã?ð¿ÍÌÌÌÌÌô?333333û?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?à?š™™™™™ù¿333333ó?š™™™™™é¿ð?à?ffffffæ?ffffff@333333ó?ÍÌÌÌÌÌì?ø?š™™™™™é¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌü¿š™™™™™Ù¿ÍÌÌÌÌÌ@š™™™™™Ù¿ð¿333333Ó¿333333ã¿ÍÌÌÌÌÌ쿚™™™™™Ù?333333Ó?ffffffæ?š™™™™™É?š™™™™™é¿à?š™™™™™¹¿ÍÌÌÌÌÌô¿š™™™™™É¿š™™™™™ñ?ð?ÍÌÌÌÌÌ@à?333333Ó?333333ã?333333ã?ffffffæ?š™™™™™ñ¿š™™™™™Ù¿à¿à?š™™™™™ù?à¿ffffffö¿š™™™™™Ù?à¿ÍÌÌÌÌÌ@333333À333333û¿333333@ffffffæ?š™™™™™ù¿333333Ó¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ffffffö¿@š™™™™™É¿@š™™™™™¹¿š™™™™™ñ¿333333Ó¿ffffff濚™™™™™Àš™™™™™Ù¿š™™™™™ñ?š™™™™™ À333333û?ÍÌÌÌÌÌü?š™™™™™ñ¿à?333333û?à?@333333ӿ࿚™™™™™é?š™™™™™ù?š™™™™™ù?ð?š™™™™™é¿ffffffæ?š™™™™™Ù¿š™™™™™¹¿ffffffö?ÍÌÌÌÌÌô¿333333ã¿333333ó?à¿ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?š™™™™™é¿333333ã?à¿ÀÍÌÌÌÌÌü¿š™™™™™Ù¿š™™™™™ù?333333Ó¿ø?š™™™™™ù?333333ó?ÍÌÌÌÌÌô?ffffffþ? @ffffffþ?š™™™™™ù?ð?š™™™™™ñ?ffffff@333333ã?ð?ffffffæ?ffffffþ¿à¿ÍÌÌÌÌÌô?333333û?š™™™™™@𿚙™™™™É¿ÍÌÌÌÌÌ@š™™™™™Ù¿333333ã?š™™™™™ñ?š™™™™™É¿333333Ó¿š™™™™™¹¿à¿š™™™™™ñ¿š™™™™™Àà?ð?š™™™™™ñ?š™™™™™¹¿š™™™™™ñ¿à¿@š™™™™™Ù?33333³0@ffffffæ?ffffffæ?ÍÌÌÌÌÌô¿à¿ffffffþ?@333333@š™™™™™Ù?ffffffÀ333333Ó¿š™™™™™Ù¿ffffffæ?À333333û?ÍÌÌÌÌÌÀ333333ã¿ffffffþ¿333333û?ÍÌÌÌÌÌô?333333Ó¿š™™™™™É?333333@ø?333333û?333333û¿333333Ó¿ffffffö?ÍÌÌÌÌÌì?333333ã?ffffffæ¿à¿š™™™™™é¿à?333333ó¿š™™™™™É?𿚙™™™™¹¿š™™™™™é¿ÍÌÌÌÌÌü¿333333ó?ffffffö?ffffffÀš™™™™™¹¿Àš™™™™™¹¿ÍÌÌÌÌÌô?à¿ø¿ÍÌÌÌÌÌì¿333333ó?ø¿ffffffæ¿333333ã¿à¿š™™™™™É?š™™™™™É?ø?ÍÌÌÌÌÌô¿š™™™™™¹¿ÍÌÌÌÌÌ@ffffffÀð?š™™™™™ñ?8Àš™™™™™5À33333³2@3333332À9Àš™™™™™NÀfffffæ>À/À-ÀÍÌÌÌÌÌÀ€:À"À333333À"À3333334À33333³<Àš™™™™™2À333333?Àš™™™™Y@À333333'Àš™™™™™3ÀÍÌÌÌÌÌÀÀÍÌÌÌÌL4À33333³7À`QÀffffff2ÀÍÌÌÌÌÌ2À33333³;Àš™™™™Ù@ÀÍÌÌÌÌÌ%À3333337ÀÍÌÌÌÌÌ3Àffffff?ÀÍÌÌÌÌÌ7ÀÍÌÌÌÌÌ5À3333331ÀÍÌÌÌÌÌÀffffff3Àfffffæ9À333333"À1ÀÍÌÌÌÌL6Àš™™™™7Àš™™™™4ÀÍÌÌÌÌÌ(À333333/À3333333Àffffff8Àš™™™™™FÀÍÌÌÌÌÌ%À*À@333333@À7Àfffffæ9ÀÍÌÌÌÌŒDÀÍÌÌÌÌL7À4Àfffffæ1Àš™™™™8@ÍÌÌÌÌÌ!ÀÍÌÌÌÌÌ-Àš™™™™™9Àfffffæ6Àffffff+Àfffffæ1Àš™™™™™4À;Àffffff ÀÍÌÌÌÌÌ6À>ÀÍÌÌÌÌÌ5Àffffff*Àš™™™™4Àš™™™™™Ù?3333331À33333³0À€1Àffffff4À@AÀÍÌÌÌÌL=À€;Àffffff@š™™™™™8À`RÀffffff<Àfffffæ4Àš™™™™ÙUÀÍÌÌÌÌ PÀfffffæ9À€2À33333³3ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌ@ð?š™™™™™ù?33333³7ÀÍÌÌÌÌÌ6ÀÍÌÌÌÌL1ÀÍÌÌÌÌÌ)ÀÍÌÌÌÌL@Àš™™™™™ÀÍÌÌÌÌ AÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌ@Àš™™™™™ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌL0ÀÍÌÌÌÌLAÀš™™™™1À333333+Àš™™™™™,À@CÀ€5À333333À33333³0À9Àffffff5Àš™™™™™.Àš™™™™6Àš™™™™™#Àfffffæ3Àš™™™™™À+ÀÍÌÌÌÌL4Àš™™™™2À333333/ÀÍÌÌÌÌÌ0À333333;ÀÍÌÌÌÌÌ*Àš™™™™™.À3333333À0Àffffff*Àfffffæ8Àfffffæ8À$À33333³0Àffffff,Àffffff;À@&ÀÍÌÌÌÌL2Àš™™™™™À333333À333333Àš™™™™™:ÀffffffÀ333333À'À/ÀffffffÀ€1ÀÍÌÌÌÌL3ÀÍÌÌÌÌÌ1À5À333333ÀÍÌÌÌÌL=À+À333333À33333³<À3333337Àffffff/Àš™™™™™*Àš™™™™;Àš™™™™™*ÀÍÌÌÌÌÌ(À333333(À€;Àfffffæ8Àš™™™™™4À@Àffffff%ÀÍÌÌÌÌÌ*Àffffff2Àffffff0À333333Àffffff@À33333³2À3333330Àš™™™™™AÀ333333,À€3À333333<Àš™™™™™%@ÍÌÌÌÌL9ÀÍÌÌÌÌÌ+ÀÍÌÌÌÌÌ:ÀÍÌÌÌÌ VÀÍÌÌÌÌL8À?Àš™™™™™À3333338À333333=ÀffffffÀ33333³5Àffffff#Àš™™™™™>À€NÀ)À€1Àfffff&LÀfffffæ0Àš™™™™YGÀš™™™™™0ÀÍÌÌÌÌÌ"ÀÍÌÌÌÌÌWÀ33333s@À333333&Àš™™™™™2@ffffff%À#ÀÀEÀ333333 Àš™™™™™/Àfffffæ4À3333330Àffffff5À2À333333 ÀÍÌÌÌÌÌü¿ffffff(ÀÀ𿚙™™™4À33333³1À3333331Àffffff6Àš™™™™™#Àš™™™™1À3333337Àffffff)ÀÀHÀÍÌÌÌÌÌ5Àš™™™™™>À33333³0À333333À.ÀÍÌÌÌÌÌ5À3À33333³?À333333(À#Àš™™™™1ÀÍÌÌÌÌÌ5ÀÀEÀ3333334Àš™™™™5Àš™™™™™À1À333333,ÀÍÌÌÌÌL7À333333DÀ€3ÀÍÌÌÌÌÌ5À9À33333³<Àš™™™™<ÀÍÌÌÌÌ HÀš™™™™>ÀffffffÀ2À-Àš™™™™=Àffffff&À333333À333333&ÀffffffÀ À)Àffffff/À,ÀÍÌÌÌÌÌÀ'Àš™™™™3ÀffffffBÀÍÌÌÌÌL9À3333332Àffffff8À33333³<À333333!À333333*À33333³2À333333Àš™™™™™4À:ÀÍÌÌÌÌL;Àš™™™™2Àffffff5À€9ÀÍÌÌÌÌL5Àš™™™™™,ÀÍÌÌÌÌÌ@@ÍÌÌÌÌÌ,ÀÍÌÌÌÌÌ?À33333³:ÀfffffÎp@š™™™™™ À333333/À,À3333332ÀÍÌÌÌÌÌ)Àš™™™™;À333333,ÀÍÌÌÌÌÌ2À33333sSÀ€8Àš™™™™™2À33333³8À333333BÀ333333BÀffffff>@ffffffE@ffffff+ÀÍÌÌÌÌÌ.ÀffffffB@ffffff,ÀÍÌÌÌÌÌ"Àffffff+À333333Àš™™™™™Àffffff2À33333óCÀffffffÀÍÌÌÌÌÌ$ÀÍÌÌÌÌÌÀ33333óBÀ,À333333'Àffffff$À3333336ÀÍÌÌÌÌÌ:Àffffff4Àffffffþ?33333³2À࿚™™™™1ÀÍÌÌÌÌÌ%Àfffff¦@Àffffff+Àš™™™™™-À333333Àš™™™™™,Àš™™™™™ù?33333³8Àš™™™™™À333333"À333333ÀÍÌÌÌÌÌ$À3333335Àš™™™™YCÀš™™™™™1À€=ÀffffffÀ€6ÀÍÌÌÌÌÌ=Àš™™™™™.À>À33333ÓRÀš™™™™6À333333BÀš™™™™Y@Àš™™™™™@€0Àfffff¦@À3333333ÀffffffIÀÍÌÌÌÌÌÀ3333338ÀÍÌÌÌÌlVÀš™™™™™.À333333BÀffffff:ÀÀffffff1ÀÍÌÌÌÌÌÀ+ÀÍÌÌÌÌÌÀš™™™™ÙM@333333ÀfffffæDÀÍÌÌÌÌL9À&Àš™™™™?Àš™™™™YBÀš™™™™™8Àfffffæ;ÀkÀ9À3333336Àš™™™™™,ÀÀÀ@À€0Àfffff¦\ÀÍÌÌÌÌÌ!Àš™™™™™#@š™™™™™:Àš™™™™™ ÀÍÌÌÌÌL7À333333Àffffff:ÀfffffæE@ffffff.Àš™™™™™+À€4Àš™™™™ÙKÀ%Àfffffæ4À333333 Àš™™™™YAÀš™™™™™1ÀÍÌÌÌÌÌ$À333333@À0À333333@ÀffffffÀš™™™™™+À:Àffffff%Àš™™™™™@À3333338ÀÍÌÌÌÌÌ,ÀÍÌÌÌÌ WÀfffffæ=ÀÍÌÌÌÌÌ/Àš™™™™1@ffffff<Àš™™™™2@ffffff$@š™™™™™1ÀÀ333333$ÀFÀš™™™™0ÀÍÌÌÌÌL?À€5Àfffffæ2À333333$À33333³9ÀÍÌÌÌÌL9Àš™™™™™ Àš™™™™™@3333336À333333*Àš™™™™™6Àš™™™™™2À333333Àš™™™™7@33333³<ÀffffffÀ33333³=ÀÍÌÌÌÌÌ:À333333+Àffffff6À>À33333óMÀÍÌÌÌÌÌ+Àffffff0À333333ÀÍÌÌÌÌÌ@33333óCÀÍÌÌÌÌL;ÀÍÌÌÌÌÌ&Àš™™™™™(À3333337ÀÍÌÌÌÌÌ5À3333336Àffffff8ÀÀ1Àš™™™™™DÀà¿ÍÌÌÌÌÌü¿€<ÀfffffæEÀ ÀÍÌÌÌÌÌ(ÀÍÌÌÌÌLCÀš™™™™™ÀÍÌÌÌÌÌÀ @ffffff:Àffffff8Àffffff.Àš™™™™™,À€6À€=À2ÀÍÌÌÌÌŒ@ÀÍÌÌÌÌŒAÀÍÌÌÌÌÌ/ÀÍÌÌÌÌÌ&ÀÍÌÌÌÌL<Àfffffæ;ÀÍÌÌÌÌÌ5À3333330À#ÀÍÌÌÌÌÌ4À333333=ÀffffffAÀš™™™™™?ÀÍÌÌÌÌL0ÀÍÌÌÌÌÌ%@š™™™™DÀš™™™™™ù¿=À333333GÀ333333/ÀÍÌÌÌÌÌü¿€9ÀÍÌÌÌÌŒ@@3333338À7ÀCÀš™™™™YAÀffffff%À333333@33333s@À@À$À333333@À33333³4Àš™™™™9Àš™™™™™ÀÍÌÌÌÌL?Àš™™™™™À333333@ÍÌÌÌÌÌ'À€6Àffffff)À33333³9ÀffffffBÀÍÌÌÌÌÌ<À33333³4@2À33333³1Àš™™™™:@š™™™™™$@333333@4ÀfffffæDÀffffff1Àš™™™™™=Àffffff!ÀffffffÀffffff*@ffffff/ÀÍÌÌÌÌLCÀ@AÀš™™™™3Àfffffæ9ÀÍÌÌÌÌÌ7Àš™™™™™/ÀÍÌÌÌÌLGÀ333333-ÀÍÌÌÌÌÌ.À333333,À33333³9À33333³5@ffffffö?ÍÌÌÌÌÌ1Àš™™™™™ À3333330Àš™™™™YWÀš™™™™™ ÀÍÌÌÌÌL3ÀÍÌÌÌÌL>Àš™™™™YIÀffffff6À333333ã?ffffff)ÀffffffÀÍÌÌÌÌÌÀš™™™™™+À3333333ÀÍÌÌÌÌ DÀ333333*ÀÍÌÌÌÌÌ&À&Àš™™™™™4À9À333333'À33333³8À2Àš™™™™;Àffffff/À@BÀš™™™™™@3333339À3333332Àffffff1Àà¿ÍÌÌÌÌÌ0Àš™™™™™<Àš™™™™™(À333333,ÀÍÌÌÌÌÌ&@š™™™™YDÀš™™™™3ÀÍÌÌÌÌÌ%À3333339ÀÍÌÌÌÌÌ:ÀÍÌÌÌÌÌÀš™™™™@Àfffff&EÀÍÌÌÌÌŒ@À33333sBÀ@ÍÌÌÌÌ FÀfffffæ6Àffffff8Àffffff6Àfffffæ<À333333ó?ffffff;Àfffffæ<À"ÀÍÌÌÌÌ BÀ333333ÀÍÌÌÌÌÌì¿33333³0Àffffffþ?š™™™™ÙAÀ€1À;À:ÀÍÌÌÌÌÌ&Àffffff*Àš™™™™™-À€0À#ÀÀCÀ333333'@333333,Àfffffæ>À6@CÀš™™™™™6À(À3333332ÀÍÌÌÌÌLAÀÍÌÌÌÌÌ@Àš™™™™™é¿ÍÌÌÌÌL=Àš™™™™7Àffffff$À33333s@Àš™™™™™/ÀÍÌÌÌÌÌ8ÀÍÌÌÌ̬TÀ3333332À333333*ÀÀDÀ33333³@ÀÍÌÌÌÌÌ!À3333333Àffffff)Àffffff*ÀÍÌÌÌÌ FÀ33333³3Àfffffæ8Àffffff*À@A@9À#@fffffæ7À333333ã?33333³7À333333#À7À333333(ÀffffffÀš™™™™™-Àffffff-Àffffffþ¿<À333333-ÀffffffÀÍÌÌÌÌL=Àffffff@š™™™™™ÀÍÌÌÌÌÌÀš™™™™;À33333³:ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌ!À333333$@š™™™™™ ÀÍÌÌÌÌL?Àffffff;ÀÍÌÌÌÌÌ쿚™™™™YAÀ333333Àš™™™™9Àš™™™™™!Àffffff5ÀÍÌÌÌÌÌÀ(ÀffffffÀÍÌÌÌÌÌ<ÀÍÌÌÌÌÌÀ€@À=À.À333333-À333333@ÍÌÌÌÌÌ*Àffffff2Àfffffæ<ÀffffffBÀÍÌÌÌÌL5Àš™™™™™>ÀÍÌÌÌÌL2À"ÀÍÌÌÌÌÌ,Àffffff*Àffffff,À333333$À/À333333!À333333)ÀÍÌÌÌÌÌ"Àš™™™™™Àš™™™™1Àš™™™™™.Àš™™™™ÙBÀ@Àfffffæ3ÀÍÌÌÌÌÌ*ÀÍÌÌÌÌÌ-À333333&@333333 Àš™™™™0ÀÀfffffæ1Àš™™™™™3Àš™™™™YEÀfffffæ5ÀÍÌÌÌÌÌ:Àš™™™™6ÀÍÌÌÌÌÌ!À3333332ÀÍÌÌÌÌÌ7@ÍÌÌÌÌÌ#À33333sBÀ333333"@33333ãn@3333338À2@ÀÍÌÌÌÌ BÀ333333/ÀÍÌÌÌÌL2À333333û?fffff¦AÀffffffMÀš™™™™:Àffffff@ÍÌÌÌÌÌ9Àš™™™™HÀÀCÀÍÌÌÌÌL7@ÍÌÌÌÌÌÀffffff1ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ,@333333@ÍÌÌÌÌÌ$@š™™™™™7Àš™™™™™ Àffffff5@%Àš™™™™™ÀffffffÀffffff,Àš™™™™™%@3333330Àffffffæ?ÍÌÌÌÌÌ À3333331Àfffffæ0Àš™™™™™?Àffffff5À333333/@ÍÌÌÌÌLBÀÀffffffÀš™™™™™/Àš™™™™™ Àffffffæ¿4Àš™™™™™ù¿fffffæ0À333333/ÀÍÌÌÌÌŒCÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌ@333333Àffffff@333333Àffffff7ÀÍÌÌÌÌÌü¿ffffff*ÀÍÌÌÌÌÌ=@333333Àffffff,ÀffffffÀ3333335ÀÍÌÌÌÌŒKÀÍÌÌÌÌL2À9ÀÍÌÌÌÌÌ7Àš™™™™™ À3333334À3333337Àffffffþ¿33333s@@ÍÌÌÌÌÌ)Àš™™™™:ÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ2ÀÍÌÌÌÌÌ8Àffffff/ÀÍÌÌÌÌÌÀÍÌÌÌÌL4À!Àffffff1ÀÍÌÌÌÌL2Àš™™™™™A@š™™™™™Ù?€AÀÍÌÌÌÌL0ÀÍÌÌÌÌÌ'ÀÍÌÌÌÌ DÀš™™™™YEÀ333333ã?9ÀÍÌÌÌÌÌ1@š™™™™4À€0ÀÍÌÌÌÌL<Àš™™™™™$Àš™™™™™!ÀÍÌÌÌÌL5@ffffff$Àš™™™™™À"ÀÍÌÌÌÌL=À333333(Àš™™™™™-ÀfffffæBÀ333333&Àø?3333333Àš™™™™™<ÀffffffÀffffffBÀš™™™™™/Àš™™™™3À33333³4@3333332ÀÍÌÌÌÌÌô¿ffffff?À33333óAÀ@8Àš™™™™™@fffffæ3ÀÍÌÌÌÌÌ>À€3Àffffff9À333333BÀffffff)ÀÍÌÌÌÌÌZÀ?À8ÀÀ,ÀÍÌÌÌÌÌ=@33333óFÀš™™™™™2ÀÀAÀ333333&Àffffff:À5À333333EÀ33333³?À333333 Àfffffæ7À333333@ÀÍÌÌÌÌÌ>À333333%ÀÍÌÌÌÌÌ!@ÍÌÌÌÌÌ3Àš™™™™™É¿ffffffAÀ33333³3À3333335@ÍÌÌÌÌÌì¿ÍÌÌÌÌL0ÀÍÌÌÌÌÌ@š™™™™™Ù?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ@š™™™™™É¿ÍÌÌÌÌÌì?333333û?ÍÌÌÌÌÌì?ffffffæ?š™™™™™é?š™™™™™¹?š™™™™™¹?ð?ffffff@333333ó?š™™™™™ù?333333@š™™™™™¹?ÍÌÌÌÌÌ@333333Ó?333333ã?š™™™™™ñ?333333ó?ð?ð?ffffffþ?333333û?333333ó?à?š™™™™™Ù?ffffffö?š™™™™™Ù?š™™™™™é?333333ó?333333ó?ÍÌÌÌÌÌì?ð?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™ñ?ffffffæ?ÍÌÌÌÌÌì?š™™™™™¹?ÍÌÌÌÌÌì?š™™™™™¹?@à?š™™™™™é?š™™™™™é?333333Ó?š™™™™™ñ?š™™™™™é?ÍÌÌÌÌÌü?ø?š™™™™™ñ?333333ã?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™é?à?à?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™¹?333333ã?ÍÌÌÌÌÌì?333333ó?333333ó?à?ð?333333û?š™™™™™¹?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333û?š™™™™™ñ?š™™™™™ñ?333333ã?š™™™™™ñ?ÍÌÌÌÌÌ @ÍÌÌÌÌÌì?š™™™™™ @ffffff@@333333Ó?š™™™™™Ù?333333ó?à¿333333ã?333333Ó¿ffffffæ?š™™™™™É?333333Ó?š™™™™™¹?333333ã?ð?ffffffæ?ffffffæ?š™™™™™É?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™¹?333333ó?š™™™™™¹?ffffffæ?š™™™™™é?š™™™™™ñ?@ÍÌÌÌÌÌì?333333û?ffffffæ?à?333333ã?ð?ffffffö?š™™™™™É?à?ÍÌÌÌÌÌì?ffffffæ?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?š™™™™™ñ?ÍÌÌÌÌÌô?ffffffæ?š™™™™™ñ?š™™™™™é?š™™™™™ñ?ÍÌÌÌÌÌì?š™™™™™ñ?333333ã?333333ã?ÍÌÌÌÌÌô?333333û?333333ó?ð?333333ã?333333ó?@ffffffö?š™™™™™¹?ÍÌÌÌÌÌì?ffffffæ?ø?ÍÌÌÌÌÌì?š™™™™™Ù?à?à?333333ã?333333ã?333333û?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?š™™™™™ñ?š™™™™™é?š™™™™™¹?š™™™™™Ù?333333ã?333333ã?@š™™™™™¹¿š™™™™™@333333ã?š™™™™™é?ffffff@š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™É?ffffffæ?333333Ó?333333ã?š™™™™™@ffffffæ?333333ó?ð?333333Ó¿@333333ã?333333ó?333333@ffffffö?ø?࿚™™™™™É?š™™™™™ù?š™™™™™¹?ð?š™™™™™Ù?333333@ÍÌÌÌÌÌô?š™™™™™É¿ÍÌÌÌÌÌì?333333ã?@ffffffæ?š™™™™™¹?ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™Ù?š™™™™™¹¿ð?@ffffff@ð¿ÍÌÌÌÌÌì?š™™™™™ñ?à?333333ó?à?à¿333333ó?ffffffæ?š™™™™™Ù?š™™™™™é?333333ã?š™™™™™ñ?š™™™™™@š™™™™™ù?š™™™™™é?š™™™™™ñ?ffffffæ?ffffff@333333û?333333û?ð?š™™™™™¹¿à?š™™™™™É¿š™™™™™¹¿š™™™™™ñ?š™™™™™¹?š™™™™™¹?333333ã?š™™™™™é?š™™™™™@à?ffffffæ?š™™™™™ñ?333333Ó?ffffffö?ð?@š™™™™™Ù?ffffffö?ÍÌÌÌÌÌô?ø?333333û?ffffffæ?š™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™ñ?š™™™™™ù?333333ã?š™™™™™¹?š™™™™™¹?333333Ó?š™™™™™¹?š™™™™™É¿š™™™™™¹?333333ã?š™™™™™ù?š™™™™™ñ?333333ó?ffffffö?333333û?š™™™™™ñ?š™™™™™¹?š™™™™™ù?š™™™™™¹¿ð?š™™™™™é?à?333333ã?333333Ó?š™™™™™ñ?ffffffæ?333333Ó?š™™™™™É?ÍÌÌÌÌÌô?ffffffö¿š™™™™™Ù¿š™™™™™É?š™™™™™@333333Ó¿333333ã?š™™™™™¹¿ø?ffffffæ?ð?ÍÌÌÌÌÌ @ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™¹?ø¿à?ð?ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô?ffffffö?š™™™™™Ù?ffffff@ÍÌÌÌÌÌ@333333ã?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?333333Ó¿ÍÌÌÌÌÌô?š™™™™™é?š™™™™™¹¿š™™™™™Ù?ÍÌÌÌÌÌ@ffffff@ð?333333ã?š™™™™™¹?š™™™™™¹?š™™™™™ñ?ffffffþ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?333333ã?š™™™™™¹?à?ø?ø?@š™™™™™É?š™™™™™Ù?333333ã?ø?333333ã?š™™™™™é?333333 @ffffffþ?ffffff@333333ó?333333û?À333333Ó¿@š™™™™™ù?à?š™™™™™Ù?ð?š™™™™™@ÍÌÌÌÌÌ@š™™™™™Ù¿ffffffö? @à?š™™™™™¹?š™™™™™é?š™™™™™¹¿š™™™™™¹?333333ã?ffffffæ?ÍÌÌÌÌÌÀ333333ã?ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?ffffffæ?@ÍÌÌÌÌÌü?ffffffþ?$@š™™™™™É¿š™™™™™¹?š™™™™™@@ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@@à?ÍÌÌÌÌÌ@š™™™™™É?ø?š™™™™™é?ÍÌÌÌÌÌì?ø¿š™™™™™É?š™™™™™Ù?ÍÌÌÌÌÌì?ffffff@333333ã?š™™™™™¹?ÍÌÌÌÌÌô?š™™™™™é?333333ó?à?ÍÌÌÌÌÌ@333333û?ð?ffffffö?ÍÌÌÌÌÌì?ffffff@š™™™™™é¿à?ÍÌÌÌÌÌì?à?ÍÌÌÌÌÌ@@š™™™™™ñ?ø?à?ffffffö?ffffffæ?š™™™™™É?š™™™™™¹?ffffff @ffffffö?ð?š™™™™™ù?š™™™™™é?š™™™™™é?š™™™™™é?ffffffæ?ð?š™™™™™¹?333333@à?š™™™™™ñ?ð?333333û?ffffffÀ@š™™™™™Ù¿à¿š™™™™™é¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™Ù¿ffffffþ¿š™™™™™À333333!Àš™™™™™(À*Àš™™™™™=Àš™™™™3ÀÀÍÌÌÌÌÌü¿ÍÌÌÌÌÌÀš™™™™™Ù¿ffffff-ÀÍÌÌÌÌL>ÀÍÌÌÌÌÌÀ333333*À333333ÀÍÌÌÌÌÌ ÀÀÍÌÌÌÌÌÀffffff!Àffffff(ÀÍÌÌÌÌÌ1Àffffff ÀÀš™™™™™:ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ.Àš™™™™™Àà¿ÍÌÌÌÌÌ2ÀffffffÀ3333335À33333³:À333333Àð¿ffffff+ÀÀ@ÍÌÌÌÌÌ@š™™™™™@333333ó?š™™™™™ù?ÍÌÌÌÌÌ'@ffffff@ffffffæ?333333ó?š™™™™™@@ffffff @ffffff@ÍÌÌÌÌÌ!@ffffff@@š™™™™™é?š™™™™™é?š™™™™™@š™™™™™@333333@š™™™™™@@@333333 @ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?š™™™™™Ù?ÍÌÌÌÌÌì?@ø?ÍÌÌÌÌÌ@ffffffæ?ffffff@@333333û?333333 @ÍÌÌÌÌÌ@333333û?@š™™™™™@š™™™™™@333333 @ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ffffffö?ø?333333@š™™™™™!@ffffffþ?333333@š™™™™™ù?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @ @ffffff @ÍÌÌÌÌÌ@333333@ffffff@ÍÌÌÌÌÌü?š™™™™™@ø?@ÍÌÌÌÌÌ@333333@ffffff @ffffff@ffffff@š™™™™™@333333@ÍÌÌÌÌÌ @@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™ @š™™™™™ù?!@333333'@ÍÌÌÌÌÌ@ø?à?š™™™™™ù?333333ó?ffffff@@333333@333333@ffffff@š™™™™™Ù?š™™™™™@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌ@@@333333@ffffff@š™™™™™ @333333@333333.@ffffff@ÍÌÌÌÌÌü?333333 @333333@ffffff@ffffff@@š™™™™™ @333333 @@š™™™™™ @š™™™™™ù?ÍÌÌÌÌÌì?ffffffö?ffffff@333333û?ð?@ffffff @@ÍÌÌÌÌÌ@ffffff@ffffff@ÍÌÌÌÌÌ@ffffff@ffffff#@@š™™™™™@@š™™™™™@ffffff @š™™™™™@333333@@#@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@š™™™™™@@333333@@ffffffæ?ÍÌÌÌÌÌ@333333û? @ffffff@ffffff@333333ó?@ÍÌÌÌÌÌ@š™™™™™"@ffffff@ÍÌÌÌÌÌ@ffffffæ?š™™™™™ù?š™™™™™@š™™™™™@333333!@ÍÌÌÌÌÌ @333333@ÍÌÌÌÌÌ @ffffff@ffffff@@š™™™™™@@ffffff@ÍÌÌÌÌÌì?@333333@333333@333333@š™™™™™ñ?333333'@ @ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?ð?ÍÌÌÌÌÌ%@ffffff@š™™™™™@ffffff@š™™™™™ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @@ffffff@333333@š™™™™™ @ @ffffff@š™™™™™@333333ó?@ÍÌÌÌÌÌü?š™™™™2@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌ@ffffff@333333@333333ó?š™™™™™ @ÍÌÌÌÌÌü?š™™™™™@@š™™™™™@ÍÌÌÌÌÌ@ffffffæ?@@333333!@š™™™™™,@333333@ffffff@š™™™™™(@333333@ÍÌÌÌÌÌ@333333%@š™™™™™ @š™™™™™#@ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?@ÍÌÌÌÌÌ@ @333333û?š™™™™™@333333ó?š™™™™™@š™™™™™@@333333@ffffff@333333!@ @ð?ø?ÍÌÌÌÌÌ@333333@ø?!@ð?@š™™™™™@333333$@@@š™™™™™ @ @333333@ÍÌÌÌÌÌ@333333@333333 @@@ffffff@ÍÌÌÌÌÌü?ffffff@333333@ffffffþ?ffffff@@ÍÌÌÌÌÌ@ffffff @ffffffæ?333333@ffffff@ÍÌÌÌÌÌ@333333@333333ó?!@333333,@ÍÌÌÌÌÌ(@@š™™™™™!@333333@š™™™™™@ffffffæ?333333@š™™™™™@š™™™™™ñ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?@ÍÌÌÌÌÌ"@ffffff@@š™™™™™ù?ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@š™™™™™@ffffffæ?@333333 @@š™™™™™ @@š™™™™™é?š™™™™™@š™™™™™ @333333"@ffffff@ @333333@ffffff@ÍÌÌÌÌÌ@š™™™™™é?333333@@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?$@ffffffþ?ÍÌÌÌÌÌ @@ffffff @š™™™™™"@@@!@333333ó?š™™™™™@ffffff@š™™™™™@ffffff@ÍÌÌÌÌL6@ffffff @ffffff@@š™™™™™@ffffff@ffffff@š™™™™™é?ffffff)@@333333û?333333û?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ)@ @š™™™™™ñ?š™™™™™ù?ÍÌÌÌÌÌ@ffffff#@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ@333333@ø?333333@š™™™™™ @ffffff@š™™™™™ @@š™™™™™@ffffff@ÍÌÌÌÌÌ@@ffffff@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ%@ffffff@ÍÌÌÌÌÌ!@ÍÌÌÌÌÌ@ffffffþ?333333 @š™™™™™@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@€<@ð?ffffff$@333333@ffffffþ?ffffff@ @š™™™™™ @š™™™™™@ @333333ó?š™™™™™ @ffffff@ffffff@@ffffff@ffffff@ð?ffffff@'@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@š™™™™™ @333333 @ÍÌÌÌÌÌ@ffffff@333333 @š™™™™™@ffffff @ÍÌÌÌÌÌ@333333@333333@ffffffþ?ffffffþ?à?ffffff @ffffff3@333333@333333'@š™™™™™Àš™™™™™ÀffffffÀš™™™™™ÀÀfffff¦EÀ€1ÀÍÌÌÌÌÌ!Àš™™™™™À!ÀÍÌÌÌÌÌ)ÀÍÌÌÌÌÌÀš™™™™™ À À333333ÀÍÌÌÌÌÌ'Àffffff#Àš™™™™™*ÀÀffffffÀffffff#ÀffffffÀ333333%Àffffff%À33333³0Àš™™™™™'À333333Àš™™™™™!ÀffffffÀffffffÀ333333&ÀffffffÀÍÌÌÌÌÌ&Àš™™™™™Àffffff"ÀÀÍÌÌÌÌÌÀš™™™™™ Àš™™™™™À333333!À333333(À333333'ÀÍÌÌÌÌÌ!À333333ÀffffffÀffffffÀffffff,ÀÍÌÌÌÌÌÀffffff!À333333ÀÍÌÌÌÌÌÀffffff)Àffffff!Àš™™™™™0Àš™™™™™.Àš™™™™™%Àffffff&ÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀ333333$ÀÍÌÌÌÌÌ&À333333ÀffffffÀffffffÀš™™™™™ÀÀš™™™™™'À&ÀffffffÀffffff"ÀÍÌÌÌÌÌ%Àš™™™™™ À)Àffffff#ÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌ$À)ÀÍÌÌÌÌÌÀš™™™™7À0Àffffff,Àš™™™™™ ÀffffffÀffffff&ÀÍÌÌÌÌÌÀÀš™™™™™ Àš™™™™™ ÀffffffÀš™™™™™ Àš™™™™™!À333333 À333333-Àffffff*ÀÍÌÌÌÌÌ'ÀÀffffff+À333333À333333(À33333³0ÀÀ333333Àš™™™™™#À5ÀÍÌÌÌÌÌ0ÀÍÌÌÌÌÌÀffffff2ÀffffffÀ"À333333 ÀÍÌÌÌÌÌ À333333 Àš™™™™™À!ÀffffffÀ333333û¿ffffffÀÀffffff"À333333&À À(Àš™™™™™À'À333333+À-Àš™™™™™!À)ÀÍÌÌÌÌÌ0Àš™™™™™ù¿ffffffÀ333333"Àš™™™™™ÀÍÌÌÌÌÌ#À33333³1ÀÍÌÌÌÌÌ!ÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌÀffffff"À333333!À333333À333333"ÀÀ333333 ÀÍÌÌÌÌÌ+À$ÀÀffffffÀ&Àffffff/ÀÍÌÌÌÌÌÀš™™™™™À!Àš™™™™™Àffffff'ÀÍÌÌÌÌÌ"À4Àffffff&ÀÀ33333³3ÀÀ333333%À333333!À333333À"ÀÀffffffÀffffff+ÀÍÌÌÌÌL1ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ,À333333(Àš™™™™™À4ÀÍÌÌÌÌÌ&À333333(À,À333333+Àš™™™™™ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ$ÀÍÌÌÌÌÌÀš™™™™™-ÀÍÌÌÌÌÌ À@AÀ/ÀÀ333333ÀÍÌÌÌÌL2À333333ÀÍÌÌÌÌÌÀš™™™™™Àfffffæ7Àš™™™™™)ÀÀ3333331Àš™™™™™)Àffffff)Àfffffæ5ÀffffffÀÍÌÌÌÌÌ!À$Àffffff À333333 Àš™™™™™Àš™™™™™ÀÍÌÌÌÌÌ À333333ÀÍÌÌÌÌÌ#À+ÀffffffÀ333333)ÀffffffÀ"ÀÍÌÌÌÌL=À333333)Àffffff$Àš™™™™™$Àš™™™™™-ÀffffffÀš™™™™™ À333333Àffffff%ÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ'À€1ÀffffffÀš™™™™™À)ÀÀš™™™™™"Àš™™™™™/À€2À$À/ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™À0À333333À333333ÀffffffÀš™™™™™ÀffffffÀffffff ÀÍÌÌÌÌÌÀ333333"ÀffffffÀffffff"Àš™™™™™ÀffffffÀÍÌÌÌÌÌ%À Àffffff'À#À333333Àš™™™™™ÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌ%ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌÀ!ÀÍÌÌÌÌÌ$ÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌ1Àffffff!Àš™™™™™Ù¿š™™™™™*Àš™™™™™#Àš™™™™™$À333333À333333Àš™™™™™!ÀÀ À(Àš™™™™™ À333333,À"Àffffffþ¿ ÀÀÀ)Àš™™™™™%À(ÀÍÌÌÌÌÌ&ÀÍÌÌÌÌÌ2À2Àffffff À333333Àš™™™™™ Àffffff'ÀÀÍÌÌÌÌÌ"Àš™™™™™ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌ/À.ÀÍÌÌÌÌÌô¿š™™™™™ÀÀffffffÀ333333Àffffff&ÀÍÌÌÌÌÌ*ÀffffffÀ333333*À333333À À)Àš™™™™™+À333333&ÀffffffÀ333333Àš™™™™™'ÀÍÌÌÌÌÌ)Àffffff,Àš™™™™™/Àffffff-Àš™™™™™7À>Àš™™™™™!ÀÍÌÌÌÌÌ2ÀffffffÀffffffÀ333333Àffffff-ÀÍÌÌÌÌÌ*ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌ-À;Àš™™™™™%ÀÍÌÌÌÌÌÀffffff(ÀÀÍÌÌÌÌÌÀffffffÀffffffÀ#ÀÍÌÌÌÌÌ À$À333333À€5Àš™™™™™+À'Àš™™™™™À€=À333333ÀÍÌÌÌÌÌ Àfffffæ0ÀÀš™™™™Y@Àffffff,ÀÍÌÌÌÌÌ$Àø¿ÍÌÌÌÌÌÀ333333'ÀÍÌÌÌÌL2Àffffff%Àš™™™™™+Àš™™™™™À Àffffff#À333333/ÀÀffffff#À€3ÀÍÌÌÌÌÌÀ+Àffffff+À3333335ÀÍÌÌÌÌÌ+À.À333333#ÀÍÌÌÌÌÌ0ÀÍÌÌÌÌÌÀ333333$Àš™™™™™(ÀffffffÀš™™™™™CÀ(Àffffff!À333333"Àš™™™™™À333333À333333,À333333%ÀffffffÀÍÌÌÌÌÌ.ÀÀ333333À+ÀÀffffff$Àš™™™™™"À)ÀÍÌÌÌÌÌü¿ffffffö¿*À333333 Àš™™™™™Àffffff ÀÍÌÌÌÌÌ!ÀffffffÀ,ÀÀš™™™™™@@@ø?ÍÌÌÌÌÌô?š™™™™™é?@333333 @ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™Ù?333333 @333333ó?@š™™™™™ù?š™™™™™@@ÍÌÌÌÌÌ@ffffffö?ffffffæ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@š™™™™™ @ÍÌÌÌÌÌ-@ð?ÍÌÌÌÌÌ@š™™™™™ù?ÍÌÌÌÌÌ@ð?ÍÌÌÌÌÌÀš™™™™™ñ¿Àš™™™™™É?š™™™™™é?à?à¿ffffff濚™™™™™é?333333ã¿333333ã?࿚™™™™™é?š™™™™™É¿š™™™™™É?š™™™™™¹?333333Ó?࿚™™™™™é¿š™™™™™Ù¿333333Ó?š™™™™™¹¿š™™™™™É¿333333Ó¿š™™™™™Ù?333333Ó¿333333Ó?š™™™™™ñ?š™™™™™ù¿š™™™™™Ù?š™™™™™É¿š™™™™™É?š™™™™™¹?š™™™™™¹?à?š™™™™™Ù¿333333Ó?š™™™™™¹?š™™™™™¹?ffffffö¿333333Ó?š™™™™™Ù¿333333Ó¿š™™™™™É?333333Ó¿à?333333Ó¿š™™™™™¹?333333Ó?333333ӿ࿚™™™™™¹¿š™™™™™é?ffffff濚™™™™™Ù¿š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™É¿š™™™™™É?š™™™™™¹?à?333333Ó?š™™™™™É?333333Ó?š™™™™™¹¿š™™™™™É¿333333Ó?333333Ó?333333Ó?333333Ó?š™™™™™Ù¿à¿š™™™™™¹?š™™™™™¹?š™™™™™Ù¿333333ã¿ffffffÀš™™™™™¹¿š™™™™™É¿š™™™™™é¿š™™™™™Ù¿š™™™™™Ù¿333333Ó¿š™™™™™É¿333333Ó?š™™™™™É¿333333ã?333333Ó?333333Ó¿333333Ó?š™™™™™Ù¿333333Ó?à?ffffffæ¿333333Ó?š™™™™™¹¿š™™™™™ñ¿333333㿚™™™™™É¿š™™™™™É?333333Ó¿333333Ó?ffffffæ?333333Ó¿š™™™™™Ù?333333Ó?333333Ó¿š™™™™™¹?š™™™™™¹¿333333㿚™™™™™¹?ffffffæ?š™™™™™É¿š™™™™™Ù¿š™™™™™Ù?š™™™™™É?š™™™™™¹?š™™™™™Ù?ffffff濚™™™™™Ù¿333333ã¿ÍÌÌÌÌÌì?š™™™™™¹¿à¿š™™™™™¹?ffffff濚™™™™™é?š™™™™™É?š™™™™™Ù?š™™™™™é?333333Ó?š™™™™™¹?š™™™™™Ù?ffffffæ?š™™™™™é¿333333Ó?š™™™™™¹?š™™™™™Ù?š™™™™™Ù¿333333Ó?š™™™™™¹¿š™™™™™¹?à¿333333Ó¿333333Ó?š™™™™™É¿š™™™™™Ù?š™™™™™¹?š™™™™™É?333333Ó?š™™™™™É?333333ã¿333333Ó?š™™™™™É?333333ã?333333ã?࿚™™™™™¹?333333Ó?š™™™™™É¿à?š™™™™™Ù¿š™™™™™¹?š™™™™™Ù?333333Ó?333333Ó?ffffff濚™™™™™É¿à?333333Ó?š™™™™™É?࿚™™™™™¹?š™™™™™¹?š™™™™™É?ffffffö¿ÍÌÌÌÌÌ쿚™™™™™É?š™™™™™é?š™™™™™É?š™™™™™¹?š™™™™™é?š™™™™™É¿š™™™™™É?333333Ó?333333ó?š™™™™™É?ÍÌÌÌÌÌì?š™™™™™¹¿333333Ó¿à?š™™™™™Ù¿š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™¹?à¿333333Ó?333333Ó¿š™™™™™Ù?š™™™™™É?š™™™™™ñ?333333Ó?ÍÌÌÌÌÌ쿚™™™™™Ù¿333333Ó¿š™™™™™¹?333333ã¿ffffffæ¿ffffff濚™™™™™É¿à?𿚙™™™™É?࿚™™™™™Ù?š™™™™™É¿333333Ó¿à¿333333Ó?333333Ó?333333㿚™™™™™¹?ffffff濚™™™™™Ù¿ffffffæ?š™™™™™¹?š™™™™™¹¿š™™™™™Ù?š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™¹?333333ã¿à?š™™™™™¹?ffffffæ?333333ã¿ð?š™™™™™é?š™™™™™É?š™™™™™¹¿333333Ó¿š™™™™™¹?333333Ó?333333ã?š™™™™™¹?š™™™™™É¿š™™™™™¹?š™™™™™¹¿š™™™™™é¿à?š™™™™™¹¿š™™™™™¹?à?š™™™™™É¿š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿š™™™™™¹?333333Ó¿ffffffæ¿333333Ó?࿚™™™™™¹?à?š™™™™™Ù?à?333333Ó?š™™™™™¹?š™™™™™¹?š™™™™™É?333333Ó?333333Ó¿š™™™™™Ù?š™™™™™É?333333Ó?š™™™™™É¿š™™™™™É¿333333㿚™™™™™¹?ffffffæ¿ffffff濚™™™™™Ù¿ð¿š™™™™™Ù¿š™™™™™¹?333333㿚™™™™™É?333333ã?࿚™™™™™É¿333333Ó¿š™™™™™É?333333Ó?333333Ó¿š™™™™™É?š™™™™™É¿š™™™™™Ù¿š™™™™™Ù?š™™™™™Ù¿333333Ó¿š™™™™™Ù?š™™™™™É?ffffff濚™™™™™É?š™™™™™¹¿333333Ó¿š™™™™™Ù¿š™™™™™É?333333Ó¿ffffffæ¿à¿š™™™™™Ù¿333333Ó?𿚙™™™™É¿333333ã?š™™™™™É?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ쿚™™™™™É?ð?333333ã?š™™™™™É?ð?333333ã¿à?333333ã?333333Ó?š™™™™™ñ¿š™™™™™É?ÍÌÌÌÌÌ쿚™™™™™¹¿š™™™™™¹?333333Ó?š™™™™™¹?š™™™™™é?š™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™ñ¿š™™™™™¹?š™™™™™¹?š™™™™™Ù¿à?š™™™™™é¿333333Ó?333333㿚™™™™™¹?š™™™™™É?š™™™™™É¿š™™™™™É?š™™™™™ù¿š™™™™™É¿333333Ó?ÍÌÌÌÌÌì?š™™™™™é¿š™™™™™É?š™™™™™Ù¿š™™™™™É?š™™™™™É?𿚙™™™™Ù?š™™™™™É?333333ã?ffffff濚™™™™™Ù?š™™™™™Ù?š™™™™™Ù?à?š™™™™™¹?š™™™™™Ù?š™™™™™Ù¿š™™™™™É?333333㿚™™™™™Ù?333333Ó?à?à?š™™™™™¹¿š™™™™™É?š™™™™™Ù?š™™™™™¹?࿚™™™™™Ù¿ffffffæ?ð?š™™™™™É¿š™™™™™é¿à¿333333ã¿333333Ó?š™™™™™É?333333Ó¿š™™™™™¹?š™™™™™É?š™™™™™É¿š™™™™™Ù?à?ffffffæ?š™™™™™¹?à?š™™™™™É?š™™™™™Ù¿ffffff @à?š™™™™™Ù?333333@@333333À333333@ÍÌÌÌÌÌ@333333 @@@ÍÌÌÌÌÌ@333333@š™™™™™ÀÍÌÌÌÌÌì¿333333ã?ÍÌÌÌÌÌô?333333 @ffffff@š™™™™™é?333333ã?ffffff@š™™™™™ù?ÍÌÌÌÌÌ@ @š™™™™™@š™™™™™é?ð?ffffff@š™™™™™@ffffffö?š™™™™™@ÍÌÌÌÌÌô?333333@333333û?ÍÌÌÌÌÌ @ffffff@ÍÌÌÌÌÌ@333333@ffffffö?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffffþ?333333@ffffff@š™™™™™¹¿ÍÌÌÌÌÌ@333333û?333333 @š™™™™™ñ?š™™™™™¹¿š™™™™™ @š™™™™™¹¿@@ø?ÍÌÌÌÌÌ@š™™™™™@ffffff@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌ @333333ó?ffffffö?@ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?š™™™™™@š™™™™™@@333333@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffffæ?ffffffþ?š™™™™™ù?ffffff @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@#@ÍÌÌÌÌÌ@@$@ÍÌÌÌÌÌ@333333Ó?š™™™™™@@ffffffþ?@ÍÌÌÌÌÌ@333333Ó?ÍÌÌÌÌÌô?š™™™™™Ù?ð?š™™™™™@ÍÌÌÌÌÌü?š™™™™™É¿ffffffö?ÍÌÌÌÌÌ@@ÍÌÌÌÌÌü?ø?ffffff@333333@ @@333333@ÍÌÌÌÌÌì¿@333333@@š™™™™™@ffffff@ÍÌÌÌÌÌ@333333 @333333û?š™™™™™@333333ã?@š™™™™™@333333@ffffff@333333@š™™™™™ @š™™™™™ù?ø?ÍÌÌÌÌÌ@@333333Ó?@ffffff @š™™™™™ñ?š™™™™™Ù?ÍÌÌÌÌÌ@@à¿333333@š™™™™™@@@333333@ @ffffffæ?š™™™™™É¿š™™™™™é¿š™™™™™ @š™™™™™ @š™™™™™é?ð?@333333ã?333333û?š™™™™™ @à?à?š™™™™™@@ÍÌÌÌÌÌ@333333Ó¿333333û?@š™™™™™@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@333333Ó?@ffffff@ffffffæ?š™™™™™ù¿š™™™™™ñ?333333 @ @@333333Ó?@@ÍÌÌÌÌÌü?@š™™™™™Ù?ffffffþ?ffffff@ð?333333@@333333ó?ffffffþ?ÍÌÌÌÌÌ@à?@ÍÌÌÌÌÌ@š™™™™™ù?ÍÌÌÌÌÌ@@333333@š™™™™™É¿ffffff@ÍÌÌÌÌÌ@@š™™™™™@ffffff @ffffff@š™™™™™ @š™™™™™ @š™™™™™é¿š™™™™™ À@ffffff!@ffffff@ffffff@ffffff@š™™™™™ù?š™™™™™ñ?ÍÌÌÌÌÌ@ø?š™™™™™É¿ÍÌÌÌÌÌ @@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?333333@@š™™™™™@@ffffffþ?ffffff @333333û?@ffffff@š™™™™™é?@š™™™™™ñ¿š™™™™™@ÍÌÌÌÌÌ @ffffffæ? @š™™™™™ù?ffffffö?š™™™™™ù?š™™™™™ù?333333 @333333@š™™™™™ù?@à¿ffffff@333333ã¿333333û?333333 @@333333 @ÍÌÌÌÌÌ@333333ó?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™ @ffffffæ?ÍÌÌÌÌÌì?333333@š™™™™™ñ?ffffffö? @ @š™™™™™@333333 @š™™™™™@ffffff@š™™™™™É?333333û?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@333333㿚™™™™™@ÍÌÌÌÌÌ@333333@ffffff@ffffff@š™™™™™ @ÍÌÌÌÌÌ@333333ã?333333@ÍÌÌÌÌÌì?š™™™™™@ð?ÍÌÌÌÌÌì?š™™™™™@š™™™™™@@š™™™™™Ù?ffffff@ffffffÀš™™™™™ÀÍÌÌÌÌÌü?ffffff!@@ÍÌÌÌÌÌì?š™™™™™ù?ffffffö?333333ã?ffffffö?@ÍÌÌÌÌÌ@333333@š™™™™™ @ffffff@ÍÌÌÌÌÌ@š™™™™™ @ÍÌÌÌÌÌÀ@ffffffþ?ÍÌÌÌÌÌÀ@š™™™™™@š™™™™™É¿ÍÌÌÌÌÌ@@š™™™™™é?ffffff @ffffff@ffffffö?ÍÌÌÌÌÌÀš™™™™™@š™™™™™@ÍÌÌÌÌÌì?š™™™™™ @ffffff@@ffffff@@ffffffæ?š™™™™™ñ?š™™™™™@@333333 @š™™™™™@š™™™™™é?333333@ffffffæ?š™™™™™Ù¿ffffffö?@@333333ã?@@333333û?333333û?ffffffö¿333333@ÍÌÌÌÌÌü?333333@ffffffþ?333333 @333333@ø?ffffff@š™™™™™ @š™™™™™ñ?333333@@ffffff@333333"@š™™™™™ñ¿ @ffffff*@ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ffffff@ffffff@333333Ó?333333Ó?ffffffö?ffffffþ?333333!Àš™™™™™ñ?ÍÌÌÌÌÌ@@333333@333333@ÍÌÌÌÌÌ @ffffff@@ÍÌÌÌÌÌ'@ffffffö?ffffffö?š™™™™™@333333ã?ÍÌÌÌÌÌ&@ffffff@š™™™™™"@ÍÌÌÌÌÌ@š™™™™™é?333333û?š™™™™™é?333333@ffffffæ?š™™™™™@š™™™™™Àffffff @333333ã¿ÍÌÌÌÌÌ@ @š™™™™™Ù¿ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@@š™™™™™ñ?š™™™™™ù?ÍÌÌÌÌÌ@333333 @333333ó?ø?ffffff@𿚙™™™™ñ?š™™™™™@š™™™™™@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ffffff@ÍÌÌÌÌÌÀ333333 @ð¿333333ã¿@ffffffö?333333@ÍÌÌÌÌÌ@333333@š™™™™™ñ?ffffff @ÍÌÌÌÌÌ @ffffffæ?ø?333333@ÍÌÌÌÌÌ@ffffff@ø?š™™™™™!@À@ÍÌÌÌÌÌ@333333)Àffffff0Àfffffæ4@ffffff)ÀÍÌÌÌÌÌ(Àš™™™™Ù[Àfffff&GÀ€4ÀÍÌÌÌÌÌ+Àš™™™™™%Àfffff¦AÀ333333$ÀÍÌÌÌÌÌü¿ffffff'@à?š™™™™™6Àffffff3Àš™™™™4ÀÍÌÌÌÌÌDÀÍÌÌÌÌÌ@ffffff Àffffffö¿ÍÌÌÌÌÌì?ffffff+À33333³4ÀIÀ€9À)Àfffffæ0À€3ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ(Àffffff1Àš™™™™™+Àš™™™™™7ÀÍÌÌÌÌÌ0Àš™™™™1À333333 Àffffff"ÀÍÌÌÌÌÌÀffffffö¿4ÀÍÌÌÌÌÌ2À&Àffffff+À333333À333333)ÀÍÌÌÌÌÌ%À33333³5ÀfffffæBÀš™™™™™ ÀffffffÀ333333û?!Àffffff8Àfffffæ2ÀffffffCÀÍÌÌÌÌŒBÀ5Àš™™™™:À333333"@ÍÌÌÌÌÌÀš™™™™™!Àš™™™™6À"À333333,À+Àffffffæ¿ÍÌÌÌÌÌ9ÀÀffffff"À333333:À333333;Àffffff$ÀÍÌÌÌÌL3À&Àš™™™™YGÀffffff(Àffffff<ÀÍÌÌÌÌL8Àš™™™™AÀfffffæ1Àfffff¦AÀffffff"@ffffffGÀš™™™™ihÀÍÌÌÌÌL9ÀÍÌÌÌÌÌ<Àfffff¦\ÀÀOÀffffff=À333333 Àfffffæ;À333333Ó¿ffffff@)@ÍÌÌÌÌÌ @333333 Àš™™™™™=Àš™™™™™ù¿ffffff@fffffæ7Àš™™™™<Àš™™™™™AÀÍÌÌÌÌÌô?š™™™™™2À€6À333333&À,ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀffffffÀš™™™™0ÀÍÌÌÌÌŒFÀfffff¦BÀ!ÀÍÌÌÌÌ BÀš™™™™™À2À333333Àffffffæ?333333/ÀÍÌÌÌÌÌ0À333333À€0Àš™™™™™¹¿333333/Àš™™™™™@/Àffffff*À3Àffffff-Àš™™™™3ÀffffffÀfffffæ>Àš™™™™2À333333%ÀÍÌÌÌÌÌ$Àffffff*ÀÍÌÌÌÌL?À333333@#ÀÍÌÌÌÌÌ À333333+Àfffffæ4ÀÍÌÌÌÌ AÀ333333*ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333-Àfffffæ2ÀÍÌÌÌÌÌ0Àffffff$Àfffffæ0ÀÍÌÌÌÌL1Àš™™™™™+Àš™™™™5Àffffff8ÀÍÌÌÌÌÌ"À333333Àš™™™™7À33333³:ÀÀÀ€:À333333#À3333332À333333/Àš™™™™=Àffffff=À333333#À33333³HÀ333333%@33333³>À€1Àš™™™™™+Àffffff'Àffffff@$Àš™™™™Y@À33333³AÀffffffÀfffffæ=À€9ÀÍÌÌÌÌL2@ÍÌÌÌÌŒIÀ€<Àš™™™™™=Àfffff†RÀÍÌÌÌÌ FÀÍÌÌÌÌÌCÀ=@š™™™™™0À3À333333À33333sEÀš™™™™™ÀÍÌÌÌÌìPÀÍÌÌÌÌL?Àš™™™™™!Àš™™™™™À33333sDÀffffffÀÍÌÌÌÌÌ_ÀÀÍÌÌÌÌÌ"À33333“]Àš™™™™9ÀÀÍÌÌÌÌÌ Àš™™™™0À€:À€MÀffffff@š™™™™™Àš™™™™™6Àš™™™™™8ÀÍÌÌÌÌÌLÀÍÌÌÌÌÌ-ÀÀš™™™™™@$Àš™™™™™ñ?ÍÌÌÌÌÌ@€9ÀÍÌÌÌÌÌ:Àfffffæ<ÀÍÌÌÌÌÌ=Àš™™™™™@333333Àfffffæ7@ffffff(ÀffffffHÀš™™™™™"@%À3333335À3333337À333333û¿333333@š™™™™™1À5Àffffff'Àffffff7À)À€4À€GÀffffffÀÍÌÌÌÌÌ3Àffffff@š™™™™1Àffffffö?ffffffAÀ@MÀÍÌÌÌÌÌ1Àfffffæ;Àš™™™™™5À@NÀ7À33333³<Àffffff0À333333Ó¿š™™™™™¹¿ÍÌÌÌÌÌ-Àš™™™™7Àffffff%ÀÍÌÌÌÌÌ#Àffffffö¿š™™™™™@333333ÀÍÌÌÌÌÌ Àffffff @33333³1ÀÍÌÌÌÌÌÀ333333+ÀÍÌÌÌÌÌ+ÀÍÌÌÌÌÌü¿š™™™™™;À Àffffff9À3333333À333333@ÍÌÌÌÌÌÀ3333330À333333@š™™™™™+À333333+Àš™™™™=Àš™™™™3À€0ÀÀffffff)ÀÍÌÌÌÌÌ*ÀÍÌÌÌÌÌH@*ÀÍÌÌÌÌÌ9À3333337@ÍÌÌÌ̼`@333333#@@3333334À€8ÀÍÌÌÌÌÌÀfffffæ2Àffffff'Àš™™™™™/ÀÍÌÌÌÌì[À333333)Àffffff3ÀÍÌÌÌÌÌ>À€9À33333óAÀ3333331À333333%@š™™™™™3À333333À%@ÍÌÌÌÌÌ-ÀffffffÀ@@ÀÍÌÌÌÌL6Àfffff&AÀffffff1Àš™™™™™4À333333ó¿š™™™™™À333333@3333339À@ÍÌÌÌÌÌ,Àffffffæ¿ffffff/ÀfffffæEÀffffff6À,@ÍÌÌÌÌÌ%ÀÀš™™™™™+ÀÀš™™™™™:À333333?À€1Àø¿Àffffff%@š™™™™9À33333³0Àffffff@333333,ÀÍÌÌÌÌÌÀš™™™™™#À€FÀ0Àfffff¦BÀffffff3À33333sGÀš™™™™™1À€9À33333sIÀÍÌÌÌÌ GÀ333333Àš™™™™¹SÀÍÌÌÌÌ GÀ Àš™™™™™(À333333ó??ÀÍÌÌÌÌLBÀš™™™™™@33333óCÀ `À33333³6Àfffffæ;Àfffffæ0Àffffffö?333333%Àš™™™™™'ÀÍÌÌÌÌÌÀÀ33333³@@'À333333UÀÍÌÌÌÌÌ1Àš™™™™™@ÍÌÌÌÌLLÀ€@Àffffff>ÀÍÌÌÌÌÌ/ÀfffffF`ÀffffffÀ)ÀÀEÀÍÌÌÌÌÌ@€MÀÍÌÌÌÌÌ5À°bÀÀÍÌÌÌÌÌ&@ÍÌÌÌÌÌ-ÀÍÌÌÌÌÌÀš™™™™™8Àš™™™™™?À€1Àš™™™™™%ÀÍÌÌÌÌÌ!@3333334Àffffff-À€7À€2Àffffff0À333333?@ffffff-Àffffff @ÍÌÌÌÌÌ0Àš™™™™™JÀ€5Àffffff8Àfffff¦HÀš™™™™™%ÀÍÌÌÌÌLBÀš™™™™™+ÀÍÌÌÌÌÌ5ÀÍÌÌÌÌÌ:À333333/ÀðbÀ5Àš™™™™2À33333³5ÀÍÌÌÌÌÌ4Àffffffö¿š™™™™™@ÍÌÌÌÌÌ4Àffffff(@ÍÌÌÌÌÌ#À333333?ÀÍÌÌÌÌÌÀÀ33333³BÀš™™™™™À3333339Àš™™™™™.Àš™™™™™5Àffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌL9ÀÍÌÌÌÌL7À333333+À€6Àš™™™™™,Àš™™™™9R@ÍÌÌÌÌÌ0À+@ffffff.@333333ó?ÍÌÌÌÌÌÀ2@333333!@fffffÆWÀÍÌÌÌÌÌô¿@$@9@š™™™™YKÀffffff1Àš™™™™™@š™™™™™6@ÍÌÌÌÌŒA@33333ó@@ffffff(À333333(À3333338@š™™™™4@ÍÌÌÌÌÌ3@ÍÌÌÌÌÌ@@ÍÌÌÌÌL7@ÍÌÌÌÌÌÀ333333 Àffffff À@š™™™™™¹?333333 @333333Ó¿fffff&C@š™™™™™@ÍÌÌÌÌL8@š™™™™™;@333333@333333@ø¿33333³@@ffffff,@33333³1@ÍÌÌÌÌÌ-@@@ffffff'À333333@ffffff@6@ÍÌÌÌÌÌÀ6Àš™™™™™$@š™™™™™#Àš™™™™™2@333333<@š™™™™™ @ffffff2@€@@ffffff@š™™™™4@333333@333333*À@A@š™™™™ÙF@333333-@ffffffÀÍÌÌÌÌÌ=@ffffffÀffffff#@33333³9@€2À%@€@@ÍÌÌÌÌŒA@ÍÌÌÌÌÌÀš™™™™2@š™™™™™ñ?ÍÌÌÌÌ A@ÍÌÌÌÌÌ1ÀÀÍÌÌÌÌÌ"ÀÍÌÌÌÌÌÀ333333@ffffff;@ ÀÍÌÌÌÌÌ>@333333=À`SÀC@š™™™™™K@ffffff7@ÍÌÌÌÌÌô?:ÀÍÌÌÌÌÌ#@š™™™™™Ù¿ffffff(@ÍÌÌÌÌL>@fffff¦F@ÍÌÌÌÌÌ&@ @:Àffffff@fffffæ;@š™™™™™@š™™™™=Àš™™™™6ÀÍÌÌÌÌÌ=@333333/@š™™™™™À@8@ÍÌÌÌÌL5@333333<@š™™™™™7@š™™™™™@ÍÌÌÌÌ KÀ3333337@ÍÌÌÌÌL3@#@ffffff2@7@33333³4@fffffæ=@333333Ó?š™™™™C@333333Ó?333333Ó?š™™™™™=@š™™™™™"@€5@ @š™™™™YA@ÍÌÌÌÌÌ@333333Àffffff"@ÍÌÌÌÌÌ:@š™™™™™ Àš™™™™™@ÍÌÌÌÌÌ0@ffffffö¿333333Àfffffæ@@ÀD@ÍÌÌÌÌÌ @,@ffffff0@ÍÌÌÌÌÌ"@ÍÌÌÌÌŒA@33333ó@@ÍÌÌÌÌÌ3@ @ffffff À7À33333³;@fffffæ1@333333Àš™™™™™ÀÍÌÌÌÌÌ@ffffffÀÍÌÌÌÌÌô?#@š™™™™™Àffffffö¿ffffff@ffffff'@33333³1@š™™™™™!À333333%ÀÍÌÌÌÌÌ'@ÍÌÌÌÌÌM@@€B@š™™™™™:À@A@š™™™™™@ÍÌÌÌÌÌ/@ÀDÀffffff"Àš™™™™2@ÍÌÌÌÌÌ3@ÍÌÌÌÌ O@ÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀ333333@ÍÌÌÌÌÌ#@À5ÀfffffæD@€2ÀÍÌÌÌÌÌ-Àš™™™™™-@fffffæ>@ÍÌÌÌÌŒ@Àffffff,ÀÍÌÌÌÌÌD@ffffff'ÀÍÌÌÌÌÌ=@-@š™™™™™:Àš™™™™™'@ffffffö¿333333@š™™™™™&Àš™™™™™.@333333 Àš™™™™™&@š™™™™™MÀ7@"@333333@š™™™™™@ffffff-ÀÍÌÌÌÌLIÀš™™™™™@ÍÌÌÌÌLQ@€1@ÍÌÌÌÌŒF@1@333333@š™™™™™,À€AÀš™™™™™¹?333333À333333D@ffffff0@š™™™™<@š™™™™8@ÍÌÌÌÌÌ À-@33333³8@fffffæN@ÍÌÌÌÌÌG@3333331@33333sH@ÍÌÌÌÌÌ@š™™™™™3@š™™™™ùW@š™™™™™É?2@ÍÌÌÌÌ CÀ€2@ffffffA@333333%À€1@@ffffff(À333333ó?ÀffffffÀÍÌÌÌÌLA@333333Ó¿š™™™™™D@ÍÌÌÌÌÌ3À33333³C@ÍÌÌÌÌ EÀš™™™™™CÀ/@33333sF@ffffff0@š™™™™Ù@À333333$ÀÍÌÌÌÌÌ%@@€<@333333@333333À33333³;@ffffffþ¿š™™™™™Ù¿€A@€B@3333334@fffffæ8@€H@@š™™™™™ÀÍÌÌÌÌÌô?ffffffÀ@O@ÍÌÌÌÌÌ=Àš™™™™™&@š™™™™™:@ÍÌÌÌÌÌ4@33333³L@ÍÌÌÌÌÌ?@ffffff*@š™™™™™8@#ÀÍÌÌÌÌÌ @33333³0À@ÍÌÌÌÌÌÀÍÌÌÌÌÌ@:@ÍÌÌÌÌÌ"@ffffffP@!À0@!Àš™™™™9Y@9@š™™™™¹Z@333333@-ÀÍÌÌÌÌÌ!@ÍÌÌÌÌÌÀ333333ó¿ÍÌÌÌÌÌô¿33333sJÀÍÌÌÌÌÌ*@33333óC@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ!ÀÍÌÌÌÌÌ.@š™™™™3À333333!@333333$@ÍÌÌÌÌÌÀš™™™™™M@fffff&H@fffff&AÀffffffÀfffff¦H@333333Àš™™™™™5@3333336@ÍÌÌÌÌÌ@33333³DÀÍÌÌÌÌÌ@333333G@š™™™™™ ÀÍÌÌÌÌÌ?@$@3333331@š™™™™™?@33333³E@ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ@ÍÌÌÌÌL=@€2@š™™™™™)@ÍÌÌÌÌÌ;@š™™™™™$À5@š™™™™™é?@š™™™™™(Àš™™™™8@33333sP@ffffff$À3333336@ffffff&@ÍÌÌÌÌL7ÀÍÌÌÌÌÌ@fffff¦HÀfffff&O@€;À333333'@333333Àfffff¦@@š™™™™7À333333@š™™™™™>ÀÍÌÌÌÌÌÀ333333@fffffæ9@ÍÌÌÌÌŒG@ÍÌÌÌÌÌ@fffffæM@333333À333333ÀÍÌÌÌÌŒQ@š™™™™™-À%À%@33333SR@ÍÌÌÌÌÌÀ#À333333@€2@€<Àð?33333³NÀÍÌÌÌÌÌ7@€H@š™™™™™.Àffffff$@3333335@333333&@333333B@š™™™™™@À?@ffffff @ÍÌÌÌÌLV@ÍÌÌÌÌÌ*@fffffæ1@š™™™™YJ@fffffæ1@ffffffæ?š™™™™™¹¿ÍÌÌÌÌ G@š™™™™9Àš™™™™™:@;ÀÍÌÌÌÌLC@š™™™™8ÀÍÌÌÌÌ A@33333SW@3333339À333333(@fffffæ^@fffff&D@fffffæ:@ffffffÀÍÌÌÌÌŒ@À@L@@š™™™™YAÀ333333@333333(@ÍÌÌÌÌÌ8ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ@`UÀÍÌÌÌÌLI@ffffff @ffffffÀ$Àfffffæ1@š™™™™YIÀffffff*@333333 @333333)ÀP@333333%@š™™™™™7@333333À3333332@333333.ÀÍÌÌÌÌL0@'@fffffæ0@fffffæ1@K@𿚙™™™6@333333Àš™™™™ÙR@ffffff)@fffffæO@š™™™™Ù@@š™™™™™¹¿ffffffæ?à¿@š™™™™™ñ?š™™™™™é?ø?š™™™™™Ù?ÍÌÌÌÌÌ쿚™™™™™¹¿ø?š™™™™™é?ø?š™™™™™ù?š™™™™™¹?š™™™™™ù?ffffffæ?ffffffþ?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™ù?š™™™™™ @š™™™™™É¿333333û¿333333ó?š™™™™™Ù?333333ã?š™™™™™É?ffffffæ?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™Ù?š™™™™™é?ffffffæ¿333333ã?333333ã?࿚™™™™™¹¿ÍÌÌÌÌÌì?š™™™™™É¿š™™™™™Ù¿ø?333333㿚™™™™™ñ?š™™™™™é?333333Ó?š™™™™™É?š™™™™™É?ÍÌÌÌÌÌì?333333ó?š™™™™™é?à?ð?š™™™™™¹?š™™™™™¹?š™™™™™É?333333Ó?š™™™™™¹?š™™™™™¹?š™™™™™Ù?š™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌô?333333ã?š™™™™™é?ð?š™™™™™ @333333Ó¿à?ffffffæ?ÍÌÌÌÌÌì?333333ã?ffffffö?333333ã?š™™™™™¹?333333.@š™™™™™é¿š™™™™™ @333333 @@333333Ó?š™™™™™É?š™™™™™ù?ð¿333333û?࿚™™™™™ñ?š™™™™™¹?࿚™™™™™¹?ø?ø?ÍÌÌÌÌÌô?333333ã?ÍÌÌÌÌÌ@š™™™™™¹?š™™™™™@š™™™™™ñ¿š™™™™™Ù¿333333ã?ffffffæ?ffffffþ?ÍÌÌÌÌÌ@ffffffæ?333333û?ffffff濚™™™™™É¿333333Ó¿ð?š™™™™™ñ?à?š™™™™™Ù¿ffffffæ?š™™™™™É?333333Ó?333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™É¿333333ó?à?à?ÍÌÌÌÌÌì?ð?333333ã¿ffffffæ?ÍÌÌÌÌÌô?333333ã?ffffffæ?333333Ó?ÍÌÌÌÌÌì?š™™™™™é?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@š™™™™™É?š™™™™™É?ffffffæ?333333@ffffffæ?ffffffæ?ffffffæ?š™™™™™¹?š™™™™™é?š™™™™™é?š™™™™™ñ?š™™™™™É?š™™™™™¹?š™™™™™É?š™™™™™é?š™™™™™¹¿š™™™™™É?ÍÌÌÌÌÌì?ø?š™™™™™é¿@š™™™™™¹?333333ó?š™™™™™@š™™™™™¹¿333333Ó¿š™™™™™Ù?š™™™™™É¿š™™™™™Ù¿š™™™™™¹?š™™™™™É¿333333ã?ÍÌÌÌÌÌô?ffffffæ?333333û¿š™™™™™é?š™™™™™¹?333333ó?@š™™™™™ñ?333333û?ø¿ffffffö?š™™™™™¹?ffffffö?333333Ó¿333333@š™™™™™é?࿚™™™™™¹¿à¿ffffffæ?333333@š™™™™™É?333333@ÍÌÌÌÌÌì?š™™™™™¹¿ÍÌÌÌÌÌ@š™™™™™@š™™™™™ñ?@Àà?š™™™™™ñ?š™™™™™¹?@333333ó¿ÍÌÌÌÌÌì¿333333ó?ffffffö?333333ã?333333Ó?ffffffæ?š™™™™™ù?@à?ÍÌÌÌÌÌì?š™™™™™Ù?333333û¿à?333333ó?ÍÌÌÌÌÌì?ffffffæ?š™™™™™ñ?ffffffþ¿333333ã?š™™™™™é¿š™™™™™ñ?ø?š™™™™™É¿à?333333Ó?š™™™™™@š™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™É?š™™™™™@š™™™™™é¿ffffffþ?š™™™™™¹?š™™™™™¹?333333ã?333333ó?š™™™™™¹¿ÍÌÌÌÌÌô?š™™™™™Ù?ffffff濚™™™™™¹?š™™™™™É¿333333ã?š™™™™™¹?à¿ffffffæ?š™™™™™Ù¿ð?ÍÌÌÌÌÌì?333333ó?š™™™™™é?ffffffæ?333333Ó¿333333Ó?333333Ó?ffffffæ?333333ã?333333ã?333333Ó¿333333Ó?ÍÌÌÌÌÌ@333333ã?š™™™™™Ù?š™™™™™¹?ffffffÀš™™™™™¹¿ffffffæ?ø?š™™™™™é¿š™™™™™Ù?333333Ó¿ÍÌÌÌÌÌ@ffffffö?ffffff @š™™™™™@š™™™™™¹¿š™™™™™¹?333333û?š™™™™™ù?à?ffffffæ¿à?333333ó?ÍÌÌÌÌÌô¿ð?ð?ÍÌÌÌÌÌì?ffffff @ÍÌÌÌÌÌ@š™™™™™É¿ffffffþ?333333㿚™™™™™Ù¿333333@š™™™™™¹¿ffffffæ¿ÍÌÌÌÌÌì¿333333ã?333333@333333@333333ó?š™™™™™¹?@ffffffæ¿ffffffö?š™™™™™@ÍÌÌÌÌÌ@333333ã?ÍÌÌÌÌÌ쿚™™™™™ñ?@š™™™™™¹?ffffffö?ð?à?š™™™™™Ù?à?ÍÌÌÌÌÌ@333333@ffffff@ÍÌÌÌÌÌü?333333Ó¿@š™™™™™À333333û¿@š™™™™™ñ?à?࿚™™™™™é?ø?333333@š™™™™™é¿ffffffþ?ÍÌÌÌÌÌ@š™™™™™ñ?ÍÌÌÌÌÌì¿à¿à?š™™™™™É¿š™™™™™É?š™™™™™É?ffffff À333333ã?ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?š™™™™™¹?ÍÌÌÌÌÌ@à?333333 @ffffff@࿚™™™™™É¿ @ÍÌÌÌÌÌ@333333ã?ffffff@ÍÌÌÌÌÌô?à?333333@š™™™™™É?333333û?š™™™™™ñ?ð¿à?à?š™™™™™¹?š™™™™™ù?š™™™™™¹?333333Ó¿š™™™™™¹¿š™™™™™¹¿ÍÌÌÌÌÌ@à?333333ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?333333@š™™™™™É?š™™™™™@333333ó¿š™™™™™É?š™™™™™¹?333333ã?%@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ffffffþ?333333Ó?ÍÌÌÌÌÌô?ffffff濚™™™™™Ù?333333û?ffffff @š™™™™™É?š™™™™™ñ?ÍÌÌÌÌÌô?à?333333Ó?ffffffæ?333333ã?333333ã?š™™™™™é?333333@š™™™™™é?ffffffæ?ð?ð?ffffff濚™™™™™@ÍÌÌÌÌÌ쿚™™™™™é¿ÀÍÌÌÌÌÌì¿à¿ÍÌÌÌÌL6ÀÍÌÌÌÌL7Àffffffæ¿ð¿ð¿à¿33333s@À333333Àffffff3À333333ÀÍÌÌÌÌÌ Àffffff$À/À€=ÀÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌ*À2Àð¿%Àfffffæ1Àš™™™™1Àš™™™™™$ÀÍÌÌÌÌÌì¿ffffff%ÀÍÌÌÌÌÌ @333333@333333@"@!@ÍÌÌÌÌÌ3@š™™™™™ @ÍÌÌÌÌÌ@333333@333333@ÍÌÌÌÌÌ'@@ffffff@ÍÌÌÌÌÌ&@š™™™™™ @š™™™™™@ffffffö?333333û?@ÍÌÌÌÌÌ,@@333333,@!@ÍÌÌÌÌÌ%@š™™™™™@ffffff"@ffffff@@@ffffff@333333@ffffff@333333@š™™™™™ù?š™™™™™@ÍÌÌÌÌÌ@ffffff(@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ!@ÍÌÌÌÌÌ@ffffff@ffffff&@@ÍÌÌÌÌÌ@333333$@#@@333333@333333@3333332@ÍÌÌÌÌÌ#@ffffff0@ÍÌÌÌÌÌ @ffffff%@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff @@š™™™™™.@ffffff @š™™™™™@š™™™™™@ÍÌÌÌÌÌ!@333333@š™™™™™@ÍÌÌÌÌÌ/@@333333%@ffffff@+@ø?ÍÌÌÌÌÌ@@ÍÌÌÌÌL3@333333@ÍÌÌÌÌÌ @š™™™™™ @ffffff@ÍÌÌÌÌÌ.@333333@ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ@@š™™™™™3@#@ÍÌÌÌÌÌô?š™™™™™ @@š™™™™™@ffffff @ÍÌÌÌÌÌ$@ffffff@š™™™™™,@ÍÌÌÌÌÌ@@ffffff@š™™™™™-@0@š™™™™™@333333@@ÍÌÌÌÌÌ(@,@@3333331@ffffff<@ffffff$@333333@š™™™™™@333333@ÍÌÌÌÌÌ@ffffff@ffffff)@ffffff @š™™™™™@(@333333(@@@š™™™™™ù?ffffff@š™™™™™%@333333@š™™™™™!@@ÍÌÌÌÌÌ @333333@ffffff@š™™™™™@333333#@ffffff@ÍÌÌÌÌÌ@ffffff@š™™™™™3@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ#@ffffff#@š™™™™3@ffffff @@š™™™™™@@333333#@ffffff"@š™™™™™@ @333333@333333"@@š™™™™™é?š™™™™™@ÍÌÌÌÌÌ@š™™™™™@-@333333)@ffffff @ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌL5@ÍÌÌÌÌÌ$@333333$@ÍÌÌÌÌÌü?ffffff@š™™™™™@ÍÌÌÌÌÌ%@š™™™™™&@ffffff@%@@33333³5@š™™™™™ @ÍÌÌÌÌÌ@ffffff@š™™™™™@š™™™™™/@ @š™™™™™"@š™™™™™(@333333@ffffff @@3333332@ÍÌÌÌÌÌ @ffffff"@ffffff@š™™™™™ù?à?ÍÌÌÌÌÌ @š™™™™5@ @ffffff@333333@ffffff@ÍÌÌÌÌÌ/@ÍÌÌÌÌL4@ÍÌÌÌÌÌ+@ffffff @333333@š™™™™™&@@ffffff!@ÍÌÌÌÌÌ%@š™™™™™@ffffff@@333333;@333333"@š™™™™™!@@333333#@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ'@ffffff@š™™™™™)@ÍÌÌÌÌÌ%@ÍÌÌÌÌÌ*@š™™™™™@333333@ @š™™™™™+@š™™™™™(@fffffæ9@333333@(@33333³2@333333%@333333@3333335@ÍÌÌÌÌÌ"@3333331@@š™™™™™@333333&@ÍÌÌÌÌÌ @š™™™™™@333333 @ÍÌÌÌÌÌô?ffffff)@ÍÌÌÌÌÌ @š™™™™™$@ÍÌÌÌÌÌ@ffffff@ @333333&@š™™™™™%@ÍÌÌÌÌÌ!@ffffff@ÍÌÌÌÌÌ@š™™™™™&@š™™™™™!@š™™™™™ @ffffff#@ffffff @ @"@ffffff(@333333&@333333&@ÍÌÌÌÌÌ%@@š™™™™™@š™™™™™ @333333@ffffff(@ffffff@333333@ffffff@333333@fffffæ1@"@ffffff@š™™™™™#@@333333 @š™™™™™@@š™™™™™(@š™™™™™'@@333333-@ffffff@ffffff1@fffffæ:@ÍÌÌÌÌÌ8@š™™™™™"@ffffff4@š™™™™™.@333333@ÍÌÌÌÌÌ@333333@š™™™™™#@ÍÌÌÌÌÌ%@ÍÌÌÌÌÌ@ffffff@333333@333333 @€7@$@š™™™™™ @"@0@333333$@€0@š™™™™™ @ffffff@333333"@ffffff$@1@š™™™™™@ffffff@ÍÌÌÌÌÌ*@ÍÌÌÌÌÌ*@š™™™™™@š™™™™™1@ffffff @!@ÍÌÌÌÌÌ&@#@ffffff@ffffff@ÍÌÌÌÌÌ@@333333@@š™™™™™@ÍÌÌÌÌÌ.@ÍÌÌÌÌÌ%@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ*@,@333333@ffffff$@š™™™™™@ffffff@333333.@'@ffffffö?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ+@ffffff@š™™™™5@2@š™™™™™@#@š™™™™™@ÍÌÌÌÌÌ0@ffffff@333333@ffffff6@@š™™™™™ù?š™™™™™@ÍÌÌÌÌÌ$@333333@ÍÌÌÌÌÌ/@@š™™™™™ @333333@@333333)@ffffff@333333!@-@@333333@ffffff@š™™™™™@ÍÌÌÌÌÌ)@@š™™™™™@ffffff@ffffff#@@#@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ(@š™™™™™@@ÍÌÌÌÌÌ@ffffff/@@š™™™™™$@333333*@ @"@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌ@š™™™™™#@33333³:@ÍÌÌÌÌÌ@ffffff1@333333.@ÍÌÌÌÌÌ @š™™™™™@333333@ffffff @333333@@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@333333@ÍÌÌÌÌÌ @ffffff@"@333333@!@ÍÌÌÌÌÌ,@ffffff@@ @%@!@š™™™™™@333333@333333@@ÍÌÌÌÌÌ@ffffff@333333@333333@ffffff @ffffff!@ÍÌÌÌÌÌô?ffffff(@ÍÌÌÌÌL2@š™™™™™!@33333³4@$ÀÍÌÌÌÌÌÀffffffÀš™™™™™Àffffff$À333333HÀ&À"Àš™™™™™$À333333Àš™™™™™2À333333"À333333Àš™™™™™ ÀÀffffff Àš™™™™™'ÀffffffÀÍÌÌÌÌL1Àš™™™™™À333333À33333³6ÀÀffffff$À333333 À33333³6À-ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀffffffÀffffffÀffffffÀ333333$ÀÍÌÌÌÌÌÀ333333$ÀffffffÀš™™™™™.Àš™™™™™ÀÍÌÌÌÌÌ ÀÀffffffÀÍÌÌÌÌÌ&À333333Àffffffö¿333333Àš™™™™™!À333333"À333333ÀffffffÀ333333*À%Àffffff*Àffffff$ÀÍÌÌÌÌÌ&ÀÍÌÌÌÌÌÀffffff!À+Àš™™™™™,À$À)ÀÀ333333ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ ÀffffffÀš™™™™™%ÀÍÌÌÌÌÌÀffffffÀ333333&ÀÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌÀÀffffffÀÍÌÌÌÌÌ)Àš™™™™™ À333333,À333333)Àffffff"À333333%À'Àš™™™™™#À333333ÀÍÌÌÌÌŒGÀš™™™™3À€0Àš™™™™™>Àffffff%À333333#ÀffffffÀ333333)ÀÀÍÌÌÌÌÌ&À333333ÀÍÌÌÌÌÌÀ333333Àffffff#Àš™™™™™$À333333!Àš™™™™;Àffffff-ÀÀÍÌÌÌÌL3ÀÀ33333³DÀffffff+ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀš™™™™™$Àš™™™™™6Àffffff2ÀffffffÀffffff=ÀffffffÀffffffÀ!À333333 Àš™™™™™(À333333'À333333À333333ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀÍÌÌÌÌÌü¿333333ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ+ÀffffffÀffffffÀš™™™™™,Àffffff%À-ÀÍÌÌÌÌÌ(ÀÍÌÌÌÌL2À333333À.ÀÍÌÌÌÌÌ À+À333333*ÀÀ333333&À*ÀÍÌÌÌÌÌ-À333333Àffffff À333333Àš™™™™™ÀÍÌÌÌÌÌ*Àš™™™™™$Àš™™™™™ÀÍÌÌÌÌÌÀffffff%ÀffffffÀfffffæ4ÀffffffÀÍÌÌÌÌÌ!À Àš™™™™™$ÀÍÌÌÌÌÌ4ÀffffffÀ333333Àš™™™™™Àffffff&Àš™™™™™!Àš™™™™™*ÀÍÌÌÌÌL5Àš™™™™™/À33333³0ÀÍÌÌÌÌL1À333333"Àš™™™™™/ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333Àffffff À333333Àš™™™™™"À333333&À$Àš™™™™™.Àffffff*ÀÀ#Àffffff/À333333(À$ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌ-ÀÀ333333Àš™™™™™ÀÀÀffffffÀš™™™™DÀ0ÀÍÌÌÌÌÌ À333333Àš™™™™™,ÀffffffÀffffffþ¿š™™™™™*À3333339À333333!À333333Àš™™™™™DÀš™™™™™5ÀÍÌÌÌÌÌ1Àffffff3À À333333À(ÀÍÌÌÌÌÌ.À333333À'À*À333333#Àffffff ÀÍÌÌÌÌÌ#Àš™™™™™!ÀÀ333333ÀffffffÀÍÌÌÌÌÌÀ/Àffffff!À.ÀÍÌÌÌÌÌ'À33333³4À333333À333333Àš™™™™™*À ÀÍÌÌÌÌÌ$ÀÍÌÌÌÌÌ$ÀÍÌÌÌÌÌ Àš™™™™™/ÀÍÌÌÌÌÌ'Àš™™™™™ ÀÍÌÌÌÌÌ#ÀffffffÀš™™™™™-Àš™™™™™Àffffff)ÀÍÌÌÌÌÌ/À333333,ÀÍÌÌÌÌÌÀ€1Àffffff À(Àš™™™™™ÀffffffÀÍÌÌÌÌÌ(Àš™™™™™Àš™™™™™Àš™™™™™"Àš™™™™™ÀÍÌÌÌÌÌ"ÀffffffÀš™™™™™ù¿333333&Àš™™™™™À$À333333!À333333 ÀÍÌÌÌÌÌ&ÀÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌ'À#ÀffffffÀš™™™™™À333333ÀÀ$Àš™™™™™ À Àffffff$ÀffffffÀ!ÀÍÌÌÌÌÌì¿ffffffÀ333333*Àffffff*Àš™™™™™Ù¿š™™™™™ÀÍÌÌÌÌÌ2À€4ÀÍÌÌÌÌL0Àš™™™™™ÀÍÌÌÌÌL1Àš™™™™™7Àš™™™™™ÀffffffÀ,Àš™™™™™À3333334ÀÍÌÌÌÌL3À333333ÀÍÌÌÌÌÌ!ÀÍÌÌÌÌÌÀ333333"Àffffff+À&Àš™™™™™1Àffffff5À33333³1Àš™™™™™&Àffffff6ÀÍÌÌÌÌÌ#Àš™™™™™ÀÍÌÌÌÌÌ"À333333 Àffffff$À333333À333333)À33333³3ÀÍÌÌÌÌL0Àš™™™™™À333333ÀÍÌÌÌÌL3Àš™™™™™ÀÀš™™™™™&ÀÍÌÌÌÌÌ%À333333(Àffffff(ÀÀ333333Àš™™™™™$ÀÍÌÌÌÌÌ#À,À333333"À333333$ÀffffffÀÀš™™™™5À(À33333³:À5Àš™™™™™"ÀÍÌÌÌÌÌ7ÀÍÌÌÌÌÌ-À333333 Àffffff,ÀffffffÀš™™™™™ÀffffffÀ/ÀÍÌÌÌÌÌ%À€1À33333³2À$ÀffffffÀ$ÀÀš™™™™™#À333333!Àffffff*ÀffffffÀÍÌÌÌÌÌÀffffffæ¿333333-ÀÍÌÌÌÌÌ$ÀÍÌÌÌÌÌ À7À333333À3À333333À333333/Àš™™™™™ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌL7ÀffffffÀÍÌÌÌÌÌ.Àffffff4Àffffff@Àš™™™™™ À!Àš™™™™™ ÀÍÌÌÌÌÌ$À€7À$Àš™™™™™,Àffffffþ¿333333ÀÀÍÌÌÌÌÌÀffffff+Àffffff&Àš™™™™™0Àš™™™™™!ÀÍÌÌÌÌÌ4À€4ÀffffffÀš™™™™™!À(Àš™™™™™?À!À333333&À À333333%ÀffffffÀ&Àfffff&GÀffffffÀ333333À33333³2À333333 Àffffff Àš™™™™™!À333333"À#À*ÀÀÀ*ÀffffffÀš™™™™™#Àffffff$ÀffffffÀÍÌÌÌÌÌÀš™™™™™ÀÍÌÌÌÌÌÀš™™™™™'Àš™™™™™Àffffff"Àš™™™™™ÀÍÌÌÌÌ AÀÍÌÌÌÌÌÀ333333À@š™™™™™ñ?ÍÌÌÌÌÌì?à?ffffffö?@à¿ffffffÀ333333ã?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™¹?𿚙™™™™ù?à¿333333ã?333333Ó?š™™™™™ñ?š™™™™™É¿à?à?à?š™™™™™Ù?333333ã?š™™™™™Ù?à?š™™™™™É?š™™™™™¹?࿚™™™™™ñ?333333ã?ffffffö?ffffffö?333333ã?š™™™™™Ù¿š™™™™™É?š™™™™™Ù?333333Ó?š™™™™™é?š™™™™™Ù¿š™™™™™é?š™™™™™É?333333Ó?š™™™™™é¿à?ffffff濚™™™™™¹?ÍÌÌÌÌÌì?š™™™™™Ù¿ffffffæ?š™™™™™É?333333Ó?š™™™™™Ù?333333Ó?š™™™™™¹¿ffffffæ¿ffffffæ?š™™™™™é¿š™™™™™É?333333Ó?333333ó?ffffffæ?š™™™™™¹?š™™™™™¹?š™™™™™é?ÍÌÌÌÌÌì?333333ã?ffffffæ?333333Ó¿š™™™™™¹?ffffffæ?ÍÌÌÌÌÌì?ffffffæ?à?à?ÍÌÌÌÌÌ쿚™™™™™Ù?ffffffæ?à¿333333Ó¿ÍÌÌÌÌÌÀ333333Ó¿š™™™™™¹?ø¿š™™™™™É?š™™™™™¹¿š™™™™™Ù¿š™™™™™É?š™™™™™Ù¿š™™™™™Ù?333333Ó¿š™™™™™é?333333Ó?à¿333333ã?à?333333ã?š™™™™™ñ?333333Ó?333333ã?à?š™™™™™é?333333Ó¿š™™™™™É¿ffffffæ?š™™™™™É?š™™™™™Ù?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?à?š™™™™™É?š™™™™™É?333333Ó?š™™™™™É?333333Ó?š™™™™™é?š™™™™™É?š™™™™™Ù¿ffffffæ?š™™™™™Ù?à?š™™™™™ñ?ffffff濚™™™™™¹?ffffffæ¿ÍÌÌÌÌÌô?š™™™™™É¿333333Ó?š™™™™™¹?à¿@333333ã?333333ã?ð?ð?à?ffffffæ?333333ã?ð¿à?333333Ó?333333Ó?333333ã?š™™™™™É¿š™™™™™Ù?š™™™™™Ù¿333333Ó?à?ffffffæ?ffffffæ?š™™™™™É?333333ã?š™™™™™Ù?à?š™™™™™Ù¿à?š™™™™™Ù?š™™™™™é?333333ã?333333ã¿333333Ó?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™é?࿚™™™™™Ù?ð?333333ã?š™™™™™Ù?š™™™™™é?333333Ó¿333333ã?333333Ó?333333Ó?š™™™™™É¿333333Ó?š™™™™™É?š™™™™™Ù?@ÍÌÌÌÌÌì¿333333Ó?ÍÌÌÌÌÌì?333333Ó?š™™™™™É?ÍÌÌÌÌÌì?333333Ó¿à?š™™™™™Ù?ffffffö?333333Ó?š™™™™™ñ?333333Ó¿333333ã¿à?333333Ó¿š™™™™™¹?à?ffffffæ?š™™™™™Ù?333333Ó¿š™™™™™é?š™™™™™É?š™™™™™é?333333ã?š™™™™™ñ?à?š™™™™™É?333333Ó¿š™™™™™É?š™™™™™¹?à?333333ã¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™É?ð?š™™™™™Ù¿333333Ó?š™™™™™é¿š™™™™™Ù?š™™™™™É¿š™™™™™É¿333333Ó¿à?ffffffæ?š™™™™™Ù¿š™™™™™Ù?333333ã?333333Ó?š™™™™™é?333333Ó?š™™™™™É?ÍÌÌÌÌÌì?š™™™™™¹?ð¿333333Ó?š™™™™™Ù¿ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?333333㿚™™™™™ñ?ffffffæ?333333Ó?š™™™™™¹¿š™™™™™É¿š™™™™™Ù?š™™™™™Ù?333333ã?à?š™™™™™¹¿š™™™™™É?š™™™™™Ù¿333333ã¿333333ã?333333Ó?š™™™™™¹?à?š™™™™™¹¿333333Ó?333333Ó?333333Ó?š™™™™™¹?333333Ó?333333㿚™™™™™é?š™™™™™É?š™™™™™Ù?ð?š™™™™™é?333333㿚™™™™™é?š™™™™™Ù?ffffffæ¿ffffffæ?š™™™™™Ù?š™™™™™Ù?š™™™™™¹?š™™™™™Ù?ffffffæ?à?š™™™™™Ù¿š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™É¿333333Ó¿š™™™™™Ù?333333㿚™™™™™é?š™™™™™Ù?333333ã?š™™™™™é?š™™™™™¹?à¿ffffffæ?š™™™™™¹¿à?š™™™™™É?š™™™™™Ù?333333ӿ࿚™™™™™é?333333Ó¿à¿333333ã?333333Ó?à?š™™™™™é?333333Ó?š™™™™™¹?š™™™™™É?333333Ó¿š™™™™™É?š™™™™™É?333333Ó¿333333Ó?333333Ó?à?š™™™™™É¿333333ã?à?š™™™™™É¿š™™™™™é¿à?ð?333333ã?š™™™™™é?š™™™™™ñ?ffffffæ¿à?ffffffæ?333333Ó?333333㿚™™™™™Ù?š™™™™™ñ¿333333Ó¿š™™™™™Ù?š™™™™™Ù?333333ã?ffffffæ?š™™™™™É¿333333ã?š™™™™™Ù¿š™™™™™Ù?š™™™™™É?ffffffæ¿à?333333û¿š™™™™™Ù?à¿333333Ó?333333Ó?š™™™™™Ù¿à?𿚙™™™™É?š™™™™™¹?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô¿à?š™™™™™É?à¿333333Ó?ð¿ffffffæ?à?ffffffæ?333333Ó?š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌì?333333ó?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù¿š™™™™™Ù?š™™™™™é?à?333333ã?š™™™™™é?333333ã?333333Ó¿333333ã?à?š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™É?333333ã?š™™™™™ñ?333333Ó¿š™™™™™Ù¿š™™™™™Ù¿ÍÌÌÌÌÌì¿à?à?333333Ó¿š™™™™™¹¿333333Ó?333333Ó?333333ã?333333ã?ð?š™™™™™Ù?š™™™™™é?š™™™™™¹¿333333Ó¿ffffffö?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™@ffffff@333333Ó?ffffff@ÍÌÌÌÌÌ@ffffff!@ffffff@ffffff@333333@@ÍÌÌÌÌÌÀš™™™™™Ù?ð?333333@ffffff @@š™™™™™@ÍÌÌÌÌÌô?š™™™™™@@ÍÌÌÌÌÌ@ffffff@@ÍÌÌÌÌÌô?333333@333333@ÍÌÌÌÌÌ @ffffff@333333Ó?š™™™™™ù¿333333@333333@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ@333333 @ffffff@333333!@333333@@ÍÌÌÌÌÌ @333333 @ffffff@ÍÌÌÌÌÌü?š™™™™™@š™™™™™@ÍÌÌÌÌÌ@ffffffþ?333333Ó?333333@333333ã¿ffffff @333333@ffffff@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@ @ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @ffffff@š™™™™™@@ffffffö?333333@333333@333333@333333@ffffff@š™™™™™@ÍÌÌÌÌÌ @š™™™™™@ÍÌÌÌÌÌ@š™™™™™@333333Ó?333333@š™™™™™ @@@š™™™™™@333333@333333@ÍÌÌÌÌÌ2@333333@@333333-@ffffff@š™™™™™é?@š™™™™™@š™™™™™@š™™™™™!@333333@ffffff@333333@ÍÌÌÌÌÌì?š™™™™™ñ?333333û?š™™™™™@ð?š™™™™™@@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@ffffff&@ð?ÍÌÌÌÌÌ@333333@ffffff @ffffff@ffffff@ffffff@@ @333333 @ffffff@333333@@ffffff@333333@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @@š™™™™™ @ÍÌÌÌÌÌ@š™™™™™@333333@ÍÌÌÌÌÌ@333333@ffffff@@ÍÌÌÌÌÌ@š™™™™™é?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@333333@333333ó?@333333 @ffffff@333333@#@š™™™™™@333333û?333333ã?ø?ÍÌÌÌÌÌ#@š™™™™™@ffffff @333333 @ÍÌÌÌÌÌ@š™™™™™ñ?ÍÌÌÌÌÌô?ffffff@333333@@333333@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌì?ffffff@@š™™™™™ @š™™™™™@š™™™™™@333333Ó?ÍÌÌÌÌÌ@333333$@ffffffþ?ÍÌÌÌÌÌ쿚™™™™™ñ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™¹?ffffff@ffffff@ð?ÍÌÌÌÌÌ@š™™™™™Ù?ø¿š™™™™™ñ?š™™™™™é?@ÍÌÌÌÌÌ@š™™™™™@š™™™™™ @š™™™™™@ÍÌÌÌÌÌì?ÍÌÌÌÌÌ@333333 @@333333@@333333@333333Ó?ffffff@š™™™™™é?ÍÌÌÌÌÌ@333333@@333333@ffffff!@ffffff@333333ã?333333@333333@ÍÌÌÌÌÌ!@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™ñ?@@š™™™™™é?š™™™™™@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @ffffff@@ð?š™™™™™é?@@š™™™™™@𿚙™™™™@À333333 @@333333 @ÍÌÌÌÌÌ@š™™™™™ñ?ÍÌÌÌÌÌ@ffffff @š™™™™™ñ?ffffff@333333@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ffffffþ?ffffff@333333ã?ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌ'@ÍÌÌÌÌÌü?š™™™™™ @ffffffö?ÍÌÌÌÌÌ@333333@š™™™™™@@ÍÌÌÌÌÌô?š™™™™™@š™™™™™ñ?@ffffff@š™™™™™@ÍÌÌÌÌÌ @ffffff@@ffffff@ffffffæ?@ð?@š™™™™™é¿ÍÌÌÌÌÌ @333333 @@"@ffffff@š™™™™™@ @ÍÌÌÌÌÌì?š™™™™™ @ÍÌÌÌÌÌô?ffffff@@š™™™™™ñ?&@ffffff"@333333@ffffffæ?333333 @ffffffÀÍÌÌÌÌÌÀffffff@#@ffffff@ffffffþ?333333 @ffffffþ?ffffffö?ffffff@ffffff@š™™™™™@ffffff@333333@333333 @ÍÌÌÌÌÌ @333333@ffffffæ¿333333@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌ@@@š™™™™™@š™™™™™@ffffffö¿ffffffþ?@@š™™™™™ù?ÍÌÌÌÌÌ@333333@š™™™™™É?ffffff@@ @ffffff@@ÍÌÌÌÌÌì?333333@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ!@@ø?ø?ÍÌÌÌÌÌ@ffffff@@@š™™™™™ù?ÍÌÌÌÌÌ@ffffff@ffffffæ?ffffffö?ÍÌÌÌÌÌ@ffffff!@@@ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ(@ð?ÍÌÌÌÌÌô?@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@@ffffff@ÍÌÌÌÌÌ@š™™™™™"@ÀÍÌÌÌÌÌ@333333@ø?333333ã?ffffff@"@š™™™™™É?ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?@š™™™™™'Àð?š™™™™™ @333333@ffffff@ÍÌÌÌÌÌ!@š™™™™™ @ffffff!@@ffffff@ÍÌÌÌÌÌô?333333û?%@à?333333%@ÍÌÌÌÌÌ@333333'@333333@ffffff @ÍÌÌÌÌÌ@š™™™™™ñ?@ffffff @ÍÌÌÌÌÌ@à?ÍÌÌÌÌÌ@@333333@333333@š™™™™™¹?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@š™™™™™@ÍÌÌÌÌÌ@ffffffö?š™™™™™!@š™™™™™ù¿ð?333333@ÍÌÌÌÌÌ@ffffff%@ffffff@š™™™™™@333333@ÍÌÌÌÌÌì?ffffff@š™™™™™À@ÍÌÌÌÌÌ@š™™™™™é?š™™™™™@š™™™™™@ffffff@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@š™™™™™@š™™™™™ñ?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™@š™™™™™%@ffffff À333333@333333@333333ÀÍÌÌÌÌÌÀffffff4@ffffff@À \ÀÍÌÌÌÌÌ3Àš™™™™0ÀÍÌÌÌÌL3ÀÍÌÌÌÌÌ!Àš™™™™YAÀš™™™™™!Àø¿7@3@š™™™™™%@=À333333À33333sAÀš™™™™Ù@@@33333³5ÀÍÌÌÌÌÌ!@š™™™™™%Àš™™™™™ÀffffffHÀ33333³9Àffffff&@333333À333333 ÀÍÌÌÌÌÌ%À333333À333333ÀffffffÀš™™™™™À333333À%ÀÍÌÌÌÌÌ+@ffffff@š™™™™™0@ÍÌÌÌÌÌ@ffffff3À6@ÍÌÌÌÌÌ,@à¿333333ó¿333333Àš™™™™™ @ÍÌÌÌÌÌÀš™™™™™;ÀÍÌÌÌÌÌ@ffffff"À3333332@fffffæ?@333333ã?ø?3333330À33333³6ÀÍÌÌÌÌÌ*À*À333333 @ÍÌÌÌÌÌ!@š™™™™™ @@ffffff@ffffffÀ333333.Àš™™™™C@333333ó?š™™™™™ À333333%@ÍÌÌÌÌÌ@š™™™™™)ÀÀš™™™™™É¿€=@ÍÌÌÌÌLVÀ333333À€@À333333&Àffffffæ¿33333³0@fffffæ;Àffffff1@fffffæ0ÀÍÌÌÌÌLnÀÍÌÌÌÌÌ0@333333&À@eÀfffffæCÀÍÌÌÌÌL8ÀÍÌÌÌÌÌü?4À333333@5À€=@333333@ÍÌÌÌÌÌ@33333³=ÀÍÌÌÌÌÌ)@3333335@ÍÌÌÌÌL>@ÍÌÌÌÌlRÀ?Àfffff&@@/ÀÍÌÌÌÌLWÀffffffö¿ÍÌÌÌÌ NÀ€D@@š™™™™™@333333ÀÍÌÌÌÌŒDÀ33333sAÀ333333"@fffff&NÀÍÌÌÌÌL1@ffffff'ÀÍÌÌÌÌÌ @š™™™™™7@3Àffffff2Àffffff#À@ffffff;@ð?5@ffffff&Àffffff+@3333333Àš™™™™™@@ÍÌÌÌÌÌ"ÀÀÍÌÌÌÌL;À333333#Àš™™™™™'ÀÍÌÌÌÌÌÀš™™™™™&À+@ÍÌÌÌÌL3@333333$ÀÍÌÌÌÌÌ$@ÍÌÌÌÌÌ-Àš™™™™™/Àš™™™™™1Àš™™™™™$@ÍÌÌÌÌÌô¿š™™™™™ @ffffffÀ333333(Àš™™™™™Àffffff*À)@&Àš™™™™™À333333&Àffffff @333333+Àffffff&@ÍÌÌÌÌÌÀ33333³3Àš™™™™™/@š™™™™™7@333333ÀÍÌÌÌÌÌ/À"À333333$ÀÍÌÌÌÌL:ÀAÀÍÌÌÌÌL4À€>À333333F@ÍÌÌÌÌÌ>Àffffffö¿333333ó?ffffff@ÍÌÌÌÌÌ,@333333û?ffffffþ¿-@ÍÌÌÌÌÌ ÀÍÌÌÌÌL7À8À33333³G@3333334ÀÍÌÌÌÌLCÀš™™™™YAÀÀIÀš™™™™7À€9À33333sK@333333&ÀÍÌÌÌÌÌ@333333ã?-À€:@fffffæGÀÍÌÌÌÌÌì?š™™™™™Ù¿š™™™™™+@ffffff.À@33333CaÀffffff6@š™™™™™*ÀÍÌÌÌÌL]À333333 À@š™™™™BÀ333333?ÀÍÌÌÌÌÌ3Àfffff&FÀ333333@333333@fffffæ3À33333³5Àš™™™™YTÀ1À333333@ffffff@ffffffÀ0@š™™™™™Àš™™™™™:Àfffffæ3À'Àš™™™™™¹¿ÍÌÌÌÌL?@fffffæ2@€P@333333@š™™™™™É?F@333333+À33333³0Àš™™™™™&À333333%@33333³@@fffffæ2À333333%À@ÍÌÌÌÌÌ3Àffffff À33333³5À@À/@33333³0À0@7Àš™™™™™"@ffffffDÀš™™™™™RÀfffffæ1Àš™™™™™ÀffffffÀfffff\ÀÍÌÌÌÌÌì?33333ó@Àš™™™™™ù¿ffffffÀÍÌÌÌÌÌ@$À333333Àš™™™™™ ÀÍÌÌÌÌÌ,À333333ÀÍÌÌÌÌL0@$@š™™™™™,@fffffæ8@š™™™™™(Àš™™™™™ÀffffffÀffffff%ÀC@ffffff.Àffffffö¿š™™™™™¹¿Àffffff=@333333&@ffffffþ¿š™™™™™@š™™™™6ÀÍÌÌÌÌÌü?fffffæ3ÀÍÌÌÌÌÌÀ333333/À@ÍÌÌÌÌÌ1@š™™™™™ù¿€O@ð?333333!@š™™™™ÙE@ÍÌÌÌ̬d@€2@ÍÌÌÌÌL7@33333³2À€:Àffffffö¿33333óHÀ6ÀÍÌÌÌÌŒAÀÍÌÌÌÌÌJÀ333333,@š™™™™™@ÍÌÌÌÌÌ:Àš™™™™™$À33333ó@À€BÀffffff"Àffffff-Àffffff @š™™™™™É?š™™™™™ Àš™™™™™<@33333sGÀÍÌÌÌÌÌ?ÀÍÌÌÌÌÌ.À333333Àš™™™™™3Àš™™™™™ñ?š™™™™™@ffffff,@ÍÌÌÌÌÌSÀ33333³<@ffffffÀš™™™™™2@ffffff(Àš™™™™=À333333À€1@š™™™™™Ù?ÍÌÌÌÌÌ7Àš™™™™™@š™™™™™@€5À+À€=À@3333334@š™™™™™ @ À.@ffffff&@ffffff3ÀÍÌÌÌÌÌü¿š™™™™™!@š™™™™™ñ?ÍÌÌÌÌÌ9À*@š™™™™¹PÀfffff¦IÀEÀffffff/@KÀ333333û?ffffff9@ÍÌÌÌ̬SÀ€>Àš™™™™™@ffffff@fffff¦F@š™™™™@Àffffff @ffffff)@ÍÌÌÌÌŒFÀfffffæDÀÍÌÌÌÌÌ"À333333@ÍÌÌÌÌÌ@š™™™™™;@333333!ÀÍÌÌÌÌ AÀà¿ÍÌÌÌÌÌ@fffff&A@ÍÌÌÌÌÌ6ÀÍÌÌÌÌÌRÀø?fffffæ4@€HÀffffffÀÍÌÌÌÌŒCÀš™™™™™É?=À333333ÀÀ333333IÀÍÌÌÌÌÌ3@š™™™™7ÀÍÌÌÌÌÌ?ÀfffffeÀ€0@š™™™™™ÀÍÌÌÌÌŒCÀ!Àš™™™™™(À3333331ÀÀÍÌÌÌÌÌ9À@@@Àš™™™™™3@fffff&D@š™™™™<ÀÍÌÌÌÌÌÀš™™™™Ù@@ÍÌÌÌÌÌÀ333333 À333333ó?š™™™™™!À333333û¿$Àš™™™™¹VÀš™™™™™À3333335À ÀÍÌÌÌÌÌ'ÀÍÌÌÌÌÌ@š™™™™™-ÀÍÌÌÌÌgÀà?ffffffÀ33333sLÀ33333³3Àš™™™™™8@ÍÌÌÌÌL0À333333Àffffff$ÀÍÌÌÌÌ @À€3À€1@ÍÌÌÌÌÌ2@š™™™™™<Àš™™™™™Ù¿"ÀÍÌÌÌÌÌ%Àfffffæ2@%@š™™™™™ÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ;Àffffff:@š™™™™6À33333³;@ÍÌÌÌÌìRÀ333333@fffff&C@š™™™™YJ@33333³;@ÍÌÌÌÌL7@€I@€E@333333RÀ3333335@š™™™™5@š™™™™™E@fffffæ;@33333SÀÀffffff@fffff¦O@ÍÌÌÌÌLJ@š™™™™ÙZ@333333%@š™™™™™&@333333I@33333ÓP@àP@fffff¦H@ÀC@333333 @š™™™™=@7@33333óM@33333³F@ÍÌÌÌÌÌì¿€@ÀÍÌÌÌÌŒM@€8@š™™™™ÙH@ÍÌÌÌÌÌB@š™™™™4@ÍÌÌÌÌL8@ÍÌÌÌÌÌ4@@W@ÀD@?@ÍÌÌÌÌÌA@)@33333óJ@fffffæ9@fffff¦B@/@33333sF@ffffff2@ffffffÀÍÌÌÌÌL<@ÍÌÌÌÌÌô¿33333³;@@Q@fffffæO@š™™™™<@ÍÌÌÌÌÌN@ÀH@333333?@3333334@2@33333³N@33333³O@ÍÌÌÌÌ B@33333óI@š™™™™P@ÍÌÌÌÌÌ)@333333 @ R@fffffæ8@ÍÌÌÌÌÌ2@fffffæI@fffffQ@ÍÌÌÌÌÌA@333333C@@P@š™™™™ÙY@33333³3Àffffffæ¿Àš™™™™™=@L@`T@ffffffæ?K@š™™™™™/@š™™™™™"À33333óB@š™™™™9R@ffffff?@š™™™™™7@ffffff/Àš™™™™YC@33333ó@@5@33333sV@ÀR@ffffff@@ÀC@33333³3Àfffffæ5@fffff¦E@ffffffO@š™™™™9PÀfffffæ@@fffff¦M@'@,Àš™™™™YK@ÍÌÌÌÌ W@fffffæH@33333óE@33333sG@€8@š™™™™™$Àš™™™™ÙB@ffffffO@ÍÌÌÌÌL6ÀÀH@fffffæ4@C@@N@@fffffæE@ÍÌÌÌÌÌ7@š™™™™=@fffffÆS@fffffæ?@33333óL@š™™™™4@@O@ÍÌÌÌÌÌ?@:@š™™™™YH@ffffffK@š™™™™;@ÍÌÌÌÌŒB@ÍÌÌÌÌ L@333333 Àš™™™™™"@š™™™™YD@333333R@ÍÌÌÌÌL=@A@ÍÌÌÌÌÌD@333333I@fffff&N@@U@ÍÌÌÌÌÌI@33333³2@@š™™™™™&@fffff&\@ffffffE@š™™™™3@@G@E@333333@š™™™™™ @ÀE@ÍÌÌÌÌÌ!@333333G@š™™™™4@€A@fffff&T@š™™™™A@6@D@ Y@ÍÌÌÌÌÌ&@ÍÌÌÌÌ E@333333?Àš™™™™I@š™™™™P@ffffffM@33333sBÀš™™™™™@ÍÌÌÌÌLD@A@š™™™™™U@333333@ffffff=@š™™™™¹R@ð¿ÍÌÌÌÌÌ"@€3ÀÍÌÌÌÌÌ?@ffffffÀ33333³8ÀÍÌÌÌÌL6@fffff¦A@ÍÌÌÌÌL1@8@š™™™™YQ@ÍÌÌÌÌÌ ÀÀN@š™™™™™@@ffffff?@33333sP@33333³<@ffffffI@ffffff @333333C@ffffff!ÀÍÌÌÌÌ F@ffffffMÀš™™™™™P@fffffæ1@.@33333óJ@š™™™™™&@š™™™™Ù@@š™™™™™2@fffff†S@š™™™™™;@š™™™™yQ@333333;@š™™™™™ Àš™™™™™(À333333KÀÍÌÌÌÌÌ"@š™™™™™@ÍÌÌÌÌ K@š™™™™M@š™™™™™P@ÍÌÌÌÌŒA@š™™™™™É¿333333L@š™™™™YB@ÍÌÌÌÌŒT@š™™™™9V@€=@R@33333sH@š™™™™™L@À`@8ÀfffffæK@ÍÌÌÌÌ JÀfffffæD@33333“Q@333333.@€7@333333+@ð?9@ffffff%Àffffff1@ÍÌÌÌÌÌM@ÍÌÌÌÌÌ@ÍÌÌÌ̬Q@"À33333T@À@ÀffffffFÀA@33333S]@ffffff"@ÍÌÌÌÌÌQÀ0@š™™™™™Àš™™™™™1@€N@33333³5@š™™™™™¹?ffffffH@ffffff@ffffff@fffff&B@ÀC@š™™™™D@ÍÌÌÌÌ P@ W@š™™™™<@@+@À33333³V@€2À333333>@š™™™™9T@33333sG@€\@ÍÌÌÌÌŒL@fffffæ6@333333@@0À333333?@333333û¿3333338@@š™™™™™/@fffff¦\@€W@š™™™™YY@ÍÌÌÌÌÌ!@š™™™™™E@š™™™™™:Àš™™™™ a@fffffÆQ@°`@fffff&E@333333Àfffffæ?@ÍÌÌÌÌL?ÀÍÌÌÌÌÌ@9@ÍÌÌÌÌÌ:Àš™™™™™K@€I@ÍÌÌÌÌ C@fffffFS@ @š™™™™™+@ÍÌÌÌÌÌ/Àffffff>@>@š™™™™3@fffffFR@fffff¦W@€>Àffffff#À33333³I@€8ÀÍÌÌÌÌÌ@ffffffS@š™™™™7@š™™™™YH@š™™™™6ÀÍÌÌÌÌLN@À333333O@fffffæ@@ Q@š™™™™yQ@fffff†U@š™™™™™)@š™™™™™(@33333³L@`R@ÍÌÌÌÌLF@àR@Àš™™™™5@€=@33333sA@š™™™™YJ@fffffÆQ@33333ÓP@ÍÌÌÌÌÌ!ÀÀC@š™™™™™E@$@š™™™™™!ÀÍÌÌÌÌÌC@€>@ÍÌÌÌÌL8À33333³E@ÍÌÌÌÌÌ!@š™™™™¹R@š™™™™™&@š™™™™Ù@@€1À/@3333337@ÀD@àZ@ÍÌÌÌÌÌÀ33333sY@ffffffÀÍÌÌÌÌÌ,@fffffæC@333333@-@€H@š™™™™¹`@ffffffÀ33333³3Àffffff,@@@ÍÌÌÌÌLIÀ333333)Àš™™™™ÙGÀš™™™™G@fffffæK@fffff¦F@ÍÌÌÌÌÌ@@ Q@333333;@š™™™™YS@š™™™™™@š™™™™™ @€O@3333337@ÍÌÌÌ̬\@ÍÌÌÌÌÌì?ÍÌÌÌÌ D@š™™™™9S@3@fffffæ1@333333ã¿fffffFR@9@fffff¦K@ÍÌÌÌÌL5À33333óT@333333*@š™™™™YQ@fffff†`@ffffff:À33333sE@fffffU@fffff†P@33333óL@A@333333>@33333sP@š™™™™YD@ÍÌÌÌÌŒJÀffffff@R@6À@š™™™™Y@@@ffffff8Àš™™™™™R@ÍÌÌÌÌŒG@ÍÌÌÌÌÌ7ÀÍÌÌÌÌÌ!À33333óE@ÍÌÌÌÌ VÀÍÌÌÌÌÌG@š™™™™™*@š™™™™8ÀàP@ÍÌÌÌÌLC@ÍÌÌÌÌ,P@ÍÌÌÌÌÌ(@333333G@€3@ÀI@33333óO@€8@€2@fffffÆX@333333)@33333SV@ffffff@š™™™™9a@fffffFbÀš™™™™™W@33333sN@333333Ó¿š™™™™™É¿ffffffæ?š™™™™™Ù?š™™™™™É¿@š™™™™™¹?š™™™™™¹?š™™™™™é?à¿ffffffö¿š™™™™™É¿š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™Ù¿š™™™™™¹?333333㿚™™™™™¹?š™™™™™¹?š™™™™™É?333333Ó¿š™™™™™¹¿š™™™™™¹?333333ó?š™™™™™¹?š™™™™™Ù¿ffffff濚™™™™™Ù?š™™™™™É?š™™™™™¹?࿚™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™¹?𿚙™™™™¹¿š™™™™™¹?š™™™™™ñ¿š™™™™™Ù¿š™™™™™¹¿333333ã¿à¿à¿š™™™™™¹?š™™™™™Ù¿333333Ó?š™™™™™¹?š™™™™™é¿š™™™™™Ù¿333333Ó¿š™™™™™Ù¿š™™™™™é¿š™™™™™É?333333ӿ𿚙™™™™É¿š™™™™™¹¿š™™™™™¹¿š™™™™™É¿š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿333333ã?333333㿚™™™™™¹?à¿ffffff@ÍÌÌÌÌÌ쿚™™™™™É¿333333Ó¿333333㿚™™™™™Ù¿333333Ó¿333333Ó¿333333ã¿ffffff#@333333Àš™™™™™ñ?@š™™™™™¹¿à?š™™™™™é?š™™™™™é¿š™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™¹?š™™™™™ñ¿š™™™™™É¿š™™™™™É?333333Ó?ffffffæ¿ffffff@š™™™™™Ù¿@ffffffæ¿333333Ó¿š™™™™™¹?š™™™™™É?š™™™™™ñ?333333Ó?333333ã?š™™™™™É¿à¿333333ã¿ffffff濚™™™™™¹?š™™™™™é?333333ã?333333Ó¿š™™™™™¹?š™™™™™¹?333333ã?333333ó?š™™™™™ñ¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿š™™™™™Ù¿333333㿚™™™™™É¿à?š™™™™™É?ÍÌÌÌÌÌü?š™™™™™¹¿š™™™™™É?š™™™™™Ù?š™™™™™Ù?333333Ó¿š™™™™™É¿à¿š™™™™™É?à?š™™™™™¹?š™™™™™ñ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿š™™™™™¹?š™™™™™Ù¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹?333333Ó¿ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™É?ð¿ÍÌÌÌÌÌì?š™™™™™¹¿ffffffæ?ð¿ÍÌÌÌÌÌì?࿚™™™™™É¿š™™™™™Ù¿à¿š™™™™™Ù¿š™™™™™é¿š™™™™™¹?333333Ó¿š™™™™™¹?333333ã?à¿333333Ó¿š™™™™™Ù¿ð?š™™™™™É?333333ã?ÍÌÌÌÌÌô¿š™™™™™é¿š™™™™™É?333333Ó¿š™™™™™¹?š™™™™™ñ¿à¿à¿š™™™™™Ù¿333333㿚™™™™™Ù?ffffff@š™™™™™É¿š™™™™™¹?ffffffþ?ÍÌÌÌÌÌì¿333333ó¿š™™™™™¹¿ffffffþ?š™™™™™¹?š™™™™™É¿š™™™™™ñ¿š™™™™™¹?ffffff濚™™™™™¹¿333333 @ÍÌÌÌÌÌì¿ffffffæ¿à?š™™™™™¹?š™™™™™¹¿ffffff濚™™™™™Ù?333333ã?š™™™™™¹?ffffffö¿à¿333333㿚™™™™™É?ÍÌÌÌÌÌô¿š™™™™™¹?š™™™™™ù¿š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™ñ¿333333Ó¿333333Ó¿333333ã¿ffffffæ?333333㿚™™™™™¹?š™™™™™¹?š™™™™™é?š™™™™™¹¿333333ã?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™É?ffffffæ¿ffffffþ?333333ã¿à?333333Ó¿ffffffæ?š™™™™™É¿š™™™™™Ù¿à¿š™™™™™¹?à?࿚™™™™™É¿š™™™™™Ù?333333Ó¿ð¿333333ã¿à¿š™™™™™¹?à¿à?š™™™™™Ù¿š™™™™™¹?à?š™™™™™Ù?333333㿚™™™™™¹?š™™™™™Ù¿333333ӿ𿚙™™™™¹?333333ã?333333ó¿ffffffæ¿ð¿š™™™™™Ù¿333333Ó?à?ÍÌÌÌÌÌÀš™™™™™ñ¿333333ó?333333Ó?ffffffþ?à?š™™™™™Ù¿ffffffÀš™™™™™Ù?š™™™™™¹?š™™™™™Ù¿à¿š™™™™™¹¿š™™™™™É¿à¿333333ó?š™™™™™¹?š™™™™™Ù¿š™™™™™¹?333333Ó?ffffffæ?ffffffþ¿ÍÌÌÌÌÌü¿ffffffæ?à¿ÍÌÌÌÌÌ쿚™™™™™Ù?333333ó¿333333ó¿ffffffæ¿ffffffæ?š™™™™™ñ?333333ã?à?š™™™™™é?ð¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™¹?š™™™™™É¿333333ó¿š™™™™™ñ?à?ÍÌÌÌÌÌì¿à?š™™™™™É¿š™™™™™¹?ffffffæ¿333333㿚™™™™™ñ¿ÍÌÌÌÌÌì¿ffffff@ð?333333û?à¿333333Ó?333333û¿ÀÍÌÌÌÌÌô?š™™™™™É¿à?š™™™™™ñ¿š™™™™™ù¿š™™™™™¹?à?š™™™™™Ù?ð¿ÍÌÌÌÌÌì¿333333Ó¿à¿333333ã?š™™™™™¹¿à¿š™™™™™ À333333ã?333333ã?š™™™™™¹?š™™™™™É¿š™™™™™Ù?š™™™™™Ù¿333333ã?333333ã¿ffffffö¿š™™™™™Ù¿š™™™™™¹¿ÍÌÌÌÌÌü?333333㿚™™™™™@333333ã?š™™™™™Ù?ÍÌÌÌÌÌ@333333Ó¿à?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™Ù?à¿333333Ó¿š™™™™™¹¿ffffffæ¿ffffffö¿ø¿ÍÌÌÌÌÌì?ffffffö?š™™™™™¹¿š™™™™™¹?š™™™™™é¿ffffffö?š™™™™™¹¿š™™™™™ñ?š™™™™™ñ¿ffffffæ¿333333Ó¿333333Ó¿ÍÌÌÌÌÌ@ffffffæ?ffffffæ?ø?333333Ó¿ffffffæ¿333333ã¿ð¿ÍÌÌÌÌÌü?333333Ó¿ffffffþ?š™™™™™É¿š™™™™™Ù¿à¿ffffffæ¿ffffff濚™™™™™¹?333333ã?š™™™™™Ù?š™™™™™¹?š™™™™™É?š™™™™™¹?š™™™™™Ù¿š™™™™™Ù?š™™™™™ñ¿333333û¿333333ã¿ø¿ffffffþ¿š™™™™™0À࿚™™™™™Ù¿š™™™™™ù¿ffffff濚™™™™™Ù¿š™™™™™é¿ÍÌÌÌÌÌì¿à¿ÀÍÌÌÌÌÌ쿚™™™™™ñ¿ð¿š™™™™™ñ¿š™™™™™é¿š™™™™™ñ¿ð¿ffffff4ÀÀ33333³0ÀÍÌÌÌÌÌÀffffff濚™™™™™Ù¿à¿à¿,À࿚™™™™™Ù¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿š™™™™™Ù¿Àš™™™™™Ù¿à¿š™™™™™Ù¿"Àš™™™™™Ù¿ÍÌÌÌÌÌô¿ffffffæ¿333333㿚™™™™™Ù¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ#À𿚙™™™™ù¿333333ã¿ffffff.@#@$@.@ffffff.@?@ÍÌÌÌÌÌ@ffffff@š™™™™™@@33333³0@ffffff@ffffff @š™™™™™4@ffffff)@333333#@ffffff@ÍÌÌÌÌÌ @&@ÍÌÌÌÌÌ2@'@€2@ÍÌÌÌÌÌ!@ÍÌÌÌÌÌ%@333333)@š™™™™™)@š™™™™™2@333333'@š™™™™™"@333333@š™™™™™@"@@š™™™™™-@ÍÌÌÌÌÌ"@ffffff$@š™™™™™(@ffffff,@@ffffff@&@ÍÌÌÌÌÌ@š™™™™™1@ffffff3@ÍÌÌÌÌÌ)@@%@ffffff"@ffffff@333333@ÍÌÌÌÌL0@ÍÌÌÌÌÌ%@fffffæ2@ffffff8@ÍÌÌÌÌÌ*@š™™™™™@333333&@333333%@3333336@ffffff@ffffff,@$@š™™™™™!@ÍÌÌÌÌÌ0@fffffæ1@@ÍÌÌÌÌÌ#@š™™™™1@š™™™™™$@333333#@ÍÌÌÌÌÌ#@ffffff+@333333@ffffff@333333)@ÍÌÌÌÌÌ:@ffffff@333333@3333332@ÍÌÌÌÌL1@3333333@ÍÌÌÌÌÌ @š™™™™™2@ÍÌÌÌÌÌ+@š™™™™™@333333B@*@ffffffö?š™™™™™@@ffffff#@š™™™™™@š™™™™™"@ffffff!@3333330@ÍÌÌÌÌÌ*@š™™™™™#@ffffff@ffffff.@ffffff-@fffffæ6@ffffff@ÍÌÌÌÌÌ>@ffffff)@š™™™™™3@š™™™™™@333333)@fffffæ2@ÍÌÌÌÌL2@ffffff@333333*@$@*@)@333333$@ÍÌÌÌÌÌ%@ffffff @ffffff@ÍÌÌÌÌÌ(@)@ffffff"@ÍÌÌÌÌÌ@š™™™™™(@ÍÌÌÌÌÌ@+@!@ÍÌÌÌÌÌ/@@ffffff @333333 @š™™™™™-@š™™™™™(@ÍÌÌÌÌÌ/@ffffff&@ÍÌÌÌÌL3@333333/@ÍÌÌÌÌÌ,@ffffff#@ÍÌÌÌÌÌ"@š™™™™™*@3333334@%@š™™™™™+@333333+@š™™™™™'@š™™™™™8@-@ffffff"@ÍÌÌÌÌÌ"@$@š™™™™2@333333#@ÍÌÌÌÌÌ(@ÍÌÌÌÌÌ%@333333)@333333(@š™™™™™.@š™™™™™+@š™™™™™@fffffæ3@ÍÌÌÌÌÌ!@ÍÌÌÌÌÌ7@š™™™™2@333333+@š™™™™™0@333333)@ffffff#@ÍÌÌÌÌÌ,@)@š™™™™™"@š™™™™™/@@>@ffffff#@@š™™™™™$@333333%@ffffff/@ÍÌÌÌÌÌ@š™™™™™/@š™™™™™0@š™™™™™!@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333'@š™™™™™@ffffff&@ffffff#@ffffff@ÍÌÌÌÌÌì?š™™™™™'@š™™™™™2@333333%@"@!@ÍÌÌÌÌÌ(@33333³=@ÍÌÌÌÌÌ6@33333³1@ffffff @ÍÌÌÌÌÌ@š™™™™™@333333!@ÍÌÌÌÌÌ+@š™™™™™,@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ*@€0@ÍÌÌÌÌÌ/@333333@ÍÌÌÌÌÌ"@ffffff)@ÍÌÌÌÌÌ'@š™™™™™ @ffffff*@ÍÌÌÌÌL5@@.@ffffff0@3333331@333333%@333333@ffffff+@333333@333333&@ÍÌÌÌÌL5@ffffff"@+@fffffæ4@33333³1@33333³2@ÍÌÌÌÌÌ.@ÍÌÌÌÌÌ'@š™™™™™&@ffffff-@š™™™™™,@š™™™™0@š™™™™™ @ffffff-@š™™™™™@333333(@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌ@š™™™™™(@ð?ffffff)@ffffff$@fffffæ0@ffffffæ?333333@ÍÌÌÌÌÌ@ffffff)@š™™™™™$@!@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌ@)@ffffff'@ÍÌÌÌÌÌ @@333333 @@š™™™™™@ffffff&@ÍÌÌÌÌÌ@š™™™™™'@333333.@ffffff*@ffffff@š™™™™™)@333333@$@333333!@š™™™™™@€3@ÍÌÌÌÌÌ@ffffff6@ÍÌÌÌÌÌ#@333333 @ÍÌÌÌÌÌ)@š™™™™™#@ÍÌÌÌÌÌ(@@ÍÌÌÌÌÌ%@@š™™™™™'@š™™™™7@333333=@ÍÌÌÌÌÌ.@'@3333332@ffffff,@@ÍÌÌÌÌL4@33333³0@333333;@333333 @333333.@ffffff#@ÍÌÌÌÌÌ2@š™™™™™ù?š™™™™0@ÍÌÌÌÌÌ-@ffffff!@ffffff*@333333)@fffffæ8@ÍÌÌÌÌÌ @š™™™™™ @fffffæ0@ffffff5@(@33333³6@š™™™™™ @š™™™™™ @š™™™™™+@ffffff4@€9@$@333333!@š™™™™™.@ffffff*@333333*@#@ffffff,@ÍÌÌÌÌL0@fffffæ3@ffffff5@fffffæ9@ÍÌÌÌÌÌ @#@ÍÌÌÌÌÌ&@0@ffffff@ÍÌÌÌÌÌ*@š™™™™™@š™™™™™)@š™™™™™.@333333-@'@ÍÌÌÌÌL8@!@333333@$@333333!@%@333333)@fffffæ6@à?ÍÌÌÌÌÌ@ffffff@1@333333@333333'@€4@ffffff@333333"@!@.@š™™™™™@33333³;@ÍÌÌÌÌÌ2@š™™™™™@ffffffþ?ÍÌÌÌÌÌ@333333*@ffffff@3333333@ÍÌÌÌÌÌ"@š™™™™™@333333@ffffff@ÍÌÌÌÌÌ+@333333@ÍÌÌÌÌÌ@š™™™™1@š™™™™™+@33333³2@ffffff$@333333(@š™™™™™"@333333%@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ"@š™™™™™'@333333+@ÍÌÌÌÌÌ*@š™™™™™ @ffffff$@333333@š™™™™™ @ÍÌÌÌÌÌ@ffffff"@ÍÌÌÌÌL;@@333333@š™™™™™2@'@333333,@š™™™™™7@@33333³2@5@5@-@ÍÌÌÌÌL0@333333(@(@3333331@333333'@š™™™™™@333333$@š™™™™™#@ÍÌÌÌÌÌ%@333333*@ffffff$@š™™™™™@ÍÌÌÌÌÌ @š™™™™™'@š™™™™™/@ffffff"@ÍÌÌÌÌÌ(@1@ffffff4@333333&@333333û?ÍÌÌÌÌÌ"@š™™™™™%@ffffff+@š™™™™™ @ffffff-@ÍÌÌÌÌÌ%@ffffff0@š™™™™™1@ÍÌÌÌÌÌ@333333@š™™™™™@ffffff@,@333333@€6@š™™™™™4@ÍÌÌÌÌÌ'@š™™™™™7@ÍÌÌÌÌÌ ÀÀÍÌÌÌÌÌÀÀ333333À33333³9Àš™™™™™ÀffffffÀ333333Àš™™™™™ Àš™™™™™*ÀffffffÀ333333ÀÍÌÌÌÌÌÀš™™™™™ñ¿333333À333333 À333333ÀÍÌÌÌÌÌ#ÀÀ333333Àš™™™™™1ÀÍÌÌÌÌÌ ÀÀš™™™™™À333333/ÀÍÌÌÌÌÌÀš™™™™™ Àš™™™™™Àš™™™™™ÀÍÌÌÌÌÌÀffffffÀš™™™™™ÀÀ333333Àffffff À333333&ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀÀš™™™™™ÀÍÌÌÌÌÌÀffffffÀÍÌÌÌÌÌü¿333333Àš™™™™™ÀffffffÀffffff À333333 Àš™™™™™"Àš™™™™™Àš™™™™™"Àš™™™™™#ÀÍÌÌÌÌÌì¿Àš™™™™™ñ¿ÍÌÌÌÌÌÀffffff!À333333+ÀÍÌÌÌÌÌÀ333333'ÀÍÌÌÌÌÌÀš™™™™™ÀffffffÀffffffÀÍÌÌÌÌÌ Àffffff#À333333û¿š™™™™™Àffffff&Àffffffþ¿ÍÌÌÌÌÌÀ333333À333333Àš™™™™™ ÀÍÌÌÌÌÌÀÀš™™™™™À333333%ÀÍÌÌÌÌÌÀffffffÀ333333"Àš™™™™™!ÀÀffffff<À333333!Àš™™™™™À(ÀÍÌÌÌÌÌÀ333333"ÀffffffÀffffff ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ Àš™™™™™À333333Àš™™™™™ÀÍÌÌÌÌÌ"ÀffffffÀš™™™™™Àffffff<Àš™™™™™Àffffffæ¿ffffff-ÀÍÌÌÌÌÌ!À333333 À33333sEÀ333333ÀffffffÀ333333À333333 À(ÀÍÌÌÌÌÌ!Àš™™™™™ À3333334ÀÀš™™™™™ À333333À333333 À333333*Àffffff)Àffffff$À333333 À ÀÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀ$Àffffff*ÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌ$Àš™™™™™ÀÍÌÌÌÌÌ ÀffffffÀ À Àš™™™™™#ÀÍÌÌÌÌÌ À333333'Àš™™™™™ ÀÀ#Àš™™™™™!Àffffff Àš™™™™™À333333ÀÍÌÌÌÌÌÀ À333333"Àš™™™™™!Àà¿ÍÌÌÌÌÌô¿š™™™™™Àš™™™™™ À'ÀffffffÀ Àš™™™™™Àš™™™™™'À ÀÍÌÌÌÌÌü¿š™™™™™Àffffff&ÀffffffÀš™™™™™%À333333)Àffffff#Àš™™™™™.À(À ÀÀÀÍÌÌÌÌÌÀffffffÀÀÍÌÌÌÌÌÀffffffÀÍÌÌÌÌÌÀ333333À333333ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌÀÀffffff$À"Àš™™™™™ÀffffffÀš™™™™™Àš™™™™™Àš™™™™™Àš™™™™™Àš™™™™™ Àš™™™™™À333333Àffffff)ÀÍÌÌÌÌÌ+ÀffffffÀ𿚙™™™™"ÀffffffÀffffffþ¿333333+ÀÍÌÌÌÌÌ&ÀffffffÀÍÌÌÌÌÌÀffffff/Àš™™™™3À+ÀÍÌÌÌÌÌÀffffffÀ333333ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ,ÀffffffÀffffff$À333333"À333333ÀffffffÀ333333ÀÍÌÌÌÌÌÀÀffffffÀ333333Àffffff À333333ÀÀš™™™™™&ÀÀÍÌÌÌÌÌÀffffffÀÀš™™™™™%ÀÀ333333À333333Àš™™™™™Àffffff-ÀffffffÀÀffffffÀÍÌÌÌÌÌ*À333333ÀÍÌÌÌÌÌ!À333333À&Àš™™™™™ÀÍÌÌÌÌÌ#À333333Àš™™™™™À333333û¿ÍÌÌÌÌÌ À333333ÀÀš™™™™™ÀffffffÀš™™™™™À333333Àš™™™™™ÀffffffÀffffff Àø¿ÍÌÌÌÌÌ!ÀffffffÀÍÌÌÌÌÌ À333333Àffffffþ¿Àø¿Àš™™™™™ À(ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌ"Àš™™™™™ ÀÀffffffÀÀffffffÀš™™™™™ ÀÀÍÌÌÌÌÌÀš™™™™™ÀÀ Àffffff$Àffffff ÀÍÌÌÌÌÌÀ+À333333&ÀÍÌÌÌÌÌ*À333333ÀÍÌÌÌÌÌ)ÀÍÌÌÌÌL2ÀffffffÀ333333ó¿š™™™™™À333333ÀffffffÀš™™™™0ÀÍÌÌÌÌÌ4ÀÀffffffÀš™™™™™À333333*ÀffffffÀffffffÀ333333+À333333'ÀÍÌÌÌÌÌ'Àffffff&Àš™™™™™2À333333Àffffffþ¿ÍÌÌÌÌÌ$Àš™™™™™À333333 Àš™™™™™ÀÀš™™™™™*Àffffff&Àffffff$Àš™™™™™ÀÍÌÌÌÌÌ À À333333ÀffffffÀ333333À333333Àš™™™™™$ÀÀÀš™™™™™À Àš™™™™™À"Àffffff)Àš™™™™™ÀffffffÀš™™™™™Àš™™™™™À3333336À333333$Àš™™™™™*Àš™™™™™Àffffff!Àš™™™™™À333333!À333333Àš™™™™™Àš™™™™™ÀÍÌÌÌÌÌ(Àš™™™™™#Àš™™™™™(Àš™™™™™!À ÀÍÌÌÌÌÌÀÀÍÌÌÌÌÌÀÀš™™™™™ÀÍÌÌÌÌÌ,À333333 ÀÍÌÌÌÌÌÀ)ÀÀÍÌÌÌÌÌÀffffffÀffffff'ÀÀ(Àš™™™™™ À333333#À333333Àš™™™™™À*Àš™™™™™ Àà¿3333331Àfffffæ6À333333 Àš™™™™™ÀÍÌÌÌÌÌ,ÀÀÀ+Àš™™™™™$À333333#À333333û¿ffffff Àffffffö¿Àš™™™™™+ÀÍÌÌÌÌÌ#ÀÍÌÌÌÌÌ!À Àš™™™™™"À3333332Àš™™™™™ù¿š™™™™™Àš™™™™™À3333336ÀÍÌÌÌÌÌÀš™™™™™ ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ"ÀffffffÀ333333$À€>ÀffffffÀÀÍÌÌÌÌÌ.Àš™™™™™ÀÀš™™™™™ÀÀš™™™™™,Àš™™™™™!À"ÀÍÌÌÌÌÌÀ333333À333333ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌ!ÀÀÀÀ À#ÀÍÌÌÌÌÌÀš™™™™™$À Àš™™™™YBÀÍÌÌÌÌÌÀ333333Àš™™™™™é?š™™™™™Ù?ffffffæ?à?333333@à?à?à?ð?ffffffö?š™™™™™Ù?333333ã?à?à?ÍÌÌÌÌÌì?ÍÌÌÌÌÌ쿚™™™™™Ù¿ÍÌÌÌÌÌì?à?à?š™™™™™¹?š™™™™™¹¿š™™™™™é?š™™™™™Ù?333333Ó?ÍÌÌÌÌÌì?š™™™™™ñ?ÍÌÌÌÌÌì?à?š™™™™™é?333333ã?333333Ó?ffffffæ?š™™™™™Ù?š™™™™™Ù?333333ã?à?š™™™™™Ù?333333ó?š™™™™™Ù?à?333333ã?333333ã?ð?333333Ó?š™™™™™É?š™™™™™É?ffffffæ?à?333333ã?ffffffæ?ð?ffffffæ?ffffffæ?š™™™™™ñ?ð?à?333333ã?š™™™™™É?š™™™™™ñ?333333Ó?333333ã?š™™™™™é?333333Ó?333333ã?š™™™™™Ù?š™™™™™é?333333ã?š™™™™™é?š™™™™™¹?à?š™™™™™Ù?333333ã?š™™™™™¹¿ffffffæ?333333ã?à?š™™™™™ñ?š™™™™™é?š™™™™™é?à?š™™™™™É¿à?à?ffffffæ¿ÍÌÌÌÌÌì?ð?š™™™™™é?š™™™™™Ù?à?ffffffæ?ffffffæ?à?š™™™™™é?à?ÍÌÌÌÌÌô?ÍÌÌÌÌÌì?à?ÍÌÌÌÌÌô?333333㿚™™™™™é?š™™™™™Ù?š™™™™™Ù?š™™™™™É?333333ã?à?à?333333Ó?ð?333333ã?333333ã?š™™™™™É¿ÍÌÌÌÌÌì?š™™™™™¹?333333Ó?ffffffæ?333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™¹¿à?š™™™™™ñ?333333ã?š™™™™™é?ffffffþ?333333Ó?333333ã¿ÍÌÌÌÌÌì?à?à?š™™™™™Ù?à?š™™™™™Ù?à?333333Ó?333333ã?à?š™™™™™Ù?ÍÌÌÌÌÌì?333333ã?333333Ó?ÍÌÌÌÌÌì?à?ffffffæ?š™™™™™é?ffffffæ?333333ã?333333ó?333333ã?š™™™™™¹¿š™™™™™Ù?ÍÌÌÌÌÌì?333333ó?333333ã?ÍÌÌÌÌÌô?333333ã?ffffffæ?ffffffö?333333ã?š™™™™™é?ffffffæ?ð?š™™™™™é?à?ÍÌÌÌÌÌì?ffffffæ?š™™™™™É?ffffffæ?333333ã?333333Ó?ffffffæ?333333ã?š™™™™™é?à?333333ã?š™™™™™Ù¿333333ã?ffffffö?333333ã?à?š™™™™™Ù?ð?ffffffæ?š™™™™™é?š™™™™™Ù?à?333333ã?š™™™™™Ù?ÍÌÌÌÌÌÀà?333333Ó?333333Ó¿š™™™™™é?š™™™™™É?ð?š™™™™™Ù?š™™™™™É?333333ã?à?š™™™™™é?ffffffæ?333333Ó?š™™™™™É?š™™™™™Ù?š™™™™™¹¿à?ffffffæ?333333ã?à?333333ã?š™™™™™Ù?ffffffæ?ffffff@333333ã?333333Ó?à?š™™™™™Ù?333333Ó?š™™™™™ñ?š™™™™™Ù?š™™™™™Ù?333333Ó?333333ã?ffffffæ?ð?333333ã?ð?ð?à?à?š™™™™™Ù?š™™™™™é?š™™™™™É?333333ã?333333Ó?š™™™™™Ù?ð?333333Ó?š™™™™™é?ffffffæ?ð?333333ã?š™™™™™Ù?333333ã?ÍÌÌÌÌÌì?333333ã?333333ã?ð?š™™™™™É?š™™™™™Ù?à?ffffffæ?ð?333333Ó?333333Ó?à?ffffffæ¿ÍÌÌÌÌÌì?à?333333ã?333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333ã?ð?à?š™™™™™Ù?333333ã?š™™™™™é?333333Ó?š™™™™™é?333333Ó?333333ã?ÍÌÌÌÌÌì?333333ã?333333Ó?à?ffffffæ?333333ã?ð?à?à?333333ã?333333Ó¿333333ã?ffffffæ?333333Ó¿ffffffæ?š™™™™™ñ?š™™™™™Ù?333333ã?š™™™™™¹¿333333ã?à?à?à?࿚™™™™™Ù?à?à?à?š™™™™™Ù?š™™™™™¹?ffffffæ?š™™™™™ñ?ð¿à?333333Ó?š™™™™™Ù?333333ó?333333ó¿ffffffæ?ffffffö?333333ã?333333ã?333333ó?333333ã?ffffffæ?333333Ó?à?š™™™™™Ù¿š™™™™™¹¿ð?ÍÌÌÌÌÌì?à?à?š™™™™™é?333333Ó?š™™™™™é?ÍÌÌÌÌÌô?ffffffæ?š™™™™™ñ?š™™™™™é?333333Ó?ffffffæ?š™™™™™é?333333ã?333333ã?ffffffæ?ÍÌÌÌÌÌì?š™™™™™¹?333333ã?ø?333333ã?ffffffæ?333333ã?333333ã?š™™™™™é?š™™™™™ñ?à?ffffffæ?š™™™™™Ù?à?à?à?333333ã?ffffffæ?à?333333ã?ÍÌÌÌÌÌì?333333ã?š™™™™™é?333333Ó?š™™™™™Ù?š™™™™™é?ð?š™™™™™Ù?š™™™™™Ù?333333ã?à?š™™™™™Ù?š™™™™™ñ?ÍÌÌÌÌÌì?à?333333ã?à?à?ffffffæ¿à?š™™™™™Ù?333333Ó?ffffffæ?333333Ó?à?ffffffæ?à?š™™™™™Ù?š™™™™™é?ffffffæ?š™™™™™é?š™™™™™¹?ffffffæ?à?333333ã?š™™™™™é?š™™™™™Ù?ffffffæ?333333Ó¿à?š™™™™™É?š™™™™™¹?333333ã?š™™™™™Ù?à?š™™™™™é¿333333ã?š™™™™™é?š™™™™™É¿333333ã?š™™™™™Ù?š™™™™™Ù?à¿333333ã?333333ã?333333㿚™™™™™é?š™™™™™¹¿ð?ø?333333Ó?ÍÌÌÌÌÌ쿚™™™™™Ù?ffffffæ?š™™™™™¹¿333333ó?ffffffæ?ffffffæ?š™™™™™ñ?ffffffæ?333333ã?š™™™™™Ù?š™™™™™¹?š™™™™™é?333333ã?ÍÌÌÌÌÌì?š™™™™™é?à?š™™™™™Ù?ffffffæ?š™™™™™Ù?333333ó?š™™™™™¹?à?š™™™™™É?à?à?š™™™™™é?333333ã?ð?š™™™™™é?ffffffæ?š™™™™™é?ffffffæ?ffffffæ?ð?š™™™™™é?ÍÌÌÌÌÌì¿333333ã?š™™™™™é?ffffffæ?ð?333333ã?à?ffffff@ÍÌÌÌÌÌ@š™™™™™@ffffff @š™™™™™@š™™™™™@ffffffþ?ffffffö?@ÍÌÌÌÌÌô?ÍÌÌÌÌÌÀ333333Ó?š™™™™™ñ?š™™™™™@ffffffþ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?@ÍÌÌÌÌÌ @@333333@333333@ÍÌÌÌÌÌì?ffffffþ?333333 @š™™™™™@ffffff @333333û¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ@ffffffþ?@ffffff@333333ó?333333ó?š™™™™™@@ @ø?š™™™™™@ffffff@@333333û?333333@ø? @ÍÌÌÌÌÌô?ð?ø?š™™™™™ñ?ø?333333@@333333ã?ffffff@@š™™™™™ù?š™™™™™É?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@333333@@š™™™™™@ÍÌÌÌÌÌÀffffff @ÍÌÌÌÌÌ@333333û?ÍÌÌÌÌÌ@333333@333333 @ffffffþ?š™™™™™@š™™™™™@@333333ó¿š™™™™™@333333û?@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?333333@333333@333333/@ÍÌÌÌÌÌÀ@ÍÌÌÌÌÌô?ð?ÍÌÌÌÌÌì?ffffff@ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™@ffffff@ffffff @@333333ã?ffffffæ¿333333ó?à?ð?š™™™™™"@š™™™™™ù?333333ã?š™™™™™@ffffff@š™™™™™$@š™™™™™Ù?ffffff @ffffff@š™™™™™@333333@ÍÌÌÌÌÌ@@ffffffö?ffffffö?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?ffffff @ÍÌÌÌÌÌü?@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@333333@ffffff@š™™™™™@ffffff@333333@ÍÌÌÌÌÌô?š™™™™™@@ffffff@@@š™™™™™é?š™™™™™é?@š™™™™™@333333û?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333û?ð?@@333333@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ffffff@333333ã?à?ffffff@ffffffþ?ÍÌÌÌÌÌ@333333Ó?š™™™™™@333333@ÍÌÌÌÌÌü?333333ó?ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌü?@š™™™™™Ù?ÍÌÌÌÌÌ@@ffffffö?333333û?333333Ó?333333û?ÍÌÌÌÌÌü?333333@à¿ÍÌÌÌÌÌü?@ÍÌÌÌÌÌì?ø?à?333333ã?à?ÍÌÌÌÌÌì?ffffff@ffffff @ÍÌÌÌÌÌ@333333û?ÍÌÌÌÌÌì¿333333ã¿@ÍÌÌÌÌÌ@š™™™™™@ffffff@ffffffæ?ø?š™™™™™É?ø?š™™™™™Ù¿ffffff @ffffff@333333@ffffff@333333@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@333333@@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌô¿à?ffffff@š™™™™™@333333ó?ÍÌÌÌÌÌ @@ÍÌÌÌÌÌ@333333@š™™™™™é? @@333333 @333333@š™™™™™Ù?à¿ffffff@š™™™™™ù?š™™™™™@ÍÌÌÌÌÌÀš™™™™™@333333À@ffffffþ? @333333ã?ð?ø?š™™™™™ù?333333ã?@ffffff@š™™™™™é?š™™™™™ @ffffffþ?ÍÌÌÌÌÌ@š™™™™™Ù?ÍÌÌÌÌÌì?ffffff@ffffff@š™™™™™ù¿ÍÌÌÌÌÌ@à?ø?333333Ó?@ffffff@à?ffffff@333333ã?š™™™™™ñ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@@ffffff@333333@š™™™™™Ù?š™™™™™¹?333333Ó?ffffff@ffffffö¿ffffff@333333@@333333@š™™™™™ @ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@š™™™™™ñ?ÍÌÌÌÌÌì?š™™™™™ù?ffffffö?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™@ÍÌÌÌÌÌ @@ø¿333333û?š™™™™™ Àffffffþ?š™™™™™@333333@ffffff@333333û?š™™™™™ @à?ffffffæ¿ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?@ffffff@ÍÌÌÌÌÌ@@333333ó?@ffffffæ?š™™™™™ @š™™™™™ù?333333@š™™™™™@ffffff@@š™™™™™Ù?ÍÌÌÌÌÌì?333333ÀffffffÀ333333@333333ó?š™™™™™ù?š™™™™™ù?333333û¿ø¿ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@333333@š™™™™™¹¿š™™™™™@ffffff@š™™™™™@333333@ffffff@ÍÌÌÌÌÌ@š™™™™™ñ?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@@š™™™™™@ÍÌÌÌÌÌ@ffffff @333333ã?ø¿à?ÍÌÌÌÌÌ@333333û?ffffff@ð¿333333@ÍÌÌÌÌÌü¿333333û¿333333@ffffffæ?ÍÌÌÌÌÌ@š™™™™™ @ffffff @333333ó?333333ã¿333333 ÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?À@333333@š™™™™™É?ffffffö?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?Àffffffæ?333333@ffffff@@ÍÌÌÌÌÌ@š™™™™™ñ?š™™™™™@333333@333333û¿š™™™™™é¿š™™™™™ù?š™™™™™@ffffffæ? @ð?š™™™™™@ffffff @ÍÌÌÌÌÌ@š™™™™™@ø?š™™™™™ @ffffff @ÍÌÌÌÌÌ@š™™™™™ù?@@ffffff@š™™™™™ @š™™™™™É?š™™™™™@333333ã?333333 @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @ffffff@ffffffæ?@ÍÌÌÌÌÌ@š™™™™™Ù?@Àà?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?333333!@@ffffff@333333û?ffffffæ?333333Àš™™™™™é?@@š™™™™™Ù¿š™™™™™é¿ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@š™™™™™ñ?333333 @333333@š™™™™™@333333@ÍÌÌÌÌÌì? @š™™™™™ @333333 @š™™™™™@ffffffö?š™™™™™@ffffff Àffffff@333333@33333³1@š™™™™™'@ÀÍÌÌÌÌÌ<@3333330@333333=@ffffffö?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff*@)Àš™™™™™!Àffffff@ÍÌÌÌÌŒN@ÍÌÌÌÌÌ=@š™™™™™3@€0@š™™™™™,@ø¿333333K@š™™™™™5@!À1@#@ffffff4@>Àš™™™™™F@€3@@333333"ÀÍÌÌÌÌÌ@ÍÌÌÌÌÌ&@333333@€5@ÍÌÌÌÌÌ@€2@ffffffö¿3333333@)@ffffff1@3333336@ÀÍÌÌÌÌL?@333333L@ffffff*@"Àffffff @ÍÌÌÌÌÌ.@333333@ÍÌÌÌÌÌ!Àš™™™™™2@333333@2@ÀQ@333333,@š™™™™™-@3333330@ð¿ffffff.@ffffff@333333@333333/@ÍÌÌÌÌÌ+@ffffffC@@F@@333333ÀÍÌÌÌÌÌA@ffffff;@ÍÌÌÌÌÌÀ€7@1@333333+@@ÍÌÌÌÌÌ>@fffffæ?@ÍÌÌÌÌlQÀffffff@ffffffÀ333333-@ÍÌÌÌÌL?@ÍÌÌÌÌL7@ ÀÍÌÌÌÌÌ5@š™™™™<@33333“]À33333sQ@333333-@fffff†QÀ@š™™™™™"ÀÍÌÌÌÌÌ%@ffffffÀÍÌÌÌÌÌ@ffffffÀš™™™™A@ÍÌÌÌÌL8@ffffff%@1Àfffffæ5@€8@š™™™™ÙY@fffffFRÀàU@ffffffA@š™™™™™@ffffff&Àffffff7@IÀffffffA@š™™™™™@ffffff-@@333333#@š™™™™™@333333-@ffffffCÀø?33333³4@fffffæ7@š™™™™™(À,Àffffff!@#@€;@š™™™™™É?fffffæ=@@ÍÌÌÌÌÌÀ333333!À;@š™™™™™5@š™™™™™@ÍÌÌÌÌÌ @333333:@3333339@š™™™™™@3333330@š™™™™™Àš™™™™™;@fffffæ5@ÍÌÌÌÌÌü¿?@ffffff'@ffffff@ÍÌÌÌÌÌ5@;@ffffff @ÍÌÌÌÌÌ&@33333³1@ÍÌÌÌÌ B@ffffffB@;@!@33333³9@ÍÌÌÌÌÌì¿ÍÌÌÌÌ F@ffffffÀfffffæA@@ffffffö?333333G@ffffff@@fffff&A@@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌÀffffff@š™™™™™ÀÍÌÌÌÌL1ÀÍÌÌÌÌÌÀ€E@333333@@ffffff7@ÍÌÌÌÌL2@3333332@333333'@33333³:@33333³<@š™™™™™Àš™™™™™é?-À333333;@333333Ó¿š™™™™™/À333333@33333³0À333333!À333333)@E@3333337@ÍÌÌÌÌÌ'@*@ffffff<@ÍÌÌÌÌÌM@3333333@333333@333333+@š™™™™™*À4@ffffffTÀš™™™™A@ÀÍÌÌÌÌŒCÀ33333s@@fffffæA@"Àš™™™™™7Àš™™™™™'À333333%@3@ffffff@š™™™™™2@ÍÌÌÌÌÌ À33333³PÀš™™™™™@333333@333333)@fffffæ9@ÍÌÌÌÌL:@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌü¿3@-@333333$@ffffffF@3333330@ffffffE@fffffæC@š™™™™C@š™™™™YH@@fffffæ2@333333@š™™™™;@fffff&@@ÍÌÌÌÌÌ @ÍÌÌÌÌÌÀ€9@š™™™™™Àš™™™™6@š™™™™0À*@ÍÌÌÌÌÌ#@ffffff,À7@ÍÌÌÌÌÌ(Àfffff¦@@ffffff8Àš™™™™™#À3333331À333333(@š™™™™™ñ¿š™™™™™JÀffffff!@š™™™™™!@š™™™™™ @ffffff@ÍÌÌÌÌL;@&@333333@#@ffffff@ffffffÀš™™™™™(@ffffff@8@fffff¦A@š™™™™™@ð¿ÍÌÌÌÌÌ!@ffffffö¿š™™™™:@ffffff%@333333.@ÍÌÌÌÌÌD@š™™™™™@ffffff=@€0@@ÍÌÌÌÌÌ Àš™™™™™ÀÍÌÌÌÌL8@333333@333333)@š™™™™™Àš™™™™™@š™™™™™I@š™™™™ÙI@fffffæ?@€?@š™™™™ÙA@@š™™™™2@fffff&D@*@ÍÌÌÌÌL9@ÍÌÌÌÌL5Àš™™™™™:@3333338Àffffff(À3333337@š™™™™™-À€@@fffffæ4@ø¿fffff&A@ÍÌÌÌÌL1ÀIÀÍÌÌÌÌÌ%À@<@333333,Àffffff#@fffff&A@33333³@À333333Ó?ffffff'@ffffff/@333333/@š™™™™™@€2@ffffff#@ÍÌÌÌÌÌ @333333?@š™™™™™Ù¿ffffff.@ffffff@fffffæ4@š™™™™™:@ÀC@š™™™™™#@ffffff4@33333³1@fffff&C@ffffff@€1@ffffff)Àffffff@333333=@fffff¦B@ÍÌÌÌÌÌ2@š™™™™ÙE@ÍÌÌÌÌÌ@š™™™™™<Àffffff@ÍÌÌÌÌL@@ÍÌÌÌÌÌ4@#@š™™™™E@ÀRÀ#Àfffffæ<À@@333333ÀÍÌÌÌÌÌ;@@F@ÀEÀ3À33333³1@ÍÌÌÌÌÌ@ÍÌÌÌÌL>@€3Àfffff&D@ffffff @ffffffÀÍÌÌÌÌÌ*Àffffff3@ÍÌÌÌÌÌ@333333;@ffffff@@À333333û?-@ffffffJ@ÍÌÌÌÌÌ.À333333ó¿š™™™™™:@ÍÌÌÌÌÌ4@š™™™™3@ffffff2@š™™™™™Àš™™™™™&@ÍÌÌÌÌÌÀš™™™™™ÀffffffÀš™™™™™9@33333sD@€;À333333ZÀ1@ffffff"@>À333333$Àffffff@ÍÌÌÌÌÌ8@333333"ÀÍÌÌÌÌLFÀfffff¦E@33333³5@?@fffffæN@ffffff;Àffffff3@€?@ffffff@@€=@š™™™™™@ÍÌÌÌÌL?@1@š™™™™™>@33333sCÀš™™™™™Ù¿9@ffffff!@333333û?š™™™™?@ÍÌÌÌÌÌ#À333333_À+@3333337@fffffæ:À @ÍÌÌÌÌŒB@š™™™™™1@33333sA@š™™™™™Àš™™™™8Àš™™™™™@ffffff7@š™™™™<@ffffff@š™™™™7@ @š™™™™™8@fffff¦L@ÍÌÌÌÌÌ@@(@š™™™™™&Àfffffæ<@š™™™™4ÀÍÌÌÌÌLQ@ÍÌÌÌÌÌQÀ€1@F@ÍÌÌÌ̬Q@ÍÌÌÌÌLA@33333³4@ÍÌÌÌÌŒO@š™™™™™O@33333sU@ÍÌÌÌÌÌ.@ffffff,@33333sO@ÍÌÌÌÌL6@fffff¦BÀš™™™™™ÀÍÌÌÌÌÌ*@ÍÌÌÌÌÌY@š™™™™™G@€F@ÍÌÌÌÌLN@3333334@33333³:@€U@333333O@ffffff.@€C@š™™™™™2@ÍÌÌÌÌŒB@ø¿0`@fffffæH@š™™™™™-ÀÍÌÌÌÌÌ4Àš™™™™YF@33333³;@fffff¦A@š™™™™YE@ffffff&@33333³9@1@ÍÌÌÌÌLV@ÍÌÌÌÌLE@š™™™™™;@33333³F@ÍÌÌÌÌÌ*@33333óH@333333T@fffffæA@ÍÌÌÌÌÌì?€<@3333339@š™™™™™*@333333 @€:@ÍÌÌÌÌL4@333333G@fffff&Z@33333³1@ÀA@š™™™™ÙA@333333(@ffffff1@š™™™™™<@š™™™™™4@33333³B@ÍÌÌÌÌŒB@ÍÌÌÌÌLR@ÍÌÌÌÌìU@fffffæ9@333333:Àfffff¦N@fffff¦K@ÍÌÌÌÌÌ @š™™™™YI@ÍÌÌÌÌ J@ÀE@33333³5@ÍÌÌÌ̬[@š™™™™YO@ÍÌÌÌÌÌ@ÀÍÌÌÌÌÌ'@€;@fffff¦F@33333óN@š™™™™™@33333óD@G@R@fffffæ<@ÍÌÌÌÌÌC@š™™™™YJÀ1@à¿fffff¦D@€7@+@fffffÆQ@fffff&P@ÍÌÌÌÌLL@33333óF@ÍÌÌÌÌÌ%Àš™™™™0@@A@ÍÌÌÌ̬Z@àPÀš™™™™Éf@fffff&G@%@fffff&M@š™™™™™S@àQ@ÍÌÌÌÌLB@fffffæ=@š™™™™K@33333³5@333333Q@33333³7@ÍÌÌÌÌŒ@@33333³9Àš™™™™™&@š™™™™™ @33333³A@fffff&H@ @š™™™™™7@33333óS@ÍÌÌÌÌL9@fffffÆQ@fffffæ7@fffff&H@š™™™™™3@š™™™™™-@333333E@@A@33333óF@333333K@fffffæH@fffff&P@ÍÌÌÌ̬R@š™™™™™$@š™™™™™5@ÍÌÌÌÌÌ2@ÍÌÌÌÌLH@333333@@ffffff9@fffff&J@33333óP@ffffffD@ÍÌÌÌÌÌM@fffffæG@š™™™™™/@ffffff1@ÍÌÌÌÌ A@fffffF\@fffff&A@333333I@ÍÌÌÌÌ L@š™™™™™K@€>@ffffff@š™™™™ÙM@ÍÌÌÌÌÌ+@fffff†S@@@K@fffff&V@33333³F@š™™™™™D@33333³P@€W@ffffff#@ÍÌÌÌÌL=@ÍÌÌÌÌÌÀ@š™™™™YH@š™™™™J@ffffff>@333333@33333ó@@ÍÌÌÌÌÌ=@fffff&K@š™™™™™@š™™™™YD@ÀQ@@ffffff"@ Àfffffæ=@ffffffþ?š™™™™™ù¿š™™™™YE@fffffæ6@>@333333<@š™™™™™B@ÍÌÌÌÌÌ1@33333³@@ÍÌÌÌÌÌC@š™™™™YR@fffff&U@ÍÌÌÌÌL8@ffffff*@ÍÌÌÌÌÌ!@33333³8@3333331Àš™™™™ÙH@fffffæCÀfffff&M@ffffff9@=@š™™™™ÙQ@€N@33333³E@ffffffB@3333338@š™™™™™A@33333óE@ÍÌÌÌÌL:@ÍÌÌÌÌÌ @ÍÌÌÌÌÌÀffffffEÀ€5@&@ÍÌÌÌÌLH@fffffFW@33333R@B@333333ó?ffffffL@33333sB@fffff¦C@ÍÌÌÌÌlT@3@fffff¦B@fffffæT@33333³G@ÍÌÌÌÌìW@BÀÍÌÌÌ̬Q@(ÀÍÌÌÌÌÌG@33333sE@33333óC@ø?fffff¦@@ð?ÍÌÌÌÌL?@333333 ÀH@fffff&@@À333333J@à?33333“U@ffffff3Àffffff@333333@33333óQ@33333³2ÀÍÌÌÌÌÌ0ÀÍÌÌÌÌÌ%@ÍÌÌÌÌÌ4@@š™™™™YP@š™™™™YF@333333+@33333³8@ffffff(@ffffff,@š™™™™™5@ffffff;@š™™™™™<@š™™™™G@33333“U@E@@#@à?ÍÌÌÌÌŒE@ Àš™™™™™?@33333óT@fffffæ?@š™™™™YX@fffffæG@1@,@š™™™™™@ffffff>@ÍÌÌÌÌL0@š™™™™™6@ÍÌÌÌÌÌì?333333*@ffffffV@š™™™™)a@33333T@333333#@š™™™™F@ÍÌÌÌÌÌ5Àš™™™™ÙC@ÍÌÌÌÌ W@fffffQ@ÍÌÌÌÌLI@š™™™™™!ÀffffffJ@€3Àš™™™™™0À33333“Z@333333ÀffffffJ@š™™™™ÙI@š™™™™Ù@@33333sZ@333333À,ÀÍÌÌÌÌÌÀfffff¦A@š™™™™™C@š™™™™™'@ÍÌÌÌÌLG@ffffffI@ÍÌÌÌÌÌ3Àš™™™™™ù?š™™™™4@333333'Àš™™™™™*ÀÍÌÌÌÌÌH@š™™™™:@ÍÌÌÌÌÌ5@€<@ffffff,@€1Àš™™™™™I@333333K@š™™™™yR@ R@@Z@"@ÍÌÌÌÌìP@š™™™™™L@fffff&[@fffffæ6@33333sI@ÍÌÌÌÌL2@ffffff/@ÍÌÌÌÌ D@š™™™™W@333333P@ÍÌÌÌÌÌN@33333³9@333333-À€>@€K@š™™™™™=@š™™™™™À33333óF@€@Àffffff@ÍÌÌÌÌLD@ÍÌÌÌÌÌ4@333333M@š™™™™™+@333333=@ÍÌÌÌÌÌ%ÀÍÌÌÌÌÌ+ÀfffffæB@333333?@N@"ÀB@fffffæ?Àš™™™™™5@333333)@@333333ã¿ffffffC@ÍÌÌÌÌÌW@š™™™™™ @fffffæ0Àffffff@7@ffffff(ÀffffffÀ2@€H@ÍÌÌÌÌÌB@fffff&R@ffffff9@R@€A@ÍÌÌÌÌL;Àš™™™™™$Àffffff$@ffffffU@ÍÌÌÌÌÌ=@àP@š™™™™™*À@fffff¦G@333333B@ÀF@š™™™™™@ffffffD@fffffæI@@H@fffff¦BÀš™™™™ÙX@fffff&G@@R@š™™™™™W@ÍÌÌÌÌÌ9Àš™™™™YP@fffffæA@š™™™™™L@fffffÆW@š™™™™D@fffff&H@ÍÌÌÌÌL6@ÍÌÌÌÌ I@š™™™™™0Àš™™™™™ù?š™™™™YR@ÍÌÌÌÌÌ!Àffffff @fffffæH@333333@333333@š™™™™YL@ÍÌÌÌÌŒR@š™™™™™Àš™™™™™(@ffffffÀÍÌÌÌÌÌ8@33333³L@š™™™™ÙS@š™™™™™9Àš™™™™™@fffffæ@@@O@.@33333sI@3333339@@P@ÍÌÌÌÌŒT@333333$@š™™™™™B@€H@3333331@33333U@#Àš™™™™)`@ÍÌÌÌÌ,dÀš™™™™L@ffffffN@š™™™™™Ù¿š™™™™™Ù¿ÍÌÌÌÌÌô?š™™™™™¹?š™™™™™É¿333333ã?š™™™™™É¿š™™™™™¹?à¿ÍÌÌÌÌÌì¿333333Ó¿š™™™™™¹¿š™™™™™É?࿚™™™™™Ù¿š™™™™™¹¿à¿š™™™™™¹?š™™™™™Ù¿š™™™™™É¿333333Ó¿333333Ó¿333333㿚™™™™™¹?š™™™™™¹¿333333Ó¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™¹¿š™™™™™¹?š™™™™™é¿333333Ó¿ffffffæ¿à¿š™™™™™Ù¿š™™™™™¹?333333Ó¿ffffff濚™™™™™É¿š™™™™™¹¿333333Ó¿š™™™™™¹¿ð¿333333ã¿à¿š™™™™™¹¿333333Ó¿333333ó¿333333Ó¿333333ã?š™™™™™Ù¿à¿š™™™™™É¿š™™™™™¹¿š™™™™™Ù¿š™™™™™¹¿š™™™™™Ù¿š™™™™™Ù?ffffffæ¿333333Ó?š™™™™™é¿š™™™™™É¿š™™™™™É¿ÍÌÌÌÌÌ@š™™™™™é¿ÍÌÌÌÌÌì¿333333ã¿ð¿333333Ó¿333333ã¿à¿333333ӿ࿚™™™™™ù¿š™™™™™ù¿ffffffö¿š™™™™™É¿333333ã?࿚™™™™™¹?š™™™™™É?š™™™™™É¿ð¿š™™™™™¹¿š™™™™™ñ¿š™™™™™Ù¿š™™™™™¹?š™™™™™é¿ÍÌÌÌÌÌ쿚™™™™™¹¿À333333ã¿333333㿚™™™™™¹?ÍÌÌÌÌÌì?࿚™™™™™É?à¿333333ã¿ð¿333333Ó¿333333Ó?š™™™™™É¿ffffff濚™™™™™¹?š™™™™™Ù¿š™™™™™¹?ffffff濚™™™™™É¿š™™™™™¹¿à¿333333㿚™™™™™É¿š™™™™™Ù¿š™™™™™¹?š™™™™™¹?333333Ó¿š™™™™™É?š™™™™™¹?š™™™™™Ù¿š™™™™™Ù¿š™™™™™É¿333333㿚™™™™™¹?š™™™™™¹?š™™™™™Ù?š™™™™™¹¿ð¿ÍÌÌÌÌÌ쿚™™™™™¹¿à¿š™™™™™Ù¿š™™™™™É¿š™™™™™É?š™™™™™É¿à¿š™™™™™É¿333333㿚™™™™™¹¿333333Ó¿š™™™™™¹¿š™™™™™¹?࿚™™™™™¹?࿚™™™™™Ù¿333333Ó¿à¿ffffffæ¿333333ã¿ffffff濚™™™™™Ù¿š™™™™™¹?࿚™™™™™Àš™™™™™Ù¿š™™™™™¹?š™™™™™¹?333333ó¿ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™Ù¿333333Ó¿ffffff濚™™™™™¹¿333333Ó¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™¹?ð?333333Ó¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿ffffff濚™™™™™¹¿ð?333333㿚™™™™™é¿š™™™™™Ù¿š™™™™™¹?333333ó¿š™™™™™É¿ÍÌÌÌÌÌ@𿚙™™™™É¿š™™™™™¹?333333Ó¿š™™™™™ñ¿333333㿚™™™™™¹¿š™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™É¿333333ó¿š™™™™™É¿ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™Àš™™™™™¹?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿à¿333333ӿ࿚™™™™™é¿à¿š™™™™™ñ¿š™™™™™É?333333㿚™™™™™É¿š™™™™™É?333333㿚™™™™™¹?š™™™™™¹?ÍÌÌÌÌÌü¿š™™™™™É?š™™™™™Ù¿š™™™™™É¿à¿š™™™™™Ù?333333Ó¿ffffffæ¿à¿š™™™™™¹?š™™™™™Ù?š™™™™™¹¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿333333ã¿333333㿚™™™™™é¿š™™™™™Ù¿333333Ó?š™™™™™Ù¿š™™™™™¹?š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿333333Ó¿š™™™™™É¿š™™™™™¹¿š™™™™™Ù¿š™™™™™¹¿š™™™™™É¿333333ó¿333333Ó¿š™™™™™¹?ÍÌÌÌÌÌô¿š™™™™™ñ¿ÍÌÌÌÌÌô¿š™™™™™é?š™™™™™Ù?š™™™™™¹?ffffffþ¿š™™™™™ñ¿š™™™™™É¿š™™™™™ñ¿š™™™™™É?š™™™™™¹¿333333Àš™™™™™¹?š™™™™™¹¿ÍÌÌÌÌÌì¿333333㿚™™™™™¹?š™™™™™Ù¿à¿ffffff@š™™™™™¹?à¿333333㿚™™™™™É¿š™™™™™¹¿ffffffö¿333333Àà?333333㿚™™™™™é¿š™™™™™Ù¿333333ó¿ÍÌÌÌÌÌô¿š™™™™™Ù¿š™™™™™É¿ffffffæ?࿚™™™™™é?š™™™™™¹¿333333ó¿š™™™™™¹?š™™™™™É¿š™™™™™Ù¿š™™™™™¹?ffffffæ¿à?333333Ó?࿚™™™™™¹?333333㿚™™™™™É?š™™™™™ñ¿à¿š™™™™™ù¿ffffffæ¿ÍÌÌÌÌÌ@š™™™™™¹?š™™™™™Ù?à¿333333ó¿ø¿333333û¿š™™™™™É?š™™™™™É¿š™™™™™¹?š™™™™™ñ¿333333û¿à¿š™™™™™é¿š™™™™™Ù?ffffff濚™™™™™Ù¿à¿à¿ffffffæ?š™™™™™¹¿à¿ÍÌÌÌÌÌô¿š™™™™™É?à¿333333Ó¿333333Ó¿š™™™™™É¿333333ã¿ffffffæ¿ffffffæ¿333333ÀÍÌÌÌÌÌô¿ffffffæ?à¿ÍÌÌÌÌÌì¿ffffffö?333333ã?š™™™™™Ù?à?333333Ó¿š™™™™™É?š™™™™™¹?ÍÌÌÌÌÌ@š™™™™™Ù?š™™™™™¹?ÍÌÌÌÌÌì¿à¿à¿ÀÍÌÌÌÌÌô¿333333ó¿š™™™™™ñ¿š™™™™™¹?š™™™™™Ù¿š™™™™™É¿ÍÌÌÌÌÌì¿ffffff濚™™™™™É¿š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™Ù¿333333Ó¿333333Ó¿333333 @à?š™™™™™¹¿š™™™™™Ù?š™™™™™É¿š™™™™™Ù¿ffffffæ¿ffffffæ¿333333ã?333333Ó¿š™™™™™É?š™™™™™¹¿š™™™™™Ù?à¿333333ã¿à¿ffffffö¿š™™™™™¹¿š™™™™™É¿à¿333333Ó?š™™™™™Ù¿š™™™™™¹?ffffff濚™™™™™É?š™™™™™¹?š™™™™™Ù¿š™™™™™é¿š™™™™™ñ¿à¿à¿š™™™™™Ù¿ffffffæ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ffffffþ¿333333㿚™™™™™Ù¿à¿š™™™™™Ù¿à¿333333û¿ÍÌÌÌÌÌÀš™™™™™Ù¿333333ã¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿š™™™™™ñ¿Àš™™™™™é¿ÍÌÌÌÌÌì¿à¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿333333ó¿ffffffÀà¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ffffffæ¿ffffffö¿ð¿333333ó¿333333ó¿š™™™™™Ù¿333333ã¿ÍÌÌÌÌÌì¿333333ó¿š™™™™™ñ¿š™™™™™Ù¿ð¿#Àffffff濚™™™™™ù¿š™™™™™ñ¿š™™™™™ñ¿š™™™™™Ù¿ffffff濚™™™™™Ù¿š™™™™™Ù¿š™™™™™Ù¿333333û¿š™™™™™Ù¿333333ó¿š™™™™™Ù¿333333ã¿333333ã¿ÍÌÌÌÌÌô¿š™™™™™ù¿333333û¿š™™™™™ñ¿ø¿ð¿š™™™™™é¿à¿333333㿚™™™™™é¿ÍÌÌÌÌÌô¿ffffffæ¿333333 À333333㿚™™™™™ñ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ð¿ffffff濚™™™™™é¿à¿333333ó¿š™™™™™é¿ffffff,Àð¿ffffff濚™™™™™ñ¿š™™™™™Ù¿š™™™™™é¿ffffffþ¿š™™™™™Ù¿333333ã¿ÍÌÌÌÌÌ Àš™™™™™é¿333333ã¿ Àà¿à¿+@&@333333&@ffffff.@333333"@€3@š™™™™™@!@š™™™™™"@ÍÌÌÌÌÌ%@ffffff)@333333@ffffff@3@333333+@333333#@'@ÍÌÌÌÌÌ @š™™™™™ @#@ÍÌÌÌÌÌ&@ffffff)@š™™™™™#@ffffff#@š™™™™™/@333333"@€5@ffffff*@@333333@š™™™™™@š™™™™™"@š™™™™™ @*@š™™™™™#@ffffff'@333333)@š™™™™™)@š™™™™™"@ÍÌÌÌÌÌ@š™™™™™)@š™™™™™@333333'@š™™™™™3@333333 @ÍÌÌÌÌÌ @@&@ffffff@š™™™™™@ffffff$@333333"@š™™™™1@€4@ÍÌÌÌÌÌ)@ffffff#@ffffff&@š™™™™™&@fffffæ5@š™™™™™%@ @ffffff$@ÍÌÌÌÌÌ@š™™™™™-@ÍÌÌÌÌÌ0@333333@333333!@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ$@#@333333 @ffffff.@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ*@š™™™™™*@š™™™™™(@333333,@%@'@š™™™™™'@ÍÌÌÌÌÌ@ÍÌÌÌÌL6@ffffff(@333333'@š™™™™™@ffffff @@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@333333+@.@š™™™™™$@ffffff @ffffff,@š™™™™™#@33333³0@š™™™™™(@š™™™™;@ffffff$@3@€8@333333"@fffffæ3@ffffff*@š™™™™™$@ffffff'@š™™™™™"@1@,@š™™™™™@"@ @š™™™™™@ffffff(@333333)@%@@ffffff)@333333@ÍÌÌÌÌÌ$@ffffff@ÍÌÌÌÌÌ/@š™™™™™@!@š™™™™™#@ffffff+@#@333333/@333333!@š™™™™™0@3333332@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ'@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ*@3333331@ @+@ÍÌÌÌÌÌ'@š™™™™™#@3333331@&@ffffff@"@333333$@333333.@@333333(@333333$@ffffff*@ffffff+@ffffff)@333333'@š™™™™™@/@ÍÌÌÌÌÌ"@š™™™™™,@ffffff&@š™™™™™'@(@333333&@ÍÌÌÌÌÌ%@ffffff)@333333@ffffff"@ffffff)@ÍÌÌÌÌÌ*@6@ÍÌÌÌÌÌ@333333@š™™™™™&@333333%@*@333333@ÍÌÌÌÌÌ/@š™™™™™$@333333#@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@ffffff @ÍÌÌÌÌL=@š™™™™™ @ÍÌÌÌÌÌ@333333&@3333330@š™™™™™(@š™™™™™ @š™™™™™ @.@33333³3@š™™™™™'@€2@ÍÌÌÌÌÌ @333333@ffffff@š™™™™™$@ÍÌÌÌÌÌ@ffffff(@(@@fffffæ3@š™™™™™.@&@š™™™™™@ffffff@š™™™™™,@ÍÌÌÌÌÌ @ffffff@1@333333@š™™™™™.@š™™™™™ @333333%@/@ffffff1@333333#@ffffff @+@ÍÌÌÌÌÌ @š™™™™™$@fffffæ2@š™™™™™ @ÍÌÌÌÌÌ#@(@1@fffffæ1@3333331@333333$@$@ÍÌÌÌÌÌ+@š™™™™™*@-@ffffff@+@ @ffffff/@ÍÌÌÌÌÌ(@š™™™™™$@š™™™™™(@333333@š™™™™™@ffffff&@ffffff2@ffffffþ?ffffff$@š™™™™™@$@3333330@333333û?ffffff!@333333*@333333@333333@333333)@ffffff'@ÍÌÌÌÌÌ@333333"@333333!@š™™™™™@ffffff"@ffffff@ffffff&@ÍÌÌÌÌÌ(@ÍÌÌÌÌÌ$@333333@ÍÌÌÌÌÌ*@ffffff@š™™™™™$@ÍÌÌÌÌÌ#@%@0@!@ÍÌÌÌÌÌ0@ffffff@š™™™™™@#@š™™™™™(@ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ@š™™™™™@333333@š™™™™™ @š™™™™2@š™™™™3@ffffff&@333333*@š™™™™™/@333333'@ffffff@š™™™™™*@333333-@š™™™™™*@333333@333333.@š™™™™™ @ÍÌÌÌÌÌ/@ffffff5@@ffffff%@.@š™™™™™@ÍÌÌÌÌÌ*@š™™™™™0@š™™™™3@@ffffff(@š™™™™™*@š™™™™™ @ÍÌÌÌÌÌ!@ffffff-@%@333333@ÍÌÌÌÌÌ*@š™™™™™,@6@š™™™™™@333333%@š™™™™™)@3333334@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌ#@ffffff6@š™™™™1@3333336@ffffff.@333333'@ffffff@(@333333(@ffffff!@333333/@ffffff@ffffff%@š™™™™™$@š™™™™™+@š™™™™™#@ÍÌÌÌÌL0@š™™™™™@ÍÌÌÌÌÌ@333333 @&@#@333333+@ffffff%@ffffff&@š™™™™™@ÍÌÌÌÌL0@ÍÌÌÌÌÌ3@š™™™™™@333333-@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@333333%@@,@333333@33333³3@ÍÌÌÌÌÌ/@ffffff&@š™™™™™@@ÍÌÌÌÌÌ&@š™™™™™"@€3@š™™™™™!@š™™™™™@š™™™™™@ÍÌÌÌÌÌ!@ÍÌÌÌÌL1@ÍÌÌÌÌÌ@š™™™™™"@3333331@ÍÌÌÌÌÌ'@3333330@ÍÌÌÌÌÌ*@fffffæ0@333333$@ffffff%@š™™™™™+@ÍÌÌÌÌÌ@ÍÌÌÌÌL0@*@€3@333333#@š™™™™™ @ffffff"@@333333!@333333@ÍÌÌÌÌÌ&@33333³6@(@ÍÌÌÌÌÌ@š™™™™™/@ÍÌÌÌÌÌ!@š™™™™™*@ÍÌÌÌÌÌ5@333333 @š™™™™™3@ÍÌÌÌÌÌ,@š™™™™™.@š™™™™;@ÍÌÌÌÌÌ(@.@ffffff.@š™™™™™,@1@š™™™™™@ffffff,@ÍÌÌÌÌÌ-@!@ÍÌÌÌÌÌ.@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌ@š™™™™™&@š™™™™™)@š™™™™™%@ffffff"@ÍÌÌÌÌÌ&@€=@š™™™™™-@ÍÌÌÌÌÌ7@ffffff@333333%@&@ÍÌÌÌÌÌ)@&@33333³0@ÍÌÌÌÌÌ#@€7@š™™™™™0@ffffff@ffffff0@š™™™™™"@š™™™™™ @333333"@ffffff@3333335@6@š™™™™™.@)@À333333À333333 Àš™™™™™ ÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌ&ÀÀš™™™™™ÀffffffÀÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀffffffö¿š™™™™™Ù¿š™™™™™ù¿ÍÌÌÌÌÌÀÍÌÌÌÌÌü¿ø¿ffffffö¿ffffffÀ333333ÀffffffÀš™™™™™ À333333À333333ÀffffffÀffffffÀ333333ÀÀ333333ÀffffffÀš™™™™™À333333À333333Àš™™™™™é¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333û¿À333333 Àffffffþ¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀ ÀÀÍÌÌÌÌÌÀ333333ó¿333333ÀÍÌÌÌÌÌÀ333333ÀffffffÀffffffÀffffffþ¿ÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌü¿š™™™™™ÀÍÌÌÌÌÌ Àffffffö¿š™™™™™À333333ã¿ÍÌÌÌÌÌô¿š™™™™™Àffffffæ¿333333û¿À333333ó¿ffffffö¿ÍÌÌÌÌÌÀø¿333333ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀÀš™™™™™ÀÀÀÍÌÌÌÌÌü¿ÍÌÌÌÌÌü¿333333 À333333À ÀÀ333333ÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌÀš™™™™™ Àš™™™™™ Àffffffþ¿ffffffÀ333333 ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ&À333333û¿ffffff"ÀÍÌÌÌÌÌì¿ÀffffffÀffffffÀš™™™™™Àš™™™™™ À333333ÀÍÌÌÌÌÌ Àš™™™™™Àš™™™™™#ÀÀÍÌÌÌÌÌÀš™™™™™ Àš™™™™™ù¿333333"Àš™™™™™!Àš™™™™™ÀÀø¿š™™™™™À333333 ÀffffffÀ333333À333333Àš™™™™™À333333ã¿Àffffff ÀÀffffffÀš™™™™™Àš™™™™™ÀÀš™™™™™ñ¿ffffffÀ333333Àø¿ffffffÀffffffÀš™™™™™ ÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀffffffÀÀà¿ffffff濚™™™™™ÀÍÌÌÌÌÌÀš™™™™™ÀffffffÀÀš™™™™™ Àø¿š™™™™™Àð¿ÍÌÌÌÌÌÀš™™™™™À À333333À ÀÍÌÌÌÌÌÀ%À#À333333 À333333À333333Àš™™™™™ñ¿333333û¿ÍÌÌÌÌÌÀÀÀ333333û¿ffffffÀš™™™™™ù¿ffffffÀ333333ó¿ffffffþ¿333333À333333ÀÀffffff ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ ÀÀffffffþ¿Àš™™™™™ÀÍÌÌÌÌÌÀ333333ã¿ÀÍÌÌÌÌÌÀ333333ÀÍÌÌÌÌÌô¿ffffffÀš™™™™™ñ¿333333!À333333À333333À À333333$ÀÍÌÌÌÌÌÀš™™™™™ù¿ÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀø¿333333Àffffffæ¿ffffffÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀÀffffffö¿ÍÌÌÌÌÌ Àš™™™™™ñ¿š™™™™™Àš™™™™™Ù¿ÍÌÌÌÌÌô¿333333ó¿ÍÌÌÌÌÌô¿333333À333333ÀÍÌÌÌÌÌü¿ÀÍÌÌÌÌÌÀš™™™™™ÀÍÌÌÌÌÌ ÀffffffÀš™™™™™À333333#À333333ÀÀ333333 Àffffff À333333 ÀffffffÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀffffffÀffffffþ¿333333Àffffff À333333À333333û¿333333À333333ÀÍÌÌÌÌÌÀš™™™™™ À333333ÀÍÌÌÌÌÌÀš™™™™™ Àffffffþ¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀš™™™™™ ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ Àffffffæ¿ffffffÀÍÌÌÌÌÌô¿š™™™™™ ÀffffffÀÍÌÌÌÌÌ!À333333û¿À333333À333333 Àffffffö¿ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀffffffþ¿Àffffffþ¿ÀÍÌÌÌÌÌô¿333333ÀÍÌÌÌÌÌÀÀffffffÀ333333"ÀÍÌÌÌÌÌÀÀÀÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌ%Àffffff)À333333À ÀÍÌÌÌÌÌÀš™™™™™"ÀffffffÀffffff Àffffff$À333333Àš™™™™™ÀÍÌÌÌÌÌÀffffffÀffffff Àš™™™™™ù¿ffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌü¿333333ÀÍÌÌÌÌÌô¿ À333333À333333ÀÀÀš™™™™™ñ¿Àš™™™™™ Àð¿Àš™™™™™À333333Àš™™™™™Àš™™™™™ Àš™™™™™é¿Àš™™™™™ÀffffffÀ333333Àš™™™™™ñ¿ÍÌÌÌÌÌÀffffffþ¿Àš™™™™™"ÀÍÌÌÌÌÌÀš™™™™™ÀffffffÀš™™™™™À ÀffffffÀffffffÀš™™™™™!À333333Àš™™™™™ Àš™™™™™ ÀÀš™™™™™ ÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333À%À333333ÀÀÍÌÌÌÌÌÀffffff À333333 À333333ÀffffffÀÀ333333ó¿333333Àø¿ÀÍÌÌÌÌÌÀÀÀ333333À333333!À333333 Àš™™™™™ù¿ffffff À333333%À333333ÀÍÌÌÌÌÌÀffffffÀÍÌÌÌÌÌÀš™™™™™-Àð¿ÍÌÌÌÌÌ쿚™™™™™À333333ÀÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀ333333 Àš™™™™™Àš™™™™™é¿ÍÌÌÌÌÌ ÀÍÌÌÌÌÌÀÍÌÌÌÌÌ$À333333À333333Àš™™™™™ÀffffffÀÍÌÌÌÌÌÀÍÌÌÌÌÌÀÍÌÌÌÌÌ Àš™™™™™À333333ÀÀ333333û¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌÀ333333Àš™™™™™*ÀÀÍÌÌÌÌÌÀffffffþ¿ø¿š™™™™™Àffffff Àø¿ffffffÀffffffþ¿ÍÌÌÌÌÌÀffffffÀffffffÀffffffÀš™™™™™ù¿š™™™™™ ÀffffffÀÍÌÌÌÌÌ>À À333333À333333@à?š™™™™™Ù?š™™™™™Ù?à?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™É¿š™™™™™é?š™™™™™Ù?š™™™™™¹?š™™™™™é?ð?š™™™™™é?333333ã?š™™™™™é?à?š™™™™™É?ffffffæ?š™™™™™Ù?333333Ó?à?à?š™™™™™Ù?ð?à?333333ã?à?à?ð?š™™™™™Ù?à?š™™™™™Ù?333333ã?š™™™™™Ù?333333ã?333333ã?ÍÌÌÌÌÌì?ffffffæ?à?ð?ÍÌÌÌÌÌì?š™™™™™Ù?à?š™™™™™Ù?ð?333333Ó?ffffffæ?š™™™™™é?333333Ó?š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌì?333333ã?333333ã?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™¹¿à?š™™™™™é?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™é?ÍÌÌÌÌÌì?333333ã?š™™™™™¹¿333333ã?333333ã?333333ã¿ð?ÍÌÌÌÌÌì?ffffffæ?333333Ó?ffffffæ?ffffffæ?333333ã?à?ffffffæ?à?ÍÌÌÌÌÌô?ð?à?ð?š™™™™™É¿ÍÌÌÌÌÌì?333333Ó?š™™™™™¹?š™™™™™¹?333333ã?ð?à?333333Ó?ð?à?ð?š™™™™™É¿ffffffæ?š™™™™™É?š™™™™™Ù?333333ã?ffffffæ?ffffffæ?ÍÌÌÌÌÌô?š™™™™™¹¿à?š™™™™™ñ?š™™™™™é?ffffffæ?š™™™™™¹¿333333Ó?š™™™™™Ù¿ÍÌÌÌÌÌì?à?š™™™™™Ù?333333Ó?333333ã?š™™™™™Ù?à?š™™™™™Ù?ffffffæ?333333ã?š™™™™™Ù?ÍÌÌÌÌÌì?à?š™™™™™Ù?à?333333ã?š™™™™™é?333333ã?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?ffffffæ?š™™™™™¹¿à?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333ã?ffffffö?à?š™™™™™Ù?333333ó?à?š™™™™™é?à?ffffffæ?š™™™™™é?à?ð?ffffffæ?š™™™™™¹?333333ã?à?333333Ó?ffffffæ?à?š™™™™™é?š™™™™™Ù?333333Ó?š™™™™™É¿ffffffæ?ÍÌÌÌÌÌô?à?ffffffæ?š™™™™™Ù?333333ó?ffffffæ?333333ã?à?à?333333ã?333333Ó?333333Ó¿333333Ó?š™™™™™Ù?š™™™™™É¿ffffffæ?333333Ó?ÍÌÌÌÌÌì?š™™™™™Ù?333333Ó?š™™™™™é?ffffffæ?333333ã?š™™™™™é?à?š™™™™™é?š™™™™™É?š™™™™™¹¿ffffffæ?š™™™™™é?333333ã?ffffffæ?à?333333Ó?à?ffffff@333333ã?š™™™™™Ù?à?333333Ó?333333Ó?ÍÌÌÌÌÌì?à?š™™™™™Ù?š™™™™™¹?à?ffffffæ?ffffffæ?ffffffæ?à?ÍÌÌÌÌÌì?š™™™™™é?333333Ó?ÍÌÌÌÌÌì?š™™™™™é?š™™™™™Ù?à?à?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™É?ÍÌÌÌÌÌì?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?333333Ó?à?ð?à?š™™™™™Ù?ÍÌÌÌÌÌì?ffffffæ?š™™™™™Ù?ffffffæ?333333ã?ð?333333Ó?333333Ó?333333ã?š™™™™™Ù¿š™™™™™é?ÍÌÌÌÌÌì?à?333333ã?ffffffæ?ffffffæ?333333ã?ÍÌÌÌÌÌì?333333ã?333333Ó?333333ã?ffffffæ?333333Ó?333333ã?š™™™™™Ù?š™™™™™ñ?ð?333333ã?š™™™™™Ù?š™™™™™Ù?à?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù?à?333333Ó¿à?à?333333Ó?333333ã?ð?à?à?š™™™™™¹¿333333ã?š™™™™™Ù?š™™™™™Ù?à?š™™™™™Ù¿š™™™™™Ù?à?à?š™™™™™Ù?š™™™™™Ù?š™™™™™É?333333ã?š™™™™™ñ?š™™™™™é¿333333ã?333333Ó?333333Ó?ÍÌÌÌÌÌì?ffffffæ¿333333ã?š™™™™™¹¿333333ã?à?ÍÌÌÌÌÌô?333333Ó?333333ã?š™™™™™Ù?š™™™™™Ù?࿚™™™™™É¿ÍÌÌÌÌÌì?š™™™™™é?333333ã?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™é?š™™™™™ñ?ffffffæ?ffffffö?ffffffæ?š™™™™™Ù?333333ã?ffffffæ?333333ã?à?333333ã?š™™™™™é?š™™™™™É?333333ã?š™™™™™ñ?š™™™™™é?š™™™™™ñ?333333ã?à?š™™™™™é?ÍÌÌÌÌÌì?à?ffffffæ?à?à?ffffffæ?à?ffffffæ?333333ã?š™™™™™Ù?à?333333û?ffffffæ?333333ã?š™™™™™É?ffffffæ?ffffffæ?333333ó?ffffffæ?à?ffffffæ?š™™™™™Ù?š™™™™™Ù?ð?š™™™™™é?333333ã?ffffffæ?à?š™™™™™Ù?š™™™™™É?333333ã?333333Ó?333333Ó?à?333333Ó?333333ã?ffffffæ?š™™™™™Ù?333333Ó?333333ã?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™Ù?333333ã?š™™™™™é?ffffffæ?ffffffæ?š™™™™™Ù?ffffffæ?333333Ó¿333333ã?š™™™™™Ù?333333Ó?333333ã?š™™™™™Ù?333333ã?š™™™™™é¿à?ffffffæ?š™™™™™¹¿333333ã?ffffffæ?ffffffæ?š™™™™™Ù¿333333ã?š™™™™™Ù?ffffff濚™™™™™é?š™™™™™é?ø?à?ÍÌÌÌÌÌì¿à?à?š™™™™™¹¿š™™™™™ñ?ffffffæ?333333ã?ð?š™™™™™¹¿ffffffæ?š™™™™™Ù?š™™™™™É?ÍÌÌÌÌÌì?333333ã?ð?š™™™™™é?š™™™™™Ù?š™™™™™Ù?à?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™Ù?à?333333ã?333333ã?š™™™™™Ù?š™™™™™é?ffffffæ?ð?333333ã?š™™™™™é?ÍÌÌÌÌÌì?ffffffæ?333333ã?š™™™™™é?ffffffæ?à¿à?š™™™™™é?š™™™™™Ù?ffffffþ?š™™™™™Ù?333333ã?333333@ÍÌÌÌÌÌ@333333 @@333333@ffffff @ÍÌÌÌÌÌô?š™™™™™é?ffffff@ð?333333Ó¿ÍÌÌÌÌÌì?à?333333 @ffffffþ?ÍÌÌÌÌÌ@ffffff@à?š™™™™™ñ?@ÍÌÌÌÌÌ@ffffffö?@333333ó?@@ÍÌÌÌÌÌ@ @333333û¿333333ó¿ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?@ffffffö?ð?š™™™™™ù?ffffffþ?333333@š™™™™™ @ø?š™™™™™@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ffffffö¿ÍÌÌÌÌÌì?ø?333333û?333333ã?ø?ÍÌÌÌÌÌì?ffffffö?š™™™™™ñ?333333ó?š™™™™™@š™™™™™É?ffffff@@333333ã?ffffffæ¿ffffffþ?ffffffö?ÍÌÌÌÌÌ@ffffff@š™™™™™ @333333 @š™™™™™ù?ÍÌÌÌÌÌì¿333333@@333333û?ffffff@333333@ÍÌÌÌÌÌ@333333û?ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ @ÍÌÌÌÌÌì¿ffffff@ÍÌÌÌÌÌì?333333ã?š™™™™™@š™™™™™ñ¿š™™™™™Ù?ffffff@ffffff@Àš™™™™™É?ÀÍÌÌÌÌÌô¿š™™™™™Ù?333333ó?š™™™™™@ffffffæ?ffffff@333333@ÍÌÌÌÌÌ@@š™™™™™Ù?333333ã¿ÍÌÌÌÌÌì?ð?ø?@ÍÌÌÌÌÌô?š™™™™™Ù?ÍÌÌÌÌÌü?333333 @333333ó?š™™™™™É?333333û?333333@ÍÌÌÌÌÌü?š™™™™™@333333û?333333@333333@333333û?333333ó?ø?333333@ð?ffffffþ?333333@ffffffö?ffffff@333333@š™™™™™@ø?333333ã?ÍÌÌÌÌÌ@š™™™™™Ù?ÍÌÌÌÌÌü?š™™™™™@ffffff @š™™™™™@@ffffffö?ÍÌÌÌÌÌì?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ffffffæ?ffffff@333333@333333@@333333@ffffffþ?ffffffþ?ÍÌÌÌÌÌô?š™™™™™@ffffff@333333@ÍÌÌÌÌÌ@@š™™™™™ @ÍÌÌÌÌÌô?333333ã?ffffffþ?š™™™™™ù? @š™™™™™É?ffffff@ÍÌÌÌÌÌ@š™™™™™@š™™™™™ñ?ÍÌÌÌÌÌ@ffffff@333333ó?š™™™™™é?333333ã?š™™™™™ñ?@ÍÌÌÌÌÌô?ffffff@ffffffæ?333333û?ÍÌÌÌÌÌ@333333@࿚™™™™™ù?ffffff@š™™™™™Ù?333333Ó¿333333Ó?à?333333ã?à?ffffffþ¿à?ffffff @333333ó?333333ã¿ÍÌÌÌÌÌì¿@@ÍÌÌÌÌÌ@ffffff@ø¿š™™™™™é?333333Ó?ffffffö?333333Ó¿ffffff@š™™™™™@333333@@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @ÍÌÌÌÌÌ@333333 @š™™™™™é¿š™™™™™@à?ffffff@ÍÌÌÌÌÌÀ333333ã?š™™™™™ù?à?ffffffö?ffffff@@ÍÌÌÌÌÌ @ffffffþ?š™™™™™é?@ø?š™™™™™@ffffff@333333ã?à¿ÍÌÌÌÌÌ@333333ã?333333@ÍÌÌÌÌÌÀ@333333ó¿ffffffö?ÍÌÌÌÌÌ@š™™™™™ù?333333㿚™™™™™é?š™™™™™é?š™™™™™@š™™™™™É?š™™™™™ñ?š™™™™™@à?@333333@@š™™™™™Ù?333333ã?@333333 @š™™™™™Àš™™™™™Ù?à?ÍÌÌÌÌÌì?š™™™™™Ù?ffffff@ffffff@à?ffffffþ?š™™™™™é?š™™™™™é?š™™™™™@@333333@š™™™™™@333333@ffffff @à?333333Ó?333333Ó?ffffffþ?š™™™™™¹?@333333 @ø?333333@š™™™™™@š™™™™™ù?š™™™™™@333333Ó?ð?š™™™™™@ð?ÍÌÌÌÌÌô?à?333333@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌü¿ø?333333Àffffff@ffffff@ffffff@333333ã?ø?š™™™™™@ø?š™™™™™ À@ffffffæ?333333û?333333ã?@333333@à?ffffffþ?š™™™™™ñ?@š™™™™™é?š™™™™™ @333333@333333ã?ÍÌÌÌÌÌô?š™™™™™¹?š™™™™™¹?ffffffÀš™™™™™Àš™™™™™@ffffffö?š™™™™™@š™™™™™É¿333333ó¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@ @ÍÌÌÌÌÌ@ð?ffffff@š™™™™™@ffffff@333333ã?ÍÌÌÌÌÌü?ø?333333ó?š™™™™™é?@333333@333333ó?ÍÌÌÌÌÌü?š™™™™™ù?333333@š™™™™™@š™™™™™É?š™™™™™ñ¿ÍÌÌÌÌÌì¿ffffffþ?333333ó?333333 @š™™™™™é¿š™™™™™@ÍÌÌÌÌÌô¿š™™™™™ñ¿333333ó?š™™™™™Ù?ÍÌÌÌÌÌ@ffffff@š™™™™™@ffffffæ¿ÍÌÌÌÌÌô¿333333Àš™™™™™ù?333333û?à?333333Ó?333333 @@š™™™™™É?333333ó?ð?ÍÌÌÌÌÌü?@š™™™™™É?ð?333333û?š™™™™™ù?@ÍÌÌÌÌÌô?š™™™™™ù?ÍÌÌÌÌÌü?Àš™™™™™ñ¿333333û?ffffff@ÍÌÌÌÌÌì?ffffff@š™™™™™Ù?ÍÌÌÌÌÌü?@ffffff@ @ø?š™™™™™ @ @š™™™™™ @ @333333@ffffff@333333û?ffffff@333333Ó?@333333Ó?ÍÌÌÌÌÌü?š™™™™™@ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?333333ã?ffffffö?š™™™™™é?ÍÌÌÌÌÌì?333333@ÍÌÌÌÌÌü¿š™™™™™É?š™™™™™ @š™™™™™ù?ÍÌÌÌÌÌ@š™™™™™ @ @ð?š™™™™™Ù?ÍÌÌÌÌÌô?ÍÌÌÌÌÌü?š™™™™™ù?š™™™™™ @à¿ÍÌÌÌÌÌô¿ø?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?ffffffþ?@333333@š™™™™™é?ffffff@š™™™™™ @ÍÌÌÌÌÌü?@ð?š™™™™™ @Àš™™™™™ @333333û?€9@š™™™™™5@'@ffffffB@33333³5@š™™™™4@š™™™™™"@-@333333.@ÍÌÌÌÌÌ7@ffffff@ø¿ÍÌÌÌÌÌ@fffff¦G@ÍÌÌÌÌL=@3333334@ÍÌÌÌÌL9@fffffæ0@0@ÍÌÌÌÌÌ<@€2@333333%@ffffff3@3333330@fffffæA@š™™™™™@ÍÌÌÌÌLK@3333339@ffffff&@À333333#@š™™™™™,@ffffff&@3333335@ @ÍÌÌÌÌÌ:@333333)@9@ffffff6@ÍÌÌÌÌL3@ÍÌÌÌÌL9@@ffffff5@š™™™™YF@ffffff @š™™™™™¹?!@33333³6@ÍÌÌÌÌL0@š™™™™™É?3333330@ffffff@3333332@fffff&I@š™™™™™3@š™™™™=@ffffff7@ÍÌÌÌÌÌ+@ÍÌÌÌÌÌ@@ÍÌÌÌÌL4@333333û¿ffffff.@ÍÌÌÌÌÌ/@ÍÌÌÌÌÌ@@D@&@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ;@fffffæD@š™™™™™@€7@ÍÌÌÌÌÌ5@š™™™™1@333333!@fffffæC@š™™™™5@fffffæ@ÀÍÌÌÌÌÌ&@2@6@fffffæ?@ÍÌÌÌÌÌ9@š™™™™™2@&@ffffff;@33333sA@@G@€1@fffff&E@33333³A@š™™™™™ñ¿ÍÌÌÌÌÌ@333333 @@ÍÌÌÌÌÌì?š™™™™™=@š™™™™™4@ÍÌÌÌÌL5@ÍÌÌÌÌÌÀ333333;@ÍÌÌÌÌL1@fffff¦L@333333-Àš™™™™ÙP@€<@fffffæ3@š™™™™™S@ffffff2@fffffæ=@fffff&@@š™™™™™4@š™™™™™(@ffffff)@€8@3@333333'@3333331Àffffff@ffffff@š™™™™™5@333333>@š™™™™™ÀÍÌÌÌÌÌÀÍÌÌÌÌL0@ffffff!@3333336@ÍÌÌÌÌÌ@ÍÌÌÌÌL>@ÍÌÌÌÌÌ)@333333&@@ÍÌÌÌÌÌ>@fffffæ5@š™™™™™'@ffffff'@?@ÍÌÌÌÌLC@ffffff&@ÍÌÌÌÌL5@š™™™™™@ÍÌÌÌÌŒ@@+@333333$@333333?@š™™™™™%@)@š™™™™7@3333333@!@š™™™™™(@333333/@;@š™™™™™@fffffæ>@€9@ÍÌÌÌÌÌ7@š™™™™=@š™™™™™.@F@ffffff@€?@ÍÌÌÌÌÌ,@fffffæ9@33333³=@33333³:@fffffæ;@@š™™™™™-@333333#@@ÍÌÌÌÌÌ%@333333À333333*@fffffæA@&@333333$@š™™™™>@š™™™™™4@ffffff7@ÍÌÌÌÌÌ)@š™™™™ÙB@5@ffffff#@333333'@333333Àffffff'@'@ÍÌÌÌÌÌÀfffff†T@š™™™™™!@ÍÌÌÌÌÌô¿š™™™™5@33333³D@ffffff:@š™™™™™*@ÍÌÌÌÌÌ.@ÍÌÌÌÌLA@ÍÌÌÌÌ D@?@ÍÌÌÌÌÌ@@ffffff"@3@ffffffæ?33333³7@ÍÌÌÌÌÌ"Àš™™™™™>@š™™™™™ñ¿€I@@G@33333ó@@ÍÌÌÌÌÌÀÍÌÌÌÌÌ&À333333û¿33333³=@š™™™™™*@ÍÌÌÌÌÌ%@ÍÌÌÌÌŒB@ð?CÀ33333³1@@333333.@ÍÌÌÌÌÌ<@ÍÌÌÌÌÌ:@333333)@ffffff'@ffffff;@33333³6@3333330@ÍÌÌÌÌLD@fffffæ1@ÍÌÌÌÌL>@š™™™™7@333333K@€F@fffffæ;@š™™™™1@€4@ÍÌÌÌÌÌ>@33333³?@4@š™™™™™@ÍÌÌÌÌÌ7@!@C@ffffff@š™™™™™E@fffffæ0@ffffffÀffffff@ÍÌÌÌÌÌÀfffff&@@ÍÌÌÌÌÌ$Àš™™™™™@"Àffffff4@fffffæ4@ÍÌÌÌÌÌÀffffff(@ffffff=@ÍÌÌÌÌÌ,@@33333³=@€3@333333(@ffffff*@ÍÌÌÌÌÌ/@333333ã¿,@ÍÌÌÌÌÌ@7@333333=@ÍÌÌÌÌÌ$@!@(@333333"@33333³;@š™™™™5@ÍÌÌÌÌL:@š™™™™A@š™™™™™(@ÍÌÌÌÌÌ.@ffffff+@š™™™™™@333333@š™™™™™1@33333ó@@ffffff-@ÍÌÌÌÌÌ,@@@š™™™™YH@D@fffffæ2@ÍÌÌÌÌŒB@33333sE@š™™™™>@ Àš™™™™>@ffffff4@š™™™™YH@ffffffÀ333333?@š™™™™™À#@ÍÌÌÌÌÌ@@ø¿š™™™™ÙA@;@333333)@š™™™™™A@ffffff @ÍÌÌÌÌÌ>ÀÀ€5@3333335@š™™™™™'Àffffff+@ÍÌÌÌÌL6@ÍÌÌÌÌÌô¿ffffff @ÍÌÌÌÌÌ,@ÍÌÌÌÌL2@333333A@ÍÌÌÌÌÌ@š™™™™4@333333+@fffffæ:@ÍÌÌÌÌÌ:@ÍÌÌÌÌÌ@š™™™™2@€5@33333³:@33333sD@ÍÌÌÌÌL:@4@ÍÌÌÌÌÌ.@ffffff7@ÍÌÌÌÌL:@333333)@š™™™™™6@ffffff @333333-@ffffff4@33333³<@fffffæ4@š™™™™™>@"@š™™™™™ÀÍÌÌÌÌÌ@š™™™™™A@3333337@/@ÍÌÌÌÌÌ5@ffffffLÀ333333%@ÍÌÌÌÌÌ&ÀÍÌÌÌÌLC@ÍÌÌÌÌÌD@ffffff<@33333óB@ÍÌÌÌÌÌÀš™™™™™¹¿ÍÌÌÌÌÌ&@@ffffff@@ffffff3ÀfffffæB@ffffff'@333333%@D@@fffffæ4@,@š™™™™™7@ÍÌÌÌÌÌ#@š™™™™™2À@š™™™™3@ffffff@ffffff À/@333333C@3333335@ffffff<@ÍÌÌÌÌÌA@ÍÌÌÌÌL8@33333³2@fffff¦E@ÍÌÌÌÌL6@@ÍÌÌÌÌÌ4@š™™™™™>@š™™™™YI@333333À333333@À€0@ffffff/@ÍÌÌÌÌÌ)Àš™™™™™ù¿š™™™™0@š™™™™™;@.@fffff&@Àfffffæ@@ÍÌÌÌÌÌ2@@A@š™™™™YJ@ffffffÀÍÌÌÌÌL2@š™™™™™6@€;@ÍÌÌÌÌŒE@ÍÌÌÌÌÌ À33333óE@š™™™™™2@333333>@*@ffffff@ffffff@š™™™™:@@fffffæ@@š™™™™™@3333338Àfffffæ8@š™™™™™8@ffffff"À3333334@33333s@@š™™™™1@ÍÌÌÌÌL>@ffffff2@š™™™™™+Àffffff&@3333339@3333338@ÍÌÌÌÌÌ3@33333³@@ÍÌÌÌÌÌ1@ÍÌÌÌÌÌ>@@K@ÍÌÌÌÌÌÀ33333³@@š™™™™2@333333 @ÍÌÌÌÌÌ4@ÍÌÌÌÌÌ Àš™™™™YI@ÍÌÌÌÌÌCÀ33333³=@33333³:@33333sO@33333³E@š™™™™H@Q@33333sH@ÍÌÌÌÌŒH@fffffæ2@fffffæ4@@H@š™™™™™=@š™™™™™ @ÍÌÌÌÌÌ@ffffff @š™™™™ùS@333333G@ÀD@ÍÌÌÌÌŒG@š™™™™™3@fffffæ9@ÍÌÌÌÌÌE@ÍÌÌÌÌ,P@ÍÌÌÌÌÌ5@fffffæB@ffffff<@fffffæL@3333336@ÍÌÌÌÌŒX@33333³L@š™™™™™ ÀÍÌÌÌÌL0Àfffffæ>@33333³=@š™™™™™D@ÍÌÌÌÌL=@333333-@fffff&C@€<@š™™™™9S@š™™™™K@ffffff>@š™™™™YD@ffffff,@333333A@š™™™™™8@ffffff+@ÍÌÌÌÌÌ"@33333³3@33333³:@š™™™™;@@:@š™™™™2@fffffæ=@š™™™™9R@ÍÌÌÌÌÌ4@fffffæH@š™™™™C@ffffff2@ffffff8@š™™™™ÙB@ÍÌÌÌÌÌ!@C@fffff&C@333333O@ÍÌÌÌÌLQ@ffffff8@ffffffþ¿ÍÌÌÌÌ H@fffffæN@2@ffffffE@33333óG@ÀA@333333:@fffff&V@ÍÌÌÌÌLF@-@333333@33333³D@;@ÍÌÌÌÌŒA@333333H@š™™™™™ @ÍÌÌÌÌÌ,@€E@ T@š™™™™™@€3@ffffff@ÍÌÌÌÌÌ3@@š™™™™™,@33333³<@ÍÌÌÌÌÌ&@33333sC@ffffffJ@G@š™™™™ÙG@š™™™™™Ù?3333336@ÍÌÌÌÌÌ8@33333óO@š™™™™™ Àš™™™™9]@€B@€7@33333ÓW@€J@F@33333ó@@ÍÌÌÌÌ @@ÍÌÌÌÌ D@š™™™™™9@33333SV@333333>@ÍÌÌÌÌL=@333333@ffffff-@)@€@@@G@333333@333333)@P@š™™™™1@fffff¦I@3333336@š™™™™ÙD@š™™™™™8@.@š™™™™A@ÍÌÌÌÌÌ@@ÍÌÌÌÌ C@33333sK@33333³D@ÍÌÌÌÌ H@š™™™™ùQ@€5@333333;@š™™™™5@33333³H@š™™™™0@ÍÌÌÌÌL>@ffffffI@fffff&I@ÀC@33333óC@333333A@€6@š™™™™™4@33333sA@fffffR@33333³;@ffffffG@š™™™™ÙJ@ÍÌÌÌÌLP@fffff&B@33333³1@33333sL@š™™™™™3@š™™™™N@ÍÌÌÌÌÌ.@33333ÓP@33333sL@@E@ÍÌÌÌÌÌ@@fffff&E@š™™™™™U@ffffff4@ffffff)@ÍÌÌÌÌÌ-@ffffffæ?fffffæI@fffff&F@š™™™™™E@333333*@fffff&D@ffffffA@š™™™™ÙE@ÍÌÌÌÌÌ@ffffffI@fffff¦G@+@ffffff#@333333-@š™™™™™.@333333@š™™™™ÙM@š™™™™™-@=@333333>@33333sC@ÍÌÌÌÌL2@€>@ffffff?@@O@33333³L@fffffæ3@D@333333&@ÍÌÌÌÌL=@š™™™™™ÀÍÌÌÌÌLN@ffffff.@@H@š™™™™8@fffffæT@ÀR@`P@ÍÌÌÌÌLD@ÍÌÌÌÌL;@ÍÌÌÌÌÌ!À€H@š™™™™2@ÍÌÌÌÌL<@š™™™™™)@ÍÌÌÌÌÌ@ÍÌÌÌÌL8Àffffff5@333333.@ÍÌÌÌÌŒE@ÍÌÌÌÌÌL@ÍÌÌÌÌLN@š™™™™<@š™™™™0@€H@ÀC@33333³C@fffff&P@š™™™™5@ÍÌÌÌÌÌ8@@H@ÍÌÌÌÌLM@333333T@333333&ÀÍÌÌÌÌ F@333333(@š™™™™E@ÀF@333333@@333333Ó¿=@)@33333sJ@š™™™™™@ÍÌÌÌÌLM@ÍÌÌÌÌÌ?@ÍÌÌÌÌÌÀ€6@ffffff+@š™™™™YH@ÍÌÌÌÌÌÀffffff.@ÍÌÌÌÌÌ@ÍÌÌÌÌÌH@š™™™™™!À@333333-@333333C@€0@š™™™™E@ÍÌÌÌÌŒG@3333336@33333³9@3333331@ÍÌÌÌÌL6@4@ÍÌÌÌÌÌ<@ÍÌÌÌÌL;@ÀE@ÍÌÌÌÌ Q@ÍÌÌÌÌ C@š™™™™™(@ÍÌÌÌÌÌ+@&@š™™™™ÙD@ffffff6@š™™™™YD@fffffP@€<@333333M@fffff&A@ÍÌÌÌÌÌ1@33333³7@ÍÌÌÌÌL4@33333óC@ÍÌÌÌÌŒ@@š™™™™™4@š™™™™0@%@@R@š™™™™yU@ÍÌÌÌÌ G@ÍÌÌÌÌÌ%@ÍÌÌÌÌ I@ffffff!@fffffæC@š™™™™YK@33333óM@fffff¦K@@š™™™™™H@š™™™™™"@333333,À@W@š™™™™™@ÀF@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@@ÀQ@š™™™™™)@333333#Àš™™™™™@@H@š™™™™:@333333 @C@š™™™™9@š™™™™™"@ffffff@š™™™™™.@š™™™™™@š™™™™™Ù?fffffæ=@33333³<@ffffff=@ÍÌÌÌÌÌ7@š™™™™™-@333333$Àš™™™™YF@33333³F@ Q@33333sR@33333T@333333<@333333@@ffffffC@fffffQ@š™™™™™0@333333A@ffffff-@š™™™™9@š™™™™;@fffffÆQ@ÍÌÌÌÌŒM@ÀB@ffffff8@ffffff$@ÍÌÌÌÌÌ9@š™™™™™J@š™™™™:@@33333³1@fffffæBÀfffffæ3@€4@33333³=@ÍÌÌÌÌ,U@ÍÌÌÌÌL2@<@ffffff*@ÍÌÌÌÌÌ@€?@33333³;@ÍÌÌÌÌŒL@€9À7@ÍÌÌÌÌL5À33333³7@fffff&M@%@š™™™™™8@33333óF@š™™™™S@333333&@À(@š™™™™Y@@ÍÌÌÌÌL@@333333À33333³7@J@ÍÌÌÌÌLA@€P@33333³E@ffffffE@33333³@@33333³5Àš™™™™™+@3333333@À\@ÍÌÌÌÌ B@33333ÓR@333333ó¿š™™™™™ñ¿ÍÌÌÌÌ F@33333³D@33333³4@333333!@ÍÌÌÌÌŒJ@fffff&L@@E@3333330ÀffffffS@š™™™™ÙD@N@àS@š™™™™™É¿@G@š™™™™9@š™™™™ÙE@fffff&P@ÍÌÌÌÌÌ$@33333³K@ÍÌÌÌÌÌ6@š™™™™ÙB@ÍÌÌÌÌÌ3@ @33333sC@&@333333!@33333³L@ÍÌÌÌÌL2@33333“T@333333N@ÍÌÌÌÌÌM@ÍÌÌÌÌÌ@fffffæ6@33333óF@>@ÍÌÌÌÌ E@š™™™™ù_@š™™™™0Àš™™™™™ñ?ÀB@ÍÌÌÌÌÌH@š™™™™™A@33333³D@33333³>@fffffæJ@fffffÆR@@M@ÍÌÌÌÌ I@ÍÌÌÌÌÌ1@€H@333333ã¿ T@ÍÌÌÌÌÌOÀ€N@33333sC@333333Ó¿à¿ÍÌÌÌÌÌì?š™™™™™¹?š™™™™™Ù¿š™™™™™Ù¿333333Ó¿š™™™™™¹?࿚™™™™™Ù¿š™™™™™¹?š™™™™™É¿333333㿚™™™™™¹?š™™™™™Ù¿333333ã¿à¿333333Ó¿š™™™™™Ù¿š™™™™™¹¿ffffffæ?333333ã¿ffffffæ¿333333Ó¿š™™™™™Ù¿ffffff濚™™™™™¹?š™™™™™É¿à¿š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿š™™™™™É¿š™™™™™É?š™™™™™Ù¿š™™™™™É¿333333Ó¿š™™™™™Ù¿à¿333333Ó¿š™™™™™É?333333Ó¿333333ã¿333333Ó¿š™™™™™É¿333333Ó¿ffffff濚™™™™™Ù¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹¿ÍÌÌÌÌÌì¿333333ã¿333333ã?š™™™™™Ù¿š™™™™™Ù¿š™™™™™É¿š™™™™™É¿š™™™™™Ù¿333333Ó¿ÍÌÌÌÌÌ쿚™™™™™Ù?š™™™™™¹¿š™™™™™¹¿š™™™™™é¿š™™™™™É¿š™™™™™É¿ÍÌÌÌÌÌô?à¿ð¿ffffff濚™™™™™Ù¿š™™™™™É¿š™™™™™é¿à¿ffffffæ¿À333333ó¿š™™™™™¹¿ÀÀš™™™™™¹?š™™™™™¹¿à?š™™™™™É¿š™™™™™Ù?š™™™™™¹¿š™™™™™¹¿š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹¿ÍÌÌÌÌÌô¿à¿š™™™™™Ù¿ffffff濚™™™™™É¿ø¿ffffffæ¿333333㿚™™™™™¹¿š™™™™™¹?333333ã?࿚™™™™™¹?࿚™™™™™É¿333333ã¿ð¿333333ã¿333333Ó?š™™™™™É¿š™™™™™¹?š™™™™™¹?333333㿚™™™™™É?à¿ð¿š™™™™™É¿š™™™™™É¿š™™™™™¹¿š™™™™™é¿š™™™™™É¿š™™™™™¹¿š™™™™™É?à?š™™™™™¹¿š™™™™™É?š™™™™™¹?333333Ó¿333333Ó¿š™™™™™¹¿333333㿚™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™É?š™™™™™¹¿ÍÌÌÌÌÌì¿ffffff濚™™™™™¹¿š™™™™™¹¿333333Ó¿š™™™™™Ù¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿š™™™™™¹¿š™™™™™¹¿š™™™™™Ù¿ffffff濚™™™™™Ù¿š™™™™™Ù¿š™™™™™É¿š™™™™™É¿š™™™™™É¿š™™™™™É¿š™™™™™Ù¿š™™™™™¹¿333333Ó¿333333ã¿333333㿚™™™™™Ù¿ffffffæ¿à¿š™™™™™¹¿š™™™™™é?š™™™™™É¿à¿333333À𿚙™™™™¹?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ쿚™™™™™É¿š™™™™™Ù¿š™™™™™É¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™¹¿333333㿚™™™™™É¿š™™™™™¹?š™™™™™É¿š™™™™™¹¿š™™™™™¹¿ÍÌÌÌÌÌü¿ffffffö¿š™™™™™Ù¿š™™™™™¹?ffffffæ¿ffffff濚™™™™™¹?š™™™™™¹?š™™™™™é¿š™™™™™Ù¿ÍÌÌÌÌÌì?ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™Ù¿š™™™™™¹¿š™™™™™É?š™™™™™É¿š™™™™™É¿š™™™™™É?ffffff濚™™™™™¹¿ffffffæ¿ffffffö¿ð¿š™™™™™Ù?333333ã¿à¿333333ã¿ffffffæ¿ð¿š™™™™™Ù¿ð¿š™™™™™¹?à¿333333Ó¿š™™™™™É?š™™™™™¹?š™™™™™É¿š™™™™™¹¿333333Ó¿š™™™™™¹?ÍÌÌÌÌÌü¿š™™™™™É¿š™™™™™É¿š™™™™™É¿š™™™™™Ù¿š™™™™™Ù?š™™™™™Ù¿ffffff濚™™™™™¹¿à¿š™™™™™¹?š™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™¹?š™™™™™Ù¿à¿à¿š™™™™™é¿š™™™™™É¿š™™™™™É?š™™™™™É¿š™™™™™¹?š™™™™™É¿š™™™™™É¿333333Ó¿š™™™™™É¿š™™™™™¹¿š™™™™™¹¿š™™™™™É¿ð¿š™™™™™¹?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ쿚™™™™™é¿333333ó?š™™™™™É?š™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™¹¿333333㿚™™™™™É¿333333ó¿š™™™™™¹?š™™™™™¹?Àš™™™™™¹?ÍÌÌÌÌÌì¿ffffff濚™™™™™¹?333333Ó¿333333Ó¿ffffffþ?š™™™™™¹?š™™™™™¹¿à¿333333㿚™™™™™É¿ð¿ffffffþ¿š™™™™™¹?à¿333333ã¿ð¿š™™™™™ñ¿ð¿š™™™™™¹¿š™™™™™Ù¿š™™™™™¹?ffffffæ¿333333ã?š™™™™™¹?ÍÌÌÌÌÌ쿚™™™™™¹?š™™™™™¹¿š™™™™™¹¿š™™™™™É¿š™™™™™¹?š™™™™™Ù¿š™™™™™Ù?š™™™™™Ù¿š™™™™™¹?333333㿚™™™™™¹¿ÍÌÌÌÌÌ쿚™™™™™Ù¿ø¿š™™™™™Ù¿ø?š™™™™™Ù¿333333Ó¿333333ã¿ð¿š™™™™™ñ¿š™™™™™é¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™É¿ÍÌÌÌÌÌì¿ð¿š™™™™™é¿ÍÌÌÌÌÌô¿š™™™™™¹?à¿333333Ó¿333333Ó¿š™™™™™Ù¿à?š™™™™™¹¿à¿ÍÌÌÌÌÌ쿚™™™™™É?333333㿚™™™™™É¿333333Ó¿š™™™™™É¿ffffffæ¿333333ã¿333333ã¿ffffffÀø¿š™™™™™¹?š™™™™™É¿š™™™™™é¿š™™™™™É¿333333ó¿š™™™™™É?à?š™™™™™¹¿à¿š™™™™™¹?333333Ó¿š™™™™™@š™™™™™É¿š™™™™™É?ffffffö¿š™™™™™Ù¿à¿333333㿚™™™™™é¿ffffffæ¿ø¿à¿333333Ó¿ÍÌÌÌÌÌ쿚™™™™™ñ¿333333Ó¿ffffffö?š™™™™™é¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™É¿š™™™™™é?š™™™™™É?š™™™™™¹?333333Ó¿333333Ó¿333333㿚™™™™™¹?š™™™™™¹?š™™™™™É¿š™™™™™¹¿à?333333㿚™™™™™Ù¿333333ӿ𿚙™™™™¹¿š™™™™™Ù¿š™™™™™¹?š™™™™™Ù¿à¿333333ó?š™™™™™É?333333Ó¿333333ã¿333333ã¿à¿ÍÌÌÌÌÌì¿333333ã¿333333ã¿333333ã¿à¿333333ã¿ffffffæ¿ð¿ÍÌÌÌÌÌ쿚™™™™™ù¿à¿à¿à¿š™™™™™Ù¿š™™™™™Ù¿ffffffæ¿ø¿ffffffþ¿à¿333333ã¿ÍÌÌÌÌÌÀ𿚙™™™™ù¿ÍÌÌÌÌÌô¿š™™™™™Ù¿333333û¿ffffffþ¿à¿š™™™™™ñ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿333333㿚™™™™™Ù¿à¿š™™™™™Ù¿š™™™™™é¿ffffffæ¿333333ó¿š™™™™™Ù¿333333ã¿ÍÌÌÌÌÌü¿š™™™™™é¿š™™™™™Ù¿333333ã¿ffffff濚™™™™™Ù¿š™™™™™Ù¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿333333ó¿š™™™™™Ù¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿ffffffæ¿333333û¿š™™™™™ñ¿ffffffæ¿ffffffæ¿ÍÌÌÌÌÌ쿚™™™™™Ù¿à¿ÍÌÌÌÌÌô¿š™™™™™Ù¿š™™™™™Ù¿š™™™™™Ù¿ð¿333333㿚™™™™™é¿ffffffÀÍÌÌÌÌÌì¿333333ã¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿ffffffæ¿ffffff濚™™™™™ñ¿ffffffæ¿à¿333333ã¿333333Àffffff濚™™™™™Ù¿333333ã¿à¿333333ó¿ø¿š™™™™™ñ¿333333ó¿333333ã¿ffffffÀ࿚™™™™™é¿š™™™™™Ù¿š™™™™™é¿ffffffæ¿ffffffæ¿333333ã¿ð¿à¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿à¿š™™™™™ñ¿ffffffæ¿333333㿚™™™™™Ù¿333333ã¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌü¿ Àš™™™™™Ù¿š™™™™™é¿š™™™™™é¿ð¿333333ã¿à¿š™™™™™é¿ð¿à¿ffffffþ¿š™™™™™Ù¿š™™™™™Ù¿ffffffþ¿ Àš™™™™™Ù¿š™™™™™ñ¿à¿š™™™™™Ù¿ffffff濚™™™™™Ù¿333333 Àà¿333333㿚™™™™™Ù¿ÍÌÌÌÌÌ쿚™™™™™Ù¿š™™™™™Ù¿"@!@#@ffffff(@333333*@ffffff(@@ffffff @@š™™™™™%@"@š™™™™™@333333@š™™™™™(@ÍÌÌÌÌÌ(@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ"@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333"@š™™™™™ @ÍÌÌÌÌÌ!@ÍÌÌÌÌÌ@333333+@š™™™™™@š™™™™™.@ffffff)@š™™™™™@333333@@š™™™™™@333333 @ÍÌÌÌÌÌ%@333333@ffffff$@ÍÌÌÌÌÌ&@ffffff&@ffffff&@ÍÌÌÌÌÌ@š™™™™™%@ffffff@333333!@ÍÌÌÌÌL0@@333333@ffffff@#@š™™™™™@@ffffff@333333@ÍÌÌÌÌÌ(@€0@ffffff%@"@333333$@ffffff @š™™™™™)@$@ffffff@#@ffffff@)@,@ÍÌÌÌÌÌ@333333@333333(@/@333333@ÍÌÌÌÌÌ@š™™™™™@!@ @ffffff&@š™™™™™%@@š™™™™™-@333333&@š™™™™™"@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌ@š™™™™™@!@(@ÍÌÌÌÌÌ.@š™™™™™!@333333$@@@š™™™™™@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ"@333333*@!@ffffff@š™™™™™'@@*@ffffff$@ÍÌÌÌÌÌ2@ffffff@%@333333*@ffffff"@ÍÌÌÌÌÌ)@š™™™™™!@#@333333#@ @ÍÌÌÌÌÌ(@ÍÌÌÌÌÌ'@333333@@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ%@š™™™™™$@333333"@ÍÌÌÌÌÌ@333333@@ffffff#@333333@(@333333@ffffff!@@ÍÌÌÌÌÌ'@333333@&@ffffff@š™™™™™&@ÍÌÌÌÌÌ+@@ffffff$@š™™™™™@333333'@@ffffff@š™™™™™'@333333@š™™™™™@š™™™™™'@333333!@333333@š™™™™™ @@ÍÌÌÌÌÌ#@333333@ffffff!@333333!@ffffff#@333333'@ÍÌÌÌÌÌ"@ffffff!@ffffff@(@!@ÍÌÌÌÌÌ!@333333@333333#@ffffff!@@ÍÌÌÌÌÌ"@#@š™™™™™@ffffff @333333 @(@ffffff*@š™™™™™@@š™™™™™$@ÍÌÌÌÌÌ!@333333$@333333@)@@ffffff @333333@ffffff @ffffff@š™™™™™@š™™™™™@š™™™™™7@ffffff@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ+@ffffff&@333333 @ffffff@ÍÌÌÌÌÌ)@333333&@š™™™™™&@ffffff0@ffffff@@š™™™™™@#@@š™™™™™#@333333 @š™™™™™$@ÍÌÌÌÌL1@ÍÌÌÌÌÌ$@333333!@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ(@ÍÌÌÌÌÌ@333333@333333)@š™™™™™@333333%@@š™™™™™@ÍÌÌÌÌÌ%@333333*@@333333@š™™™™™#@ÍÌÌÌÌÌ @333333@š™™™™0@333333@ffffff!@333333!@333333,@ÍÌÌÌÌÌ.@333333%@š™™™™™@š™™™™™"@$@ffffff(@333333!@@š™™™™™%@@ffffff*@ffffff$@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌ%@š™™™™™@ffffff@ffffff!@#@ffffff@#@333333@ffffff @š™™™™™,@š™™™™™@333333@&@š™™™™™@š™™™™™@333333&@ffffff'@@ffffff@333333"@333333@ÍÌÌÌÌÌ#@š™™™™™@ffffff!@333333#@ÍÌÌÌÌÌ@333333@ffffff&@@š™™™™™#@333333#@333333&@ffffff$@ffffff@%@333333@š™™™™™@ffffff@333333 @š™™™™™(@333333@ÍÌÌÌÌÌ@333333@@ffffff*@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ#@ÍÌÌÌÌÌ*@ÍÌÌÌÌÌ!@ffffff @š™™™™™!@ffffff%@ffffff@@ffffff@ÍÌÌÌÌÌ @š™™™™™'@š™™™™™,@333333@333333@š™™™™™.@š™™™™™@ÍÌÌÌÌÌ"@*@%@š™™™™™@ffffff%@!@@ÍÌÌÌÌÌ@!@š™™™™™&@ffffff @$@š™™™™™#@ÍÌÌÌÌÌ+@333333@ÍÌÌÌÌÌ @ @333333.@ffffff"@š™™™™™!@ÍÌÌÌÌÌ@€2@ffffff&@ffffff-@ffffff&@š™™™™™#@ÍÌÌÌÌÌ@333333$@ÍÌÌÌÌÌ @š™™™™™@333333*@ffffff@333333@š™™™™™@š™™™™™#@ffffff @ffffff$@@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ%@333333 @'@333333 @ð?333333&@ffffff@š™™™™™(@333333+@333333@ÍÌÌÌÌÌ'@ffffff @ffffff@ffffff$@ÍÌÌÌÌÌ@š™™™™™&@ÍÌÌÌÌÌ@)@š™™™™™$@ffffff#@š™™™™™ @333333@333333#@ffffff"@333333,@!@ÍÌÌÌÌÌ@ffffff@333333"@)@ÍÌÌÌÌÌ@š™™™™™"@ffffff*@ÍÌÌÌÌÌ"@ÍÌÌÌÌÌ'@333333-@š™™™™™"@š™™™™™%@(@ffffff/@ÍÌÌÌÌÌ@ffffff.@ÍÌÌÌÌÌ(@ffffff0@333333@ffffff@333333@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ'@€0@ÍÌÌÌÌÌ+@333333@333333'@@ffffff$@3333331@ffffff#@š™™™™™,@ÍÌÌÌÌÌ"@ffffff @š™™™™5@333333'@333333+@ÍÌÌÌÌÌ*@ÍÌÌÌÌÌ)@.@ffffff@ffffff(@-@ffffff@333333,@333333#@$@š™™™™™$@ffffff!@š™™™™™@ÍÌÌÌÌÌ@š™™™™™&@š™™™™™3@!@š™™™™1@š™™™™™@ffffff$@ffffff#@ÍÌÌÌÌÌ#@ffffff%@+@ffffff @š™™™™™0@ÍÌÌÌÌÌ&@ffffff@%@333333@ffffff@ÍÌÌÌÌÌ@333333@333333/@ÍÌÌÌÌL0@(@ÍÌÌÌÌÌ$@ÍÌÌÌÌÌì¿333333Àš™™™™™ÀÍÌÌÌÌÌ ÀffffffÀffffffþ¿ÍÌÌÌÌÌô¿333333û¿Àš™™™™™ÀÍÌÌÌÌÌÀš™™™™™é¿333333û¿ÍÌÌÌÌÌì¿ffffffö¿ÍÌÌÌÌÌü¿ð¿à¿š™™™™™ñ¿ÍÌÌÌÌÌü¿333333Àš™™™™™ù¿š™™™™™é¿š™™™™™é¿š™™™™™é¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ð¿ø¿ÍÌÌÌÌÌô¿ð¿š™™™™™Àš™™™™™ù¿š™™™™™é¿ÍÌÌÌÌÌÀš™™™™™é¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀ333333û¿ffffffæ¿ÍÌÌÌÌÌÀ333333À333333Àð¿ø¿ÍÌÌÌÌÌÀð¿ Àffffffö¿š™™™™™À333333Àø¿333333ã¿ÍÌÌÌÌÌÀ333333ó¿ÍÌÌÌÌÌÀÍÌÌÌÌÌ ÀÍÌÌÌÌÌì¿ÍÌÌÌÌÌÀ333333ó¿ffffff Àffffffþ¿Àš™™™™™ ÀÍÌÌÌÌÌì¿ffffffÀÍÌÌÌÌÌÀ333333㿚™™™™™é¿š™™™™™ù¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌü¿š™™™™™ù¿ÍÌÌÌÌÌü¿À333333ÀÍÌÌÌÌÌô¿ffffffæ¿333333ÀffffffÀà¿333333ó¿š™™™™™ÀffffffÀffffffö¿ø¿š™™™™™ù¿ÍÌÌÌÌÌô¿ffffffÀà¿À333333û¿Àš™™™™™é¿ffffffæ¿ÍÌÌÌÌÌü¿ø¿Àffffff ÀÀÀffffffþ¿À333333ó¿š™™™™™Àffffffö¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ÀÀš™™™™™é¿333333ó¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿š™™™™™Ù¿333333ó¿ÀÀ333333 Àffffffþ¿š™™™™™ù¿ÍÌÌÌÌÌì¿333333û¿ð¿ÍÌÌÌÌÌü¿333333À333333Àà¿333333ã¿333333 Àš™™™™™ñ¿ffffffÀš™™™™™ Àø¿à¿ÍÌÌÌÌÌÀÍÌÌÌÌÌô¿ÍÌÌÌÌÌì¿ffffffÀ333333ó¿š™™™™™ñ¿š™™™™™ÀÀffffffö¿ð¿š™™™™™é¿333333ã¿à¿ÍÌÌÌÌÌÀø¿š™™™™™ñ¿ÍÌÌÌÌÌÀš™™™™™ñ¿ÍÌÌÌÌÌÀÀš™™™™™Àš™™™™™À333333ÀffffffÀffffffÀÍÌÌÌÌÌÀš™™™™™é¿333333ã¿333333ó¿333333û¿ffffffÀš™™™™™Àš™™™™™é¿333333ÀÍÌÌÌÌÌÀ333333Àš™™™™™ÀÀffffffæ¿à¿333333ó¿š™™™™™ù¿ÍÌÌÌÌÌÀ333333ã¿ffffff濚™™™™™ñ¿ffffff ÀÀÍÌÌÌÌÌÀffffffö¿ð¿à¿333333ã¿À333333ã¿ÍÌÌÌÌÌÀ333333ó¿333333ã¿ÍÌÌÌÌÌì¿333333ã¿ÍÌÌÌÌÌÀffffffÀÀffffffþ¿ Àš™™™™™Àš™™™™™ù¿333333ó¿Àš™™™™™Ù¿š™™™™™é¿333333Àffffff ÀÍÌÌÌÌÌ À333333û¿š™™™™™ñ¿Àš™™™™™ù¿ÍÌÌÌÌÌü¿ÍÌÌÌÌÌì¿ÍÌÌÌÌÌô¿š™™™™™é¿333333ó¿333333ã¿ÍÌÌÌÌÌü¿ffffffö¿333333Àffffffþ¿333333ó¿ÍÌÌÌÌÌô¿ÍÌÌÌÌÌô¿š™™™™™ñ¿ÍÌÌÌÌÌô¿333333ÀffffffÀÀffffffþ¿333333 ÀffffffÀø¿Àš™™™™™ñ¿š™™™™™ñ¿333333Àš™™™™™ Àš™™™™™ñ¿333333û¿š™™™™™é¿ÍÌÌÌÌÌü¿š™™™™™Àš™™™™™ÀÀš™™™™™ù¿ÍÌÌÌÌÌ À333333ÀÍÌÌÌÌÌÀš™™™™™ù¿ÀÍÌÌÌÌÌÀš™™™™™Ù¿333333Àffffffþ¿Àffffffæ¿333333ÀÀ ÀÍÌÌÌÌÌü¿ffffffö¿333333ó¿.Àffffffæ¿À333333û¿š™™™™™ù¿333333Àš™™™™™ù¿ffffffö¿ÀÍÌÌÌÌÌô¿333333ÀffffffÀš™™™™™Àš™™™™™À ÀÍÌÌÌÌÌÀš™™™™™é¿à¿ Àš™™™™™Ù¿ÍÌÌÌÌÌÀÍÌÌÌÌÌÀffffffÀš™™™™™Àffffffæ¿333333ã¿333333ó¿ffffffæ¿333333 ÀÍÌÌÌÌÌì¿ffffffÀÀÍÌÌÌÌÌô¿333333Àffffffæ¿Àš™™™™™ù¿š™™™™™<Àffffffæ¿ÍÌÌÌÌÌü¿à?à?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™É?333333Ó?š™™™™™Ù?š™™™™™¹¿ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™¹?333333ã?š™™™™™é?š™™™™™é?à?š™™™™™é?š™™™™™Ù?333333Ó?333333ã?333333ã?333333Ó?š™™™™™Ù?à?333333ã?š™™™™™é?à?š™™™™™Ù?à?š™™™™™Ù?333333ã?333333Ó?š™™™™™Ù?à?ffffffæ?š™™™™™Ù?ffffffæ?ffffffæ?ÍÌÌÌÌÌì?333333ã?à?ð?ÍÌÌÌÌÌì?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™Ù?333333ã?ffffffæ?š™™™™™É?š™™™™™Ù?š™™™™™Ù?ffffffæ?ffffffæ?à?à?š™™™™™Ù?š™™™™™Ù?à?à?ffffffæ?š™™™™™Ù?š™™™™™é?ffffffæ?š™™™™™é?333333ã?š™™™™™¹¿à?333333ã?333333Ó¿š™™™™™é?ffffffæ?333333ã?š™™™™™É?ffffffæ?333333ã?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™Ù?ffffffö?ÍÌÌÌÌÌì?à?ð?š™™™™™É¿š™™™™™é?š™™™™™É?š™™™™™¹?333333Ó?333333ã?333333ó?333333ã?333333Ó?ÍÌÌÌÌÌì?à?š™™™™™é?333333Ó¿333333ã?š™™™™™É?š™™™™™Ù?à?š™™™™™é?ffffffæ?š™™™™™ñ?š™™™™™É?š™™™™™Ù?š™™™™™é?ffffffæ¿ffffffæ?333333Ó¿š™™™™™Ù?333333Ó¿ÍÌÌÌÌÌì?à?333333Ó?333333Ó?333333ã?333333Ó?333333ã?ffffffæ?à?ffffffæ?333333Ó?š™™™™™é?š™™™™™Ù?š™™™™™Ù?à?ffffffæ?š™™™™™é?à?333333ã?š™™™™™Ù?ð?ffffffæ?š™™™™™¹¿333333ã?š™™™™™é?š™™™™™é?333333ã?ÍÌÌÌÌÌô?333333ã?à?ÍÌÌÌÌÌô?š™™™™™Ù?333333ã?š™™™™™Ù?333333ã?ffffffæ?š™™™™™Ù?ÍÌÌÌÌÌì?ffffffæ?š™™™™™¹?333333ã?š™™™™™Ù?š™™™™™Ù?ffffffæ?ffffffæ?š™™™™™é?š™™™™™Ù?333333Ó?š™™™™™É¿ffffffæ?ð?à?à?š™™™™™Ù?ÍÌÌÌÌÌô?ffffffæ?333333ã?à?333333ã?333333ã?333333Ó?š™™™™™É¿333333Ó?à?333333Ó¿333333ã?333333Ó?ffffffæ?š™™™™™Ù?š™™™™™Ù?ffffffæ?ffffffæ?à?š™™™™™é?à?333333ã?š™™™™™É?š™™™™™¹¿ffffffæ?š™™™™™é?à?à?à?333333ã?à?333333@š™™™™™é?333333Ó?š™™™™™Ù?š™™™™™Ù?333333Ó?š™™™™™é?š™™™™™Ù?š™™™™™Ù?š™™™™™¹?à?333333ã?333333ã?ffffffæ?š™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌì?š™™™™™¹?ð?š™™™™™é?333333Ó?à?à?à?š™™™™™é?333333Ó?š™™™™™é?333333ã?š™™™™™é?333333Ó?333333Ó?ffffffæ?ÍÌÌÌÌÌì?š™™™™™Ù?à?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?333333Ó?333333ã?ffffffæ?ð?333333Ó?333333ã?š™™™™™é?š™™™™™É¿š™™™™™é?ð?š™™™™™Ù?333333ã?ffffffæ?ffffffæ?à?ffffffæ?à?š™™™™™Ù?333333ã?ffffffæ?š™™™™™Ù?à?à?ÍÌÌÌÌÌô?ð?333333ã?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™é?ffffffæ?333333Ó¿333333Ó?à?š™™™™™Ù¿š™™™™™Ù?333333Ó?š™™™™™Ù?à?ÍÌÌÌÌÌì?à?à?š™™™™™¹¿333333ã?à?š™™™™™Ù?à?333333Ó¿š™™™™™É?š™™™™™Ù?à?š™™™™™Ù?š™™™™™Ù?š™™™™™É?333333ã?š™™™™™é?à¿ffffffæ?333333Ó?333333Ó?š™™™™™é?š™™™™™Ù¿š™™™™™Ù?š™™™™™Ù¿à?à?ð?š™™™™™Ù?à?š™™™™™Ù?š™™™™™Ù?à¿333333Ó¿ÍÌÌÌÌÌì?ffffffæ?333333ã?ffffffæ?š™™™™™é?à?š™™™™™é?ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?š™™™™™ñ?333333ã?333333Ó?š™™™™™ñ?333333ã?à?š™™™™™Ù?š™™™™™Ù?ÍÌÌÌÌÌì?š™™™™™Ù?333333ã?ÍÌÌÌÌÌì?ð?š™™™™™¹¿à?à?333333ã?š™™™™™é?š™™™™™Ù?à?333333ã?à?š™™™™™é?à?333333ã?ffffffæ?à?š™™™™™Ù?ø?333333ã?333333Ó?š™™™™™¹?ffffffæ?333333Ó?ÍÌÌÌÌÌô?ffffffæ?à?š™™™™™Ù?š™™™™™É?333333Ó?ð?š™™™™™é?à?333333ã?à?333333Ó?š™™™™™Ù?à?333333ó?333333Ó?ffffffæ?š™™™™™É?333333ã?333333ã?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™é?ÍÌÌÌÌÌì?à?ffffffæ?ÍÌÌÌÌÌì?š™™™™™é?ffffffæ?š™™™™™Ù?š™™™™™é?333333Ó¿à?à?à?ffffffæ?333333ã?à?ffffffæ¿333333ã?333333ã?š™™™™™¹¿à?ÍÌÌÌÌÌì?333333ã?š™™™™™Ù¿š™™™™™Ù?333333Ó?š™™™™™é¿à?š™™™™™¹¿š™™™™™Ù?ÍÌÌÌÌÌô?à?š™™™™™Ù¿à?333333Ó?š™™™™™¹¿ð?ffffffæ?à?ÍÌÌÌÌÌì?š™™™™™É?ffffffæ?à?š™™™™™É?š™™™™™é?à?ð?ð?333333ã?à?š™™™™™Ù?333333Ó?ffffffæ?š™™™™™É?333333ã?ffffffæ?333333Ó?à?š™™™™™é?à?ð?à?š™™™™™é?ffffffæ?ffffffæ?à?à?à?š™™™™™Ù¿à?ffffffæ?š™™™™™Ù?ø?à?à?333333@ÍÌÌÌÌÌ@333333 @ÍÌÌÌÌÌ@333333@š™™™™™@ð?à?ÍÌÌÌÌÌ@ÍÌÌÌÌÌì?ÍÌÌÌÌÌì?ø?à?ÍÌÌÌÌÌ@333333û?333333û?ffffff@ffffffæ?à?ÍÌÌÌÌÌü?ffffff @š™™™™™ñ?333333û?š™™™™™ñ?@ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@š™™™™™ @ffffffö¿š™™™™™ñ¿š™™™™™ñ?ffffffö?@333333ó?š™™™™™é?š™™™™™ù?š™™™™™ù?333333@ffffff@ð?ffffffþ?ÍÌÌÌÌÌì?ø?š™™™™™ñ¿ffffffæ?ø?ÍÌÌÌÌÌô?à?š™™™™™ù?333333ã?ÍÌÌÌÌÌô?333333ó?333333ã?ÍÌÌÌÌÌ@333333Ó¿ÍÌÌÌÌÌ@ø?š™™™™™É?š™™™™™é¿š™™™™™ù?ÍÌÌÌÌÌì?333333@@@333333@š™™™™™ñ?333333Ó¿@š™™™™™@š™™™™™ù?ffffffþ?ffffffþ?ø?š™™™™™ù? @š™™™™™@š™™™™™À333333ã¿333333@à?à?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì¿333333Ó?ø?š™™™™™ Àš™™™™™À333333ã¿ffffff Àø¿333333ã?š™™™™™Ù?ffffff@ffffffæ?333333@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffffæ?à¿333333ã?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?š™™™™™ @ÍÌÌÌÌÌì?ffffff濚™™™™™Ù?ffffff@333333û¿333333Ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@ÍÌÌÌÌÌô?ffffff@333333û?ffffffþ?ÍÌÌÌÌÌ@š™™™™™ù?333333ó?333333ó?ÍÌÌÌÌÌü?ÍÌÌÌÌÌì?ÍÌÌÌÌÌô?@333333ó?ÍÌÌÌÌÌ@ffffffö?333333û?ð?333333Ó?@333333ã?333333ó?ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌô?š™™™™™ @333333ó?ð?ø?ÍÌÌÌÌÌü?ffffffæ?333333û?ffffff@@ÍÌÌÌÌÌ@ffffffö?ÍÌÌÌÌÌü?ffffffþ?ÍÌÌÌÌÌô?@333333@š™™™™™@ø?š™™™™™@ffffff@ffffffö?à?ÍÌÌÌÌÌô?ø?ffffff@š™™™™™¹?š™™™™™ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333ã?ÍÌÌÌÌÌ@333333@š™™™™™é?333333Ó?š™™™™™é?š™™™™™é?ffffff @š™™™™™ñ?@š™™™™™ñ?ø?ffffffþ?@333333ã¿333333ó?ÍÌÌÌÌÌ@š™™™™™Ù?ð¿333333Ó?š™™™™™Ù?ffffffæ?à?333333À𿚙™™™™@ffffffæ?ÍÌÌÌÌÌì?š™™™™™é¿ffffffþ?ø?ffffff@333333û?ÍÌÌÌÌÌü¿ø?š™™™™™Ù?š™™™™™ñ?333333Ó¿ @333333ó?ffffff@ffffff@š™™™™™ù¿@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffffö¿ø?࿚™™™™™@333333û¿ÍÌÌÌÌÌì?š™™™™™É¿333333Ó?ffffffö?ffffff@š™™™™™@@333333û?ÍÌÌÌÌÌì?š™™™™™ù?333333ó?333333û?ÍÌÌÌÌÌ@š™™™™™é?š™™™™™Ù¿š™™™™™@à?ÍÌÌÌÌÌ@ffffffö¿333333û?ÍÌÌÌÌÌì¿ð?@ffffffæ?š™™™™™é¿š™™™™™ù?333333Ó?@š™™™™™É?š™™™™™é?333333û?š™™™™™É?ÍÌÌÌÌÌô?ÍÌÌÌÌÌ@š™™™™™É?à?333333Ó?333333û?š™™™™™@ÍÌÌÌÌÌÀš™™™™™Ù¿š™™™™™Ù?333333ã?š™™™™™Ù?ffffff@ffffffþ?š™™™™™Ù?ffffffö?š™™™™™é?š™™™™™é?š™™™™™ù?ffffff@š™™™™™@@333333@ffffff@à?333333ã?š™™™™™Ù?š™™™™™ù?š™™™™™Ù?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@š™™™™™ñ?@ø?ffffffö?333333û?࿚™™™™™ñ?ffffffþ?ffffffæ?ffffffö?333333Ó?ffffffö?333333@ÍÌÌÌÌÌ@333333û¿ffffffö?š™™™™™À@š™™™™™ù?ÍÌÌÌÌÌ@ø¿ÍÌÌÌÌÌü?333333ó?ø?š™™™™™ À333333 @à?ð?š™™™™™É?ffffffö?333333@š™™™™™Ù?ø?333333ó?ffffff@333333ã?@ffffffþ?333333ã?š™™™™™é?š™™™™™¹¿ð¿333333ó¿š™™™™™Àø?ÍÌÌÌÌÌì?@š™™™™™ñ¿ÍÌÌÌÌÌì¿333333ó¿š™™™™™@333333û?@ffffffþ?ffffff@333333ó?ÍÌÌÌÌÌì?š™™™™™ù?ÍÌÌÌÌÌ@333333Ó¿333333ó?333333ó?š™™™™™ñ?ffffffæ?š™™™™™@ffffff@š™™™™™é?š™™™™™ñ?š™™™™™é?ÍÌÌÌÌÌô?š™™™™™ù?š™™™™™é¿333333ó¿ð?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌì?š™™™™™ù?ÍÌÌÌÌÌì¿ÍÌÌÌÌÌ@š™™™™™é¿š™™™™™é¿ø?š™™™™™É?ffffff@@ffffff@š™™™™™é¿333333ó¿ffffffþ¿ð?ÍÌÌÌÌÌÀ333333ã?333333Ó?ffffff@ffffff@š™™™™™É?š™™™™™é?ð?@ffffff@š™™™™™É?333333ã?ÍÌÌÌÌÌì?ffffffö?@ÍÌÌÌÌÌô?š™™™™™É?ffffffö?ÀÍÌÌÌÌÌô¿ø?@ð?ffffffþ?à?ffffffþ¿@ffffff@ø?ÍÌÌÌÌÌô? @@š™™™™™é?ÍÌÌÌÌÌ @@@š™™™™™ñ?ÍÌÌÌÌÌ@š™™™™™Ù?333333û?333333Ó?š™™™™™ñ?š™™™™™ÀÍÌÌÌÌÌ@š™™™™™ù?š™™™™™É?ÍÌÌÌÌÌì?š™™™™™Ù¿ð?ffffffþ?333333û¿š™™™™™Ù?@š™™™™™ù?@@ffffff@š™™™™™é?š™™™™™Ù?ÍÌÌÌÌÌ @ÍÌÌÌÌÌü?333333ó?@š™™™™™É¿š™™™™™Ù?ÍÌÌÌÌÌô?333333@ÍÌÌÌÌÌ@333333ã?333333û?ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?à?ffffffö?ffffff@ÍÌÌÌÌÌô?ffffff@333333ã?333333@ð?333333@š™™™™™ù?ÍÌÌÌÌL7@ffffff4@š™™™™™(@ÍÌÌÌÌLA@ffffff9@+@š™™™™™,@š™™™™™/@)@š™™™™4@š™™™™™(@š™™™™™@š™™™™™!@€<@š™™™™™C@3333331@€3@š™™™™1@3333332@3333339@š™™™™1@ffffff'@33333³3@ffffff2@fffff¦A@33333³4@F@ffffff9@3333331@ÍÌÌÌÌÌ @333333!@+@-@š™™™™:@š™™™™™'@ÍÌÌÌÌLA@ffffff,@€:@ÍÌÌÌÌL<@%@€7@333333 @ffffff0@33333óB@ÍÌÌÌÌÌ$@@š™™™™™'@ÍÌÌÌÌÌ6@fffffæ1@š™™™™™!@š™™™™™'@ÍÌÌÌÌÌ'@š™™™™™7@F@š™™™™™3@;@333333:@š™™™™2@33333³9@š™™™™™5@ÍÌÌÌÌÌü¿(@ffffff)@š™™™™>@ÍÌÌÌÌÌ@@š™™™™0@&@ffffff:@33333³E@ffffff@š™™™™:@6@€?@ffffff-@š™™™™=@333333.@š™™™™™ Àffffff-@ÍÌÌÌÌÌ:@š™™™™™7@š™™™™™?@fffffæ3@š™™™™4@ffffff@ÍÌÌÌÌÌ7@333333V@333333@@,@š™™™™YG@€F@ffffff$@333333#@š™™™™™@@ÍÌÌÌÌÌ@€3@ÍÌÌÌÌL5@š™™™™6@333333!@ÍÌÌÌÌL<@š™™™™™#@33333³G@ÍÌÌÌÌÌ"@333333L@€9@ÍÌÌÌÌL1@@F@ÍÌÌÌÌÌ4@š™™™™™=@ffffff:@33333³8@ÍÌÌÌÌÌ-@ÍÌÌÌÌÌ,@ffffff.@ffffff4@ffffff @3333338@ÍÌÌÌÌÌü?š™™™™™@3333336@ÍÌÌÌÌÌ<@333333@333333ó¿ÍÌÌÌÌL0@ÍÌÌÌÌÌ"@3333336@ÍÌÌÌÌÌ@ffffff:@(@ffffff9@ffffff@€<@33333³;@ffffff-@š™™™™™)@333333C@ÍÌÌÌÌL>@333333 @1@(@333333G@š™™™™™@333333&@ffffffF@333333&@ÍÌÌÌÌÌ"@š™™™™1@€1@ffffff"@ÍÌÌÌÌÌ$@ffffff$@3333331@š™™™™™É¿š™™™™™6@333333=@fffffæ:@ÍÌÌÌÌÌ;@š™™™™™3@ffffffA@ffffff#@š™™™™™8@š™™™™2@€@@š™™™™1@33333³6@š™™™™™6@ÍÌÌÌÌÌ@š™™™™0@š™™™™™'@ÍÌÌÌÌÌ%@fffffæ1@š™™™™™À333333(@š™™™™™7@333333)@333333'@fffffæ>@š™™™™™,@ÍÌÌÌÌL7@ffffff+@ÀB@ÍÌÌÌÌÌ2@ÍÌÌÌÌÌ,@š™™™™2@ffffff@@fffffæ1@333333@K@8@ÍÌÌÌÌÌì?€5@333333F@ffffff<@.@'@€?@€6@ÍÌÌÌÌ @@ÍÌÌÌÌ J@š™™™™™@ÍÌÌÌÌÌ3@š™™™™™@€6@33333³2@fffffæ6@ffffff@š™™™™ùP@š™™™™™D@š™™™™9@ @333333ó?ffffff@?@š™™™™™@333333&@fffffæ;@ @ÍÌÌÌÌÌ+À33333³1@ÍÌÌÌÌÌ @ffffff)@7@8@333333%@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌ7@š™™™™6@ÍÌÌÌÌÌ.@fffff¦B@ÍÌÌÌÌÌ9@š™™™™8@š™™™™2@ÍÌÌÌÌŒF@ÍÌÌÌÌLC@š™™™™:@fffffæ:@5@8@?@ÍÌÌÌÌL1@š™™™™™&@š™™™™™1@3333332@š™™™™YA@ffffff$@ÍÌÌÌÌÌD@3333332@333333ã?ffffff@ffffff@š™™™™™1@ÍÌÌÌÌÌ쿚™™™™2@ÍÌÌÌÌÌÀÍÌÌÌÌÌ2@À@@333333,@)@ffffff;@ffffff3@ø?š™™™™™:@9@ÍÌÌÌÌÌ(@'@fffffæ1@ @ffffff-@333333 @ffffff5@33333³6@333333$@ÍÌÌÌÌÌ#@3333330@fffffæ0@ÍÌÌÌÌŒD@ÍÌÌÌÌÌ9@ffffff<@š™™™™™8@.@š™™™™™.@ffffff)@333333@#@š™™™™™0@333333A@ÍÌÌÌÌÌ.@33333³1@ffffff0@333333*@ffffffA@33333³8@ÍÌÌÌÌÌ @š™™™™™?@ffffffC@333333>@š™™™™™é?8@33333³7@ffffff/@ffffff#@33333³3@š™™™™™É?0@ÍÌÌÌÌÌ8@ffffff*@€7@š™™™™YB@,@fffff¦D@33333³:@š™™™™™@ÍÌÌÌÌÌÀffffff6@š™™™™™,@š™™™™™'Àffffff/@š™™™™™/@ÍÌÌÌÌÌ!@š™™™™™@ÍÌÌÌÌÌ0@333333/@š™™™™™G@333333@€0@ffffff*@<@33333³9@,@ÍÌÌÌÌÌ-@€D@š™™™™6@ffffffE@š™™™™7@š™™™™3@333333#@3@š™™™™™1@š™™™™™5@33333³9@ffffff#@,@ÍÌÌÌÌÌ+@ffffff1@€=@€3@333333!@ffffffþ?ÍÌÌÌÌÌ@ffffffA@š™™™™™6@ffffff8@5@ffffff0Àffffff5@ÍÌÌÌÌÌ @fffffæD@333333?@ffffff8@š™™™™ÙA@š™™™™™8@'@0@ÍÌÌÌÌÌ"@333333=@š™™™™™!Àš™™™™™?@š™™™™™,@€4@fffffæH@333333"@4@š™™™™™4@8@.@(Àffffff!@ÍÌÌÌÌÌ4@ffffff"@333333㿚™™™™1@ÍÌÌÌÌÌC@6@š™™™™;@ffffffD@*@9@š™™™™™U@ÍÌÌÌÌL@@ÍÌÌÌÌÌ#@3333337@ÍÌÌÌÌL?@ÍÌÌÌÌLE@ @š™™™™™>@š™™™™™1@ffffff.@@ÍÌÌÌÌÌ@š™™™™™8@ÍÌÌÌÌL9@33333³>@ÍÌÌÌÌLBÀš™™™™8@€<@€:@ÍÌÌÌÌ H@ÍÌÌÌÌÌ@š™™™™<@fffffæ3@ffffff0@33333óH@à?ffffffD@€6@fffffæ9@ffffff:@ÍÌÌÌÌÌ@&@333333>@ÍÌÌÌÌÌ-@š™™™™Y@@ÍÌÌÌÌÌ"@š™™™™YB@š™™™™8@fffffæ4@333333À33333³7@š™™™™0@ffffff(@ffffff4@333333.@ffffffÀ8@š™™™™8@š™™™™3@š™™™™™9@š™™™™>@€5@fffffæ8@ÍÌÌÌÌ D@ffffffÀÍÌÌÌÌÌ1@333333,@!@š™™™™™1@š™™™™™ÀC@š™™™™FÀ9@ÍÌÌÌÌL5@33333³H@ffffffC@€H@ÍÌÌÌÌÌN@ffffffO@š™™™™?@€4@ÍÌÌÌÌÌ3@fffffæ>@š™™™™9@ÍÌÌÌÌL3@3333331@333333(@ÍÌÌÌÌLH@ÍÌÌÌÌ K@š™™™™?@ÍÌÌÌÌŒD@ÍÌÌÌÌÌ4@ffffff6@@C@š™™™™YK@š™™™™™3@fffff&A@fffffæ<@fffff¦L@333333=@š™™™™Q@ffffffK@@333333ÀÍÌÌÌÌL0@€9@ÀC@@@@3333330@333333G@š™™™™™;@ÍÌÌÌÌìP@33333sN@fffffæ1@333333B@3333330@33333³9@š™™™™6@ÍÌÌÌÌÌ,@(@€7@:@ffffff=@ffffff)@š™™™™5@33333³6@€<@ÍÌÌÌÌÌN@33333³1@š™™™™YG@€B@33333³3@ffffff.@€A@ÍÌÌÌÌÌ@ÍÌÌÌÌŒ@@š™™™™Y@@fffff&J@š™™™™ÙM@ÍÌÌÌÌÌ7@š™™™™™ @33333³E@ÍÌÌÌÌÌM@3@@D@fffff&C@ÀE@ÍÌÌÌÌL?@ÍÌÌÌÌLN@€>@ÍÌÌÌÌLCÀffffff%@š™™™™™I@ffffff;@33333ó@@ÍÌÌÌÌLA@333333*@333333!@@@@š™™™™ÙE@333333 @"@@;@ffffff0@š™™™™™)@€;@ffffff%@ffffff?@ÍÌÌÌÌŒB@fffffæD@33333sF@ffffff/@š™™™™8@333333-@fffff¦J@fffffæ1@š™™™™ÙU@fffffæ>@ÍÌÌÌÌÌ%@š™™™™H@fffff¦H@333333#@ÍÌÌÌÌÌ<@ÍÌÌÌÌÌA@fffff¦@@š™™™™™:@ÍÌÌÌÌŒO@š™™™™™>@3333336@ÍÌÌÌÌLH@*@/@?@€D@ffffff+@ÍÌÌÌÌÌ @ffffffC@š™™™™™0@š™™™™™H@ffffff/@@A@š™™™™™3@33333³;@3333337@33333³?@C@€F@33333sC@š™™™™ÙG@33333óJ@€0@š™™™™™7@8@333333N@@ÍÌÌÌÌL9@ÍÌÌÌÌÌO@š™™™™YC@ÍÌÌÌÌL<@33333³=@fffffæ?@3333337@ffffff2@=@ÍÌÌÌÌÌE@š™™™™3@33333³@@fffffæH@š™™™™ÙL@33333óA@ÍÌÌÌÌÌ5@ÍÌÌÌÌÌE@fffffæ6@fffff&I@š™™™™™2@fffffæN@ÀA@fffffæB@3333339@š™™™™?@fffffÆQ@3@333333+@š™™™™8@š™™™™™¹?333333D@33333³?@ÍÌÌÌÌLB@fffffæ1@ÍÌÌÌÌÌC@333333;@ÍÌÌÌÌ B@333333@ffffffG@@C@3333332@ffffff'@ @@ÍÌÌÌÌL6@ÍÌÌÌÌÌ#@ffffff6@(@š™™™™5@fffffæ;@fffffæG@3333335@€>@ffffff8@ÍÌÌÌÌLJ@š™™™™Y@@ÍÌÌÌÌÌ2@fffffP@%@333333;@ø?K@À@@ÍÌÌÌÌŒB@:@š™™™™™F@ÍÌÌÌÌ P@š™™™™ÙH@ÍÌÌÌÌ E@š™™™™9@Àš™™™™F@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ;@ffffff @š™™™™™)@333333.Àfffffæ3@3333330@š™™™™ÙA@33333sH@ffffffJ@€8@ffffff3@ÍÌÌÌÌ D@ÀA@>@fffff¦K@ffffff?@33333³3@š™™™™™A@ÍÌÌÌÌLH@ P@!@@D@€0@š™™™™@@ÍÌÌÌÌLF@fffffæ5@š™™™™™ @>@ÍÌÌÌÌL4@fffff&H@š™™™™™)@ffffffJ@333333@@š™™™™™@ffffff/@š™™™™™5@ÍÌÌÌÌÌ2@ÍÌÌÌÌÌ@3333336@ÍÌÌÌÌÌ@33333³D@333333ã¿!@-@33333³@@€6@fffffæ<@33333³D@š™™™™;@5@/@ÍÌÌÌÌL8@€2@33333³?@fffffæ;@fffffæA@š™™™™ÙI@ffffff=@ffffff+@š™™™™4@€3@33333sJ@š™™™™?@fffff¦D@fffffæD@33333³9@fffff¦F@333333:@33333³0@33333³7@ffffff)@š™™™™™D@ÍÌÌÌÌL?@fffffæ5@ÍÌÌÌÌL;@ÍÌÌÌÌÌ-@@G@ÍÌÌÌÌŒH@ÍÌÌÌÌÌ:@333333@ÀF@,@€A@C@ÍÌÌÌÌÌG@š™™™™™é¿7@€=@333333-@š™™™™™À33333³L@ÍÌÌÌÌÌ1@š™™™™=@33333sC@33333³9@ÍÌÌÌÌìP@ÍÌÌÌÌL>@€3@š™™™™™@ÍÌÌÌÌŒE@ffffff1@333333û?š™™™™>@š™™™™™2@ÍÌÌÌÌÌ.@š™™™™™@š™™™™™@ÍÌÌÌÌÌ@€3@2@š™™™™5@š™™™™™;@ÍÌÌÌÌÌ%@33333³0@š™™™™™Ù?š™™™™YA@š™™™™™K@ÍÌÌÌÌÌG@ÍÌÌÌÌLN@ÍÌÌÌÌLO@33333³<@ÍÌÌÌÌÌ,@š™™™™Ù@@ÍÌÌÌÌ F@33333³3@š™™™™B@33333³5@š™™™™™7@33333³3@G@33333sL@33333³9@ÍÌÌÌÌL0@333333 @,@33333óG@š™™™™™%@ÍÌÌÌÌÌ)@ffffff<@ffffff9À333333>@ÍÌÌÌÌÌ5@ÍÌÌÌÌÌ?@K@3333332@€<@E@333333*@C@;@33333óH@ffffff*À2@ÍÌÌÌÌÌÀš™™™™;@333333;@)@33333³7@fffff&G@333333N@3333330@ÍÌÌÌÌÌÀ-@ffffffB@š™™™™@@@6@š™™™™ÙF@33333s@@fffff¦K@fffff&H@š™™™™™.@333333B@ÍÌÌÌÌÌ @€6@š™™™™™5@ffffffX@ÀB@fffff¦M@333333%@333333ÀÍÌÌÌÌLD@33333sD@3333334@ÍÌÌÌÌÌ&@ÍÌÌÌÌÌM@33333óH@33333³B@š™™™™4Àš™™™™ÙI@€H@€E@@R@ffffff&@F@ffffff6@ÍÌÌÌÌL:@š™™™™Ù@@ÍÌÌÌÌL1@š™™™™YI@3333338@33333s@@7@333333'@9@0@š™™™™1@ÍÌÌÌÌ L@ffffff7@š™™™™ù\@33333sJ@ÍÌÌÌÌLD@@ffffff:@š™™™™™H@š™™™™9@=@š™™™™U@š™™™™™ Àffffff9@š™™™™Ù@@fffff&B@š™™™™™E@€A@š™™™™Ù@@33333³D@ÀK@š™™™™™Ù?€=@ÍÌÌÌÌŒB@š™™™™™1@š™™™™™C@à¿333333O@š™™™™™@ÀÍÌÌÌÌLG@ffffff@@YÿmatrixþÿÿÿÄ @@$@ð?@,@ð?ð?ð?$@@ð?$@@ð?ð?$@ð?ð?ð?ð?ð?ð?ð?ð?0@,@$@@@@ð?ð?ð?@ð?ð?$@ð?@ð?ð?ð?ð?ð?@@ð?@ð?ð?ð?$@0@0@@ð?ð?@ð?@@ð?ð?ð?ð?@ð?@ð?@0@ð?ð?ð?$@@@ð?ð?ð?ð?@@@"@@@"@"@ð?@ð?@$@ð?$@ð?ð?ð?@ð?ð?ð?@@@ð?@ð?ð?ð?ð?ð?@ð?0@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?$@ð?ð?ð?ð?ð?ð?$@ð?ð?$@ð?ð?ð?@ð?ð?$@$@$@ð?ð?$@ð?ð?ð?@0@ð?ð?@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?@@ð?ð?ð?$@.@@ð?ð?ð?@ð?0@ð?@@@@@,@"@ð?ð?@@ð?ð?ð?0@0@ð?@ð?ð?ð?@ð?ð?"@ð?$@$@ð?@@@ð?@.@@0@ð?ð?@ð?$@@ð?0@ð?ð?ð?@ð?ð?ð?@ð?@ð?ð?ð?ð?ð?.@ð?@ð?ð?@ð?$@@@@ð?ð?@@@@ð?0@ð?ð?ð?ð?$@ð?ð?ð?ð?ð?@ð?ð?@ð?@$@ð?ð?ð?ð?@$@@ð?ð?ð?ð?ð?$@ð?$@@@@$@$@ð?.@ð?@@@ð?0@@@@ð?ð?$@$@ð?ð?@ð?ð?$@@ð?@@$@ð?$@@ð?@ð?ð?0@ð?$@ð?$@ð?ð?ð?0@$@ð?@$@ð?$@ð?@ð?ð?@ð?$@0@ð?@@@@@@0@$@@ð?@@@ð?"@ð?@ð?@@ @ð?ð?$@0@@ð?ð?@ð?0@@"@ð?ð?ð?ð?ð?ð?"@ð?$@@ð?$@,@ð?@ð?ð?ð?ð?ð?0@@@0@ð?ð?ð?ð?$@ð?ð?.@ð?ð?ð?"@ð?ð?$@ð?0@$@@$@@ð?ð?ð?ð?ð?ð?ð?ð?ð?$@ð?ð?ð?ð?$@@ð?ð?statistics-release-1.7.3/inst/datasets/carbig.mat000066400000000000000000001472611475240274700221000ustar00rootroot00000000000000Octave-1-L Accelerationÿmatrixþÿÿÿ–(@'@&@(@%@$@"@!@$@!@€1@'@&@%@&@$@ @ @#@$@.@/@/@0@-@€4@€1@-@€1@)@.@,@.@+@€2@-@/@,@3@4@*@/@/@/@/@(@'@+@*@'@(@(@+@3@.@-@,@,@€3@-@3@2@3@€4@/@1@€7@€3@€0@(@(@+@*@'@&@+@+@)@+@)@,@0@,@-@2@€3@2@0@1@-@.@€0@*@'@*@-@)@'@(@*@-@&@&@&@€0@2@0@€0@0@5@,@)@*@)@.@3@€3@€0@+@€2@,@/@*@#@€3@/@,@/@&@,@+@&@€0@1@0@1@3@€0@5@1@1@2@€0@,@-@+@0@/@€0@/@-@€0@3@-@/@,@.@/@0@0@0@5@€3@'@,@-@+@5@€2@3@3@.@+@(@0@1@0@€2@+@€0@1@-@,@1@.@1@-@+@€1@/@fffffæ0@ÍÌÌÌÌÌ-@33333³1@š™™™™™.@*@*@ÍÌÌÌÌÌ+@š™™™™™)@ÍÌÌÌÌÌ.@-@š™™™™™1@š™™™™™1@3333336@š™™™™6@ffffff,@ffffff1@33333³1@5@3333330@ÍÌÌÌÌÌ1@ffffff(@1@ffffff0@333333+@ffffff/@ffffff*@fffffæ5@/@33333³0@333333(@(@.@,@€2@š™™™™™-@š™™™™™2@/@ÍÌÌÌÌÌ0@)@3@ffffff+@ÍÌÌÌÌÌ-@ffffff0@fffffæ0@33333³1@3@333333&@ÍÌÌÌÌÌ&@ffffff(@-@-@0@3333332@š™™™™™/@1@ÍÌÌÌÌÌ/@ffffff0@333333,@-@š™™™™™)@+@€5@ÍÌÌÌÌÌ,@ffffff3@š™™™™™2@ffffff0@/@ffffff*@š™™™™™)@3333333@3333332@š™™™™™/@ÍÌÌÌÌÌ.@3333331@3333331@š™™™™™/@33333³0@33333³2@333333.@ffffff*@ÍÌÌÌÌÌ*@ffffff&@ffffff+@€0@ffffff,@ffffff-@-@š™™™™™-@33333³0@š™™™™™1@ÍÌÌÌÌÌ-@ÍÌÌÌÌÌ/@333333+@ffffff/@š™™™™™/@ÍÌÌÌÌÌ-@š™™™™™0@ÍÌÌÌÌÌ.@3333332@ÍÌÌÌÌL1@3333332@š™™™™™0@ÍÌÌÌÌÌ.@ÍÌÌÌÌÌ*@ffffff*@ffffff.@ÍÌÌÌÌÌ-@š™™™™™,@.@*@,@ffffff.@ÍÌÌÌÌÌ,@.@š™™™™4@ffffff1@ÍÌÌÌÌÌ8@3333336@ffffff*@ÍÌÌÌÌÌ-@3333333@ffffff-@0@š™™™™™&@ÍÌÌÌÌÌ)@ffffff*@ffffff-@ÍÌÌÌÌÌ2@/@ffffff0@€0@š™™™™2@š™™™™4@33333³2@š™™™™™/@/@€1@.@ffffff.@fffffæ1@ÍÌÌÌÌÌ,@3333333@33333³5@33333³7@fffffæ3@ÍÌÌÌÌÌ5@š™™™™™+@ÍÌÌÌÌL1@2@š™™™™™.@ÍÌÌÌÌÌ&@)@333333.@š™™™™™,@1@ffffff/@ffffff0@ÍÌÌÌÌÌ,@333333)@ÍÌÌÌÌÌ)@fffffæ0@ffffff0@š™™™™0@ÍÌÌÌÌÌ1@ffffff3@ÍÌÌÌÌL1@0@ÍÌÌÌÌÌ-@3333330@33333³4@ffffff,@š™™™™™/@ÍÌÌÌÌÌ,@ÍÌÌÌÌÌ0@š™™™™™-@ÍÌÌÌÌL2@ffffff4@ÍÌÌÌÌÌ.@š™™™™™3@333333)@š™™™™™+@š™™™™™/@3@š™™™™1@š™™™™™0@š™™™™™3@š™™™™™2@2@3333330@0@2@ffffff0@€4@š™™™™™.@3333332@š™™™™™1@ffffff-@ÍÌÌÌÌL1@-@-@fffffæ0@.@ffffff/@3333330@ffffff0@1@-@ffffff-@ÍÌÌÌÌÌ+@*@ÍÌÌÌÌL1@333333/@š™™™™™8@333333'@š™™™™™2@ffffff3@ Cylindersÿmatrixþÿÿÿ– @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @@@@@@@@@@@@ @ @ @ @@@@@@@@@@@ @ @ @ @ @ @ @@@@@@@@@@@@@@@@@@ @ @ @ @ @ @ @ @ @@ @ @ @ @@@@@@@@@@ @ @ @ @ @ @ @ @ @ @ @ @@@@@@@ @ @ @ @@@@@@@@@ @ @@@@@ @@@ @@@@@@@@@@@@ @ @ @ @ @@@@@@@@@@@@@@@@ @ @ @ @@@@@@ @ @@@@@@@@@@@@@@@@@@@@@ @ @ @ @@@@@@@@@@@@@@@@@@ @@@@ @ @ @ @@@@@@ @ @ @ @@@@@ @ @ @ @@@@@@@@@@@@@@@@@ @ @ @@@@@@@@@@@ @@ @ @@@@@@@@@@@@@@@@@@@@ @ @ @ @ @ @ @ @@@@@@ @@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Displacementÿmatrixþÿÿÿ–0s@àu@às@s@àr@Ðz@`|@€{@p|@`x@ `@àu@ðu@ðw@€v@ðw@@u@àr@y@p|@@\@Àh@àh@i@@X@@X@€[@ÀZ@Z@@^@àh@€v@0s@às@s@@X@€a@@\@€X@@X@m@ l@@o@@o@m@àu@y@ðu@às@ðw@y@y@ p@€a@@o@@o@€^@]@ÀS@V@ÀQ@R@@X@ÀV@@\@`X@@X@€a@€^@àu@y@às@ðu@s@Ðz@àu@àu@y@€Q@s@0s@àr@às@@^@@^@^@X@€^@@X@^@€X@@X@àu@s@àu@àr@às@Ðz@y@ðu@às@€{@p|@€v@ l@@o@m@@o@Àh@@X@y@y@€v@àu@m@@X@€a@[@€Q@€^@`c@€X@àu@y@Q@]@€\@@^@às@@^@€c@àu@Àh@i@m@@o@ÀS@€^@ÀQ@€a@@o@ p@ l@àr@àu@às@àr@s@€X@ÀS@@X@S@ÀT@€V@€V@]@^@[@ÀS@ l@@o@@o@@o@y@àu@às@ðu@àl@@o@ p@ l@àl@`p@àr@@X@€a@m@€a@À`@€V@À]@`e@€V@m@À\@^@@^@@^@ÀV@ÀZ@]@€a@€X@@Y@s@às@s@ðu@ l@@o@i@m@@U@€X@€V@ÀV@ l@@o@@o@ p@@X@@U@@X@€a@@`@às@^@€c@e@àu@àu@àr@às@€X@À[@ÀS@€^@@U@s@@p@às@àr@@o@àl@ l@@o@y@àu@y@ðu@@X@àb@@X@€a@€X@€X@@X@@X@@b@@^@T@€V@€X@€S@@U@ÀV@@p@às@àr@àl@i@i@€a@ l@m@àl@i@ l@ p@s@àl@àr@às@€X@À`@À]@@Z@À`@€c@àb@À]@``@`d@@^@`d@@V@€X@àl@i@€a@m@ l@s@àr@ðu@às@àu@ðu@°p@€v@@V@€U@€X@@^@àf@àu@ a@@p@@Z@@Z@@U@ÀV@àb@ e@ e@àb@€X@@V@€X@€U@àb@€a@àb@ l@@X@À`@^@À]@[@€U@€c@@U@€V@€V@@^@@b@ÀV@@U@@X@@V@e@€Q@€^@€a@ÀZ@à`@àb@€c@ e@à`@ÀS@€U@@T@@X@@U@@V@ÀV@@Z@€X@€X@@Z@Y@ÀZ@[@À]@^@ a@@^@ b@e@@b@àl@àu@i@ l@\@\@\@\@à`@àb@€a@àb@@Z@ÀV@ÀV@@Z@€X@^@ÀZ@[@ÀV@ÀV@ÀV@ f@`p@€c@m@b@à`@àb@€a@@X@à`@^@À]@ Horsepowerÿmatrixþÿÿÿ–@`@ d@Àb@Àb@€a@Àh@€k@àj@ l@Àg@À\@ d@ c@àe@àe@@e@d@€a@Àb@ l@ÀW@ÀW@@X@@U@V@G@ÀU@€V@ÀW@@\@€V@àj@i@@j@ h@V@€V@ÀW@øÿH@Y@@Z@Y@V@Y@ d@àe@ c@Àb@€f@@e@àe@€[@R@Y@V@€U@€V@€Q@S@@P@@Q@N@€Q@ÀW@T@K@€V@€U@ d@àe@Àb@ c@Àb@j@`c@d@Àg@@X@Àb@@`@€a@Àb@\@S@ÀU@@Q@€U@W@@X@T@V@àe@Àb@ b@ a@Àb@Àh@Àb@Àc@Àb@àj@ l@àe@@Z@Y@Y@V@ÀW@G@Àb@àd@@e@€f@Y@V@R@€W@€V@@U@ÀZ@€V@ b@Àl@€H@ÀR@ÀV@\@Àb@€[@€^@€f@ÀW@øÿY@Y@ÀP@T@@P@ÀR@Y@€[@@Z@€a@Àb@Àb@€a@Àb@ÀT@ÀP@€S@J@€N@ÀR@ÀR@ÀR@@X@@W@ÀP@ÀW@@Z@R@R@@e@ b@Àb@€b@€[@@Z@€[@ÀW@€[@€[@ `@ÀR@ÀT@Y@€S@X@ÀQ@@X@@X@€Q@€V@ÀW@V@€X@À\@€J@€U@@T@W@ÀS@ÀT@€a@Àb@^@c@Y@@Z@@T@€V@J@N@€Q@€J@Y@€S@€[@ÀW@ÀQ@€Q@ÀR@R@€Y@Àb@V@[@^@€f@ b@@`@Àb@Q@T@M@X@€Q@ b@€[@ b@@`@€[@@Z@Y@€X@€f@@e@Àg@ b@€S@V@ÀR@@V@€O@ÀT@ÀP@€S@@X@€[@€[@H@€P@J@€Q@N@€[@€a@`a@@Z@ÀW@@U@V@Y@€V@@Z@@U@€[@^@ b@ d@`a@€a@Q@ÀW@@X@ÀR@ÀW@@Z@@U@@X@ÀY@@_@À\@ `@ÀQ@Q@À\@@U@V@€V@€[@@`@ `@@a@à`@`c@Àa@@_@Àb@ÀQ@@P@T@T@@S@@_@ÀQ@€V@€Q@€Q@@P@@Q@€V@À\@À\@€V@S@N@€Q@@P@€V@V@€V@€V@€S@€V@ÀR@W@ÀR@@P@@Z@@P@H@H@ÀP@ÀP@ÀP@øÿÀP@O@€`@Y@V@øÿR@U@U@W@€[@U@M@P@N@ÀP@@P@O@Q@€O@@P@@P@€R@øÿÀR@ÀR@Y@€R@T@€[@S@]@^@€[@@Z@V@@U@V@V@V@@U@U@€V@W@øÿ€R@Q@Q@€O@€Q@V@ÀR@€Q@ÀP@ÀP@ÀP@€[@@U@W@\@X@U@€V@€U@J@U@ÀS@€T@MPGÿmatrixþÿÿÿ–2@.@2@0@1@.@,@,@,@.@øÿøÿøÿøÿøÿ.@,@øÿ.@,@8@6@2@5@;@:@9@8@9@:@5@$@$@&@"@;@<@9@9@øÿ3@0@1@3@2@,@,@,@,@(@*@*@2@6@3@2@7@<@>@>@?@€A@;@:@8@9@7@4@5@*@,@.@,@1@&@*@(@*@3@.@*@*@,@2@6@5@:@6@<@7@<@;@*@,@*@,@.@(@*@*@,@*@(@*@2@0@2@2@7@:@&@(@*@(@2@4@5@6@2@3@5@:@.@0@=@8@4@3@.@8@4@&@4@5@3@.@?@:@@@9@0@0@2@0@*@,@,@,@=@:@:@?@@@<@8@:@8@:@?@3@2@.@.@0@.@0@,@1@0@.@2@5@4@*@=@7@4@7@8@9@8@2@=@3@7@7@6@9@€@@<@9@9@:@;@€1@0@/@-@6@6@8@€6@=@€8@=@€@@4@2@€2@€1@€=@@@<@€:@4@*@3@3@€0@€0@*@*@*@€?@>@B@€9@À@@€1@1@/@.@€1@€4@3@€2@0@/@/@0@=@€8@:@€9@€>@À@@>@€>@6@€5@€5@ÍÌÌÌÌŒE@ÍÌÌÌÌ B@ffffff@@33333³C@ÍÌÌÌÌ B@fffffæ3@ffffff3@3333334@3333333@€4@3333334@š™™™™9@€4@ffffff3@š™™™™™4@ÍÌÌÌÌÌ4@š™™™™™2@š™™™™2@3333333@33333³1@š™™™™2@€1@>@€;@333333;@fffffæ>@š™™™™5@3333337@ÍÌÌÌÌÌ7@fffffæ7@ÍÌÌÌÌL4@1@š™™™™™5@3333330@€?@€=@€5@ÍÌÌÌÌÌ3@ÍÌÌÌÌL6@3333334@š™™™™™4@1@š™™™™™1@€0@3333332@fffffæ0@/@3333333@€2@fffffæ?@ÍÌÌÌÌ A@š™™™™ÙA@ffffff;@ffffff9@7@333333;@fffffæ7@š™™™™A@@A@ÍÌÌÌÌÌ?@fffff¦B@ffffff<@ÍÌÌÌÌÌ<@ÍÌÌÌÌÌ:@À@@ÀD@ÍÌÌÌÌ C@ÍÌÌÌÌ @@š™™™™™B@<@ffffff:@ÍÌÌÌÌL8@š™™™™3@fffff&A@ÍÌÌÌÌÌ=@ÍÌÌÌÌL?@€B@š™™™™@@ÍÌÌÌÌLG@fffffæ;@ffffffD@fffff&F@33333³E@333333B@>@ÍÌÌÌÌLF@33333sD@fffffæ@@ÍÌÌÌÌÌ=@š™™™™Y@@33333³7@€A@š™™™™™7@333333@@333333;@š™™™™™:@ÍÌÌÌÌÌ9@€7@>@ÍÌÌÌÌŒC@€C@ÍÌÌÌÌŒA@fffff&@@€B@š™™™™ÙB@ÍÌÌÌÌ A@š™™™™YA@333333A@fffffæ=@€@@@A@š™™™™Ù@@333333@@33333s@@š™™™™™?@š™™™™<@øÿ33333³>@ffffff9@3333338@ffffff6@š™™™™™:@3333334@š™™™™™1@<@;@A@?@=@;@8@7@B@€B@?@C@B@B@B@A@C@@@C@9@C@:@6@@@B@;@;@F@@@<@?@Mfgÿ sq_stringþÿÿÿ– cbpaffcppaccfpadpfcbtpafdvpasbafcdidctfvapcfacpfpdfpacpfmopftdvptdvcfcppfambocmacfpvvprfdtdtbacfdmcfpcbapcafpvcfpoatcdmfmfcpfoavdstopfacdftccapfbdfaavotddffhsfpcmfpcpfbcapbcftfaptvdfvaapvshfofdrcdafpcfaccvhdfpavdtfvpptmccfdhbrpdcodmcbpfpccfvptfcdsvdbmvfmdhodmpcffpabmdacbfdctddtpodavspvhpmfadcfmdbfccvmdamcpoppdfbcopvtcdcfadatmdtmddvvamhrsvdmtfhpbdcptphsdtmpffvrhtdmpsvtdbofccccpdpfavmmpmnhthhdbocftdcfvdfchulmoohlomiholmolohuolmoaoeuammohohahooomlhomhoolooomhooepeioaolooohoholomeulhamholooeeoaoooumhooeholhumlhmolohollmohaaoeihoipuooaollomhaoohhmlouoomuopoaoiiouilheoohlouhmluhooomoooaoomueoaoipooehomolhomhhooooomoaoooleoeahooouelahloehuloohhooooohouoamaooaaoloeohoolmueomhuoohoaoollauoaeoooeomohoeouohhoaomeaelllaiuhlooohahomouoaaoaoaooueoeuoaarooluohlolouaoaloooeooaaeaooaulohhhhoooomoaaleiooooaulhooohooooheiycrreyncterycdyreiyycrtludawcred teyrlcyercenrydrncenrreuaytlyydlerenyrcridrzceryllunrtydyicerdreryricyecrylerydcyetzrraenaedldaydyrcetryeecyridrcdleytdaanbayerrneyriecyieryrcnyltrlcdulanaerdnedcryerceelndrncltyrlyuyrderdninyteddreiyrnerrlnyredbltwzlrztnddrnerrycirdceirdeytdyydtdlaulnnrrcderrdirerlzdcrdudyytaiednlyetercddyztyzdtlldrnnbltzirnyideyyynbtyzyrrlnnytzualytidrreeendnrclzzyrsnynntidrryderldrevcm ddvmt rvdm gmdvcom dskgib dvg svodk mvd vtdmgdt vtdclgtoskmogkvdvtmd ccsyd vdmvkgadsogoc vdgcvdmyc mv dmkvdms ovsddctvttlivgbosmd vsdovv mdcgd iklosgttdatmvcdtvmdcv mcvdod toksdk igvbdtldgavg dmvd vvkdgdt ksodvmgocivdgdcamsvsgcvcmdtvydktodvgaks dkddsdsgctvddm ccg vcdgvosgomssivbgkdtcd gvdcgcdvykdg cigsmmstcvstkovsvd giodsodgskkicdaaksduddmcgvmomdasodmddkadosdgbvoscsdyvvvtgtd kddmcsdoddscsydogvdkgdvrko roi or o eo rkto use re urt s or ri oe i ri u e tusotesr rio ukmsa r ooseu utetk r eur osk or osr om trua u ri oe tmo ru trr o ke s tue ar oru iro kr okr t itsu s eo a eure or rrsae i sut ooetelr eakuourmeurko irs sit rersu as auameuir o kue rk ertuetomu o esaiu er uek rssae elemoou krmistrur e tautaeuss eaursuam aokerotoarutao suatuae otukm srrriei saaouaataaukms ter se ro u oua eo u u o au nwo o noa w uo oa u a oa r o anwua wo oau r ol o u wol na a o ro ul uo uwo uo aon r oa aou on aoo u w an u uor aou o u o a aawn w o lo uo oow a wna uoadlo lunoo ro u aol waa o uwn w n o rao u r o oan auon ow ar o r olw dloouun ooawaono a na nww d luwn p u ouau una u wl an o an o loooa a w urn a n ol a o w ol t ltc nl t t l t at l l a tl lc t c lc y t at al lct y be l t att l yl te tl tal tb l y lc bt l ll t a tly clt l t l c a a t tl tl lla c a tt eal tt lb yl t cle ac l a a b ycl t y l l tb ta cy l y lea eatbtt lbca l l aa e t a h t lt t t at t b elllc c a ty be l a le h eh e h h e h g e e g he e h e gh ge e h ir e h g e e hr he hge hi e e ih e ee h g he eh e h e g g e he eeg g h sce h ei e h er g e g g i e h e e hi g e erg sc ihh ei g e e gg s g h eh h h g i reee g h ir e g et t t t e t t e t t t e et t l t e t t t et l t t l t tt e t t t t e e t t tte e - t tl t t e t e e l t t t l e t t e - l tl e t t ee - e t e l ttt e l t e t n n n n e n n e e n n n n n b e n n n e e n n b e e n nn b n n e n e n e e e n n n z z z Modelÿ sq_stringþÿÿÿ–$cbpaffcppaccfpadpfcbtpafdvpasbafcdidctfvapcfacpfpdfpacpfmopftdvptdvcfcppfambocmacfpvvprfdtdtbacfdmcfpcbapcafpvcfpoatcdmfmfcpfoavdstopfacdftccapfbdfaavotddffhsfpcmfpcpfbcapbcftfaptvdfvaapvshfofdrcdafpcfaccvhdfpavdtfvpptmccfdhbrpdcodmcbpfpccfvptfcdsvdbmvfmdhodmpcffpabmdacbfdctddtpodavspvhpmfadcfmdbfccvmdamcpoppdfbcopvtcdcfadatmdtmddvvamhrsvdmtfhpbdcptphsdtmpffvrhtdmpsvtdbofccccpdpfavmmpmnhthhdbocftdcfvdfchulmoohlomiholmolohuolmoaoeuammohohahooomlhomhoolooomhooepeioaolooohoholomeulhamholooeeoaoooumhooeholhumlhmolohollmohaaoeihoipuooaollomhaoohhmlouoomuopoaoiiouilheoohlouhmluhooomoooaoomueoaoipooehomolhomhhooooomoaoooleoeahooouelahloehuloohhooooohouoamaooaaoloeohoolmueomhuoohoaoollauoaeoooeomohoeouohhoaomeaelllaiuhlooohahomouoaaoaoaooueoeuoaarooluohlolouaoaloooeooaaeaooaulohhhhoooomoaaleiooooaulhooohooooheiycrreyncterycdyreiyycrtludawcred teyrlcyercenrydrncenrreuaytlyydlerenyrcridrzceryllunrtydyicerdreryricyecrylerydcyetzrraenaedldaydyrcetryeecyridrcdleytdaanbayerrneyriecyieryrcnyltrlcdulanaerdnedcryerceelndrncltyrlyuyrderdninyteddreiyrnerrlnyredbltwzlrztnddrnerrycirdceirdeytdyydtdlaulnnrrcderrdirerlzdcrdudyytaiednlyetercddyztyzdtlldrnnbltzirnyideyyynbtyzyrrlnnytzualytidrreeendnrclzzyrsnynntidrryderldrevcm ddvmt rvdm gmdvcom dskgib dvg1svodk mvd vtdmgdt vtdclgtoskmogkvdvtmd ccsyd vdmvkgadsogoc vdgcvdmyc mv dmkvdms ovsddctvttlivgbosmd vsdovv mdcgd iklosgttdatmvcdtvmdcv mcvdod toksdk igvbdtldgavg dmvd vvkdgdt ksodvmgocivdgdcamsvsgcvcmdtvydktodvgaks dkddsdsgctvddm ccg vcdgvosgomssivbgkdtcd gvdcgcdvykdg cigsmmstcvstkovsvd giodsodgskkicdaaksduddmcgvmomdasodmddkadosdgbvoscsdyvvvtgtd kddmcsdoddscsydogvdkgdvrkor roiaor oreo rktoh use 2g re2urt sgor mri oe ihri u e tusotesr rio aukmsamr ooseu utetkmr eur oskaorh osr omgtrua u ri oe tmo hru trrmo ke m s tue ar oru iro krmokr t gitsu sp eo a eurem or hrrsae ipsut ooetelr eakuourmeurko irs sit rersu3as auameuir ockuecrk ertuetomu o esaiu cer uek rssaeselemoou krmistrur ce tautaeuss eaursuam aokerotoarutao suatuae otukm srrriei csaaouaataaukms ter se ro uetgouameotue umo auomnwo190rfo 0noapwruotaoagu caooamr1o1anwua wopoaugmr ol aogu wolpna a aog rolul muoomuwocuoraon pr1oa1m1 9aoumoonpaooaug gafwman 11 uxuormaoul oau omapraawnpwa1o 9 11c lo aguomooow gaawnap uoadlof lunoo ro ugaoltwaamo uwn2 wf n o raoffuo r oo f oan auon5 9ow arfo olr colw pdloouuns ooawaonofo 4a na nww5d luwn pm u ouau una ueewl an o9 an ogloooa afow urn a n olga omw rolstboaltcbnlotbctule tra at090e2ld0 l iaetlotlcatmocrlcuy9t2 at calilctabylbertlrt1atti c ctlrcyltteebtlratalotbe l riy2lc2a01d9 btarl i llttrccrtoaa c22c 1tlyaclttclttslu iec a iac0t29c39actlctrtlarllacarcca i2tt eal1daott lbmylstrclehac ulc a 0raig cbdyclaatncyanlruml o tb 029taacyanaltyseoleagcieatbtt tslbca l lana0 6 gc aa0ect a rhuatsalt tc gtssatp 6t0d cbrelllcacanaggty a cc cber cluaralekherleh a erhehhseschnvpg 0e2m5e2dpecngmherae lhou ne s 0 4c1ghcogene hla eirxaeah4g n5coceaeao edhrlahenvhgeuhimce6xn 4e 8n04almihvnebnceeahaeoaaxgnc7o84i .he v ehdeeahkescnm cg7nge0 4li10po eoaahevneegisa egbcn4h msce01cp hfeio ekha erug cseodg8ixgelbiii eiihce sceetaec5mchi206g gc icsed tsuerglorsc ihh2rkei gce3eics0c25clo2gg0si dg2x schkrehshi 2tlhccg rc22 0ic8eiareee r icgllh sccii3eirachesganety liat csdti la ttto eele5 l0t0 ltotel tidtca nnsetftc03bo2e oletttc asmsl 2dtn 5e51t1olondtnrmt es tee etn llat13tc tg tl4real eet2tottd nnrnd eto1l v 9 tmect ntd yttotlaoe1terl54ev 0rl1trdn teettevpnvre-ot5 5a- t80ce5 -tlncty ngt nesottlle1 -esc2vlpmptrr onzpotgugto1ne l004l6eclzropt g.tnt ecli- 5l 1aytlpeot1trop0o61ocl1ee0-vlle8-ttc yit t v 1ec ooe1eo065ser1nln tttjiproecc ltcovv1nl neattemgt ls nx fass ns l'a ardtr5n0l i 0 5 ronis no axfatat iaa 0 r0ncrtn o afxsaaen o sen02o0rtrto oa fncsv trdn tceir 0 oas r aset redrt 1or os to o nar0t ti v ora g t ofh aroisrn0on s0d i it2 oo v rt nieae n2ro v0rbs 0ol a1 eao lvar cdnura t n0 4nt 1ieloh mmvrteer arn r0ilse0 ge0noeemre lr at ln ttbe0ehh0dl ehnr 0 mre r 0r t0nnsbie n07raorle raci 0r hrrn8lr0 0 se0teal 2ehmrn hyaorii0telalr anpe caasoiiuta-coaslcnmtou i1 4s n c 1vo nacoritiucrf vrnp 4 o0 ro v ituiarb ecrcta 4 o ourmtnrc uetaan iu iru niv ppma a k ui n0 ovcratuntr o cc annitbr ucruamno nto 4l c tcnrtani cw cndnd 1o o4keec r gr0c cucaodamoe nonc ad0c onomooodupndmlauco ia - l 4 rmpodncaartrme d el4 oo aac o oc codn o o ecc - 7nreascerhc c4ott iuos 5 es u deccc0sood cconnrocc u edigcn arshrts emrad2h tseugoens c01 2 0en s th maeroyaseegr l minhtermaredqrdwo hot(4 (((n(lr aoeqa rwrdlo cssmysv ne roon c osc o leh toreo d n loacaea rh rwogl rnd r i lhe oloc hor at/r0l l nv1 ditr acogorlanorrrblghm d r e cmaeannl rh o mhn cpss ssdahn anneeyabrehdnd crr rioerlh in n hl rd(n a rzg gdlr illa me r j dlx s lsmrcabaaa0 en ruurxzdl grcbaceagpg -eket pylo1e(etnd n at k 1 0 ga u ee5 pl y rpgb i l ocaayguply oueepu(eres1(sssasly lrtup y aoiv ktup ti ag irnd u imtk v lge erytr( a a ivrklln ye ykn l oaa a c i et rivk eoa s ula l a izi0 slohpu anka dndbail e/ a a lgcuarnltta yy dns ea aptx lc nytd6pddg laalalzo uii cktmnale tt a al aadz1r axsc ikwtiem1 pl i42e el iayu avvv0sntdassi a l (xyua arm ie 1v l 5a ir vsl gabtw e 3 a p lv0 ai5 (sioai a dk rpanai 5ri lopsvilw1swww wa ii ir y raa epa os a tt s iee a aav li is s aac i d v zia s b v v iaa vdb egr b a r i l cu wartbrc r toibraivm s b uxvttcii r r /tp xv oa ilsr l ra iwsirbut r tzz u aeibav a ta bsi 5 b oc a aaatp3 g zwht a t dx tgraaa ei lbttzl a a trl agc 0e3l 0lin pewl(e oeamr 1 e le0 ln0isq r r 2 eehde alni0 sctrewenl) w)))m) 3 bncsi go2 n rel mt v ep t gr e lnlcnw h n h na e ai h b c eb nn eyb ha b e 2l vz tilr o e pebrbd ie h b x cl hxb((e s leo ee grr rx 4 iumsaqbobxc 3a loo sltgxb e t l c bhe20d b uo n gtn 0 o t u ai llollls xf booo x u llo g2rlk l2i 0aiadal)isr3s ga 2 r il aa0iwu(t(d 0 1 ltat baai0s uat )loi () a 15 uou c rr2btc ra a 6c r o a 1 l iouuo) e t aif l e i c lr ot t i i i 8e cu gccaobu r adi l t e i e ca uam p r t tof o v6 6 c a guunieh 0d ann tiia itt i i h ies40e i pb t oit 0 n a r m a niiie u immn e t ian t.o u l0t i pll tw 4scor d 3 tl i)isas 0 2 ro3 o i s s l t s r 60 sbe ak5r u s oci m 2 l t xs r ru l 2 r t lo t t as t p 0 c shesurr c ic tci2t r t s gl ua e ct t i c 6 e r oi t b 0o s omo tet o f b tre0 l t er no b a s eee t t o ms 2 p e e lle e)s0 ank e 0 b ee b rwbw 0 0 1 dp u t t8r e(e w k 0 tr ne o s b uc uax 0 e e ut( c r e + eu e j r s d b sgos u xa of+e s hj tn c a e l r c q nrct a d s t minb re n t a ldgu a n 4 o s srrrh u l m ) ism a e 3r l 0 e r e)o) 0 1 t t o8o s ) 0 ooc rcu t e br pr 0 sos u y 2 g e e ac hu s r cut2 c i o) i rc i e o c u eloc c c t r c b c c() lx ( a a r l i t e m sc ( 0l(i u e cm o u 1 o m y cwc i mul s ug o e uu el m s mw s m mh m l clsag t ll upb u s a ) a lo f l i ( awu k s 3 e o e a k d e s d l l w2t a s ecd a o s 2osi x t ua u ( t p ra o)u i ( ga ebsh m t ri o a e ( ) t a aa i u kaumh o jo sea s a l ou t s a s s(sns a d u l c (i w i s o a-c e dia l n w w e l sl g s ol n s s hs drta l bs l b s( o l lm e x sp a m t c t l p b x s wss t c l g k de ) e n gdh r el i c ) ) e ti h w ( y c t ( w as aoom e e s i r ws m i i r e sr m l o k o o le a s )wi@o u o h is s oob rl b o s ob a ) s a o o s ) mi num r b i )w b b ie a m m n a c i )c m s n a ee e noa ai u u e 1 mu m w l u m w c g u n ) u u v cm n n( k c c t m sl l rc o r d 1 ) e r ) h g 8 e d d b dt (o o b e) k (n s a 7 s ( a c c a i r au su m r l d n s m l l u e o ur wn o ) i ( ( w a a s u b )t u e s s ) s s e g o r g s w w s s l h ) y h e ) ) i i a a l c c m ( m ) s w ) Model_Yearÿmatrixþÿÿÿ–€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@ÀQ@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@@R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@€R@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@ÀR@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@@S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@€S@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@ÀS@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@@T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@Originÿ sq_stringþÿÿÿ–UUUUUUUUUUFUUUUUUUUUJUUUJGFGSGUUUUUJUJUGUUUUUUUUUUUUUUUUUGFIJJGUJUGUUUUUUUUUUUJUUUUSGFFUJJUJUUUUUUUUUUUUUUUUUGUUUUUJUJJUUIUUIGGSUSJUUUUUJUJUUUUUUUUUGGGJJUIIJJIUUUUUUUUUUUUUUUJUUUJGJUGUGFSSJIGUUFUUUUUUUUUUGJUUUUGJJUSUFJGUUUUJUFUJUUUUUUUUUUUUGUJUUUJGJGJGUJJJUUUUUUUUUUUUUUUUUUJJUJUUJGSSFGJUUUUUUUUUUUUUGJUUGUFUUUJIUUUUGJUJUUUUGJJJJJUJGGGGJFJGJJEUJUUUUUJUJJJJJUUUGFJJJJFSSJJUUUUUUUUUUUUGJJUUJJJJJJUUUUJUUUGUUUSSSSSSSSSSrSSSSSSSSSaSSSaereweSSSSSaSaSeSSSSSSSSSSSSSSSSSertaaeSaSeSSSSSSSSSSSaSSSSwerrSaaSaSSSSSSSSSSSSSSSSSeSSSSSaSaaSStSSteewSwaSSSSSaSaSSSSSSSSSeeeaaSttaatSSSSSSSSSSSSSSSaSSSaeaSeSerwwateSSrSSSSSSSSSSeaSSSSeaaSwSraeSSSSaSrSaSSSSSSSSSSSSeSaSSSaeaeaeSaaaSSSSSSSSSSSSSSSSSSaaSaSSaewwreaSSSSSSSSSSSSSeaSSeSrSSSatSSSSeaSaSSSSeaaaaaSaeeeearaeaanSaSSSSSaSaaaaaSSSeraaaarwwaaSSSSSSSSSSSSeaaSSaaaaaaSSSSaSSSeSSSAAAAAAAAAAaAAAAAAAAApAAAprarerAAAAApApArAAAAAAAAAAAAAAAAAraapprApArAAAAAAAAAAApAAAAeraaAppApAAAAAAAAAAAAAAAAArAAAAApAppAAaAAarreAepAAAAApApAAAAAAAAArrrppAaappaAAAAAAAAAAAAAAApAAAprpArAraeeparAAaAAAAAAAAAArpAAAArppAeAaprAAAApAaApAAAAAAAAAAAArApAAAprprprApppAAAAAAAAAAAAAAAAAAppApAApreearpAAAAAAAAAAAAArpAArAaAAApaAAAArpApAAAArpppppAprrrrpaprppgApAAAAApApppppAAArappppaeeppAAAAAAAAAAAArppAAppppppAAAApAAArAAA n a amnmdm a a m mnlaam a m a dmnn aa a m a aa l lmmd da a a mmmaa llaal a ama m mnddalm n ma maa d nam a n a m a amamam aaa aa a amddnma ma m n al ma a maaaaa ammmmanamaal a a aaaaa mnaaaanddaa maa aaaaaa a m c n nacaea n n a acynna n a n eacc nn n a n nn y yaae en n n aaann yynny n nan a aceenya c an ann e cna n c n a n nanana nnn nn n naeecan an a c ny an n annnnn naaaancnanna n n nnnnn acnnnnceenn ann nnnnnn n a e nennn n ne n n nnee n nnn n nnn n n nenn n e n n n e n e n n n n nnnen n n e n n nnnn e n n ne enn n n y y y y y y y y y yy yyy y y y y y y y y y y y y y y y y y yyyy y d y y y Weightÿmatrixþÿÿÿ–`«@Ú¬@ت@Òª@òª@õ°@±@ذ@I±@®@$¨@.°@„¯@F°@®@Ö«@2¬@2ª@b­@¨@ˆ¢@"¦@¬¥@6¤@¤ @¬œ@à¤@ü¢@Ž¢@t¡@°¤@²@±@±@|²@¤ @°¡@h¡@øŸ@èž@”¤@Þª@ª@Ì©@°©@q°@p±@:°@°@[³@в@´@$§@Т@¤©@†¨@X¡@– @4 @" @´›@4™@¨œ@Œž@Ì¡@œ @œ¡@Т@d¡@²°@!±@'°@!°@°¬@²@–±@h±@F±@4¢@h®@°@ư@Ú¯@ê¦@ž£@F§@¡@¶¢@à¡@”£@è @h @°@°¬@(¯@”¯@‚­@X³@p±@ ±@°@²@W³@Ú­@b¨@œ©@§@š§@°¦@xž@…³@*³@.²@“±@Ê¥@Ρ@¢@–¢@˜ @ ¢@P£@²¡@ä¯@¶°@,@Ü @,¤@h¦@Žª@Ȥ@î¥@ ¬@<¨@v¦@ª¦@ª@xž@&£@°œ@Ü£@Š­@`¬@:¬@-°@[²@i±@²@¡°@V¡@¬ž@ø¡@Ä™@LŸ@š @x @Œ¡@r£@®¢@@Ÿ@€©@«@Ъ@¬¨@<²@X±@’±@1²@†®@r®@$­@’­@¾§@*©@¨@ö @ž¤@Ħ@@¤@¥@^¡@â£@P§@Dž@©@ ¥@§@§@Þ¤@ œ@@£@X¡@¤@ž¡@4¡@w°@^°@ô®@w°@B©@2ª@ˆ§@¨@ÌŸ@è @Dž@ œ@†¬@ì«@z¬@ò¨@„œ@Ÿ@Ö @ ¤@œ¨@È®@Œ©@ä¦@Ø­@±@®¯@<®@V­@ôŸ@Ö @„œ@ø¡@dž@P®@¸¯@,°@ǰ@€«@ª@\¬@Š«@|°@E°@å°@ï°@Pž@h¥@²¡@†¥@ @6 @Ÿ@¡@þ¥@P¤@@¥@Ÿ@ œ@Ÿ@, @ œ@Jª@.­@ä«@ž«@¦¨@*§@@¥@̪@©@hª@ü§@H¬@¤ª@ª@êª@ ©@à¯@Ö @¤@ø¡@l¡@¦£@r¥@N¦@Ê¢@¦@ˆ¨@Ö¥@¤ª@Ÿ@® @Z©@\§@”¦@‚©@@ª@®@­@æ®@ì­@±@¬¯@*¬@È®@ž@Üž@ì@ܤ@”«@x®@ì¨@¸ª@0¡@Ì @Ÿ@¤ @ܤ@F¤@¥@ø£@À @Àž@ @ŒŸ@ì¤@l¦@v§@jª@¡@.¥@Ü£@£@²¡@| @à¥@| @J @>¢@ §@d©@èœ@¬œ@ @Ôœ@¼¦@è¢@ˆ£@²¦@ä¡@t£@–¤@x¤@J¥@¢¢@l›@L@€›@" @Üž@ @Ÿ@N¡@ôŸ@˜¢@¡@ ¢@D¡@\¢@n¤@–¤@<©@à¥@°¨@¨¦@ä¦@®ª@­@è§@«@Z¤@ ¤@¶¢@¤@º£@^¥@b¦@¶§@ðž@¤Ÿ@Èž@š @š @à @:¡@Š¡@´ž@´ž@,Ÿ@§@ާ@2¤@&¦@Ò¤@„¢@ §@Ì¥@¤ @î¡@‚¤@@¥@cyl4ÿ sq_stringþÿÿÿ–OOOOOOOOOOFOOOOOOOOOFOOOFFFFFFOOOOOFFFFFOOOOOOOOOOOOOFOOFFFFFFFFFFFFFOOOOOOOOOOOOOOFFFFFFFFFOOOOOOOOOOOOOOOOOFOOOOOFFFOFOFOOFFFFOFOOOOOOFFFFOOOOOOOOFFFFFFFFFFFOOOOOOOOOOOOOOOFFOFFFFOFOFFFFFFFFFFOOOOOOOOFFFFOOOOFFFFFOFOOOOOOFFFFFOOOOOOOOOOOOFFFFFFFFOFOFFFFFOOOOOOFOOOOOOOOOOFFFFFFFFOOFOFFOOFOOOOOOOOOOFFFFOOFOFFFFFOOFFFFFFFFOFFFFFFFFFFOFFFFFOOFFFFFFOFFFFFFFFFFFFFFFFFFFOOOOOOOFFFFFFFFFFFFFFFFFFFOOFOFFFFFFFFttttttttttotttttttttotttooooootttttoooootttttttttttttottooooooooooooottttttttttttttoooooooootttttttttttttttttotttttooototottooootottttttoooottttttttoooooooooootttttttttttttttootoooototoooooooooottttttttoooottttoooootottttttooooottttttttttttoooooooototooooottttttottttttttttoooooooottotoottottttttttttoooottotooooottooooooootooooooooootooooottooooootoooooooooooooooooootttttttooooooooooooooooooottotoooooooohhhhhhhhhhuhhhhhhhhhuhhhuuuuuuhhhhhuuuuuhhhhhhhhhhhhhuhhuuuuuuuuuuuuuhhhhhhhhhhhhhhuuuuuuuuuhhhhhhhhhhhhhhhhhuhhhhhuuuhuhuhhuuuuhuhhhhhhuuuuhhhhhhhhuuuuuuuuuuuhhhhhhhhhhhhhhhuuhuuuuhuhuuuuuuuuuuhhhhhhhhuuuuhhhhuuuuuhuhhhhhhuuuuuhhhhhhhhhhhhuuuuuuuuhuhuuuuuhhhhhhuhhhhhhhhhhuuuuuuuuhhuhuuhhuhhhhhhhhhhuuuuhhuhuuuuuhhuuuuuuuuhuuuuuuuuuuhuuuuuhhuuuuuuhuuuuuuuuuuuuuuuuuuuhhhhhhhuuuuuuuuuuuuuuuuuuuhhuhuuuuuuuueeeeeeeeeereeeeeeeeereeerrrrrreeeeerrrrreeeeeeeeeeeeereerrrrrrrrrrrrreeeeeeeeeeeeeerrrrrrrrreeeeeeeeeeeeeeeeereeeeerrrerereerrrrereeeeeerrrreeeeeeeerrrrrrrrrrreeeeeeeeeeeeeeerrerrrrererrrrrrrrrreeeeeeeerrrreeeerrrrrereeeeeerrrrreeeeeeeeeeeerrrrrrrrererrrrreeeeeereeeeeeeeeerrrrrrrreererreereeeeeeeeeerrrreererrrrreerrrrrrrrerrrrrrrrrrerrrrreerrrrrrerrrrrrrrrrrrrrrrrrreeeeeeerrrrrrrrrrrrrrrrrrreererrrrrrrrrrrrrrrrrr rrrrrrrrr rrr rrrrr rrrrrrrrrrrrr rr rrrrrrrrrrrrrr rrrrrrrrrrrrrrrrr rrrrr r r rr r rrrrrr rrrrrrrr rrrrrrrrrrrrrrr r r r rrrrrrrr rrrr r rrrrrr rrrrrrrrrrrr r r rrrrrr rrrrrrrrrr rr r rr rrrrrrrrrr rr r rr r r rr r rrrrrrr rr r orgÿ sq_stringþÿÿÿ–UUUUUUUUUUEUUUUUUUUUJUUUJEEEEEUUUUUJUJUEUUUUUUUUUUUUUUUUUEEEJJEUJUEUUUUUUUUUUUJUUUUEEEEUJJUJUUUUUUUUUUUUUUUUUEUUUUUJUJJUUEUUEEEEUEJUUUUUJUJUUUUUUUUUEEEJJUEEJJEUUUUUUUUUUUUUUUJUUUJEJUEUEEEEJEEUUEUUUUUUUUUUEJUUUUEJJUEUEJEUUUUJUEUJUUUUUUUUUUUUEUJUUUJEJEJEUJJJUUUUUUUUUUUUUUUUUUJJUJUUJEEEEEJUUUUUUUUUUUUUEJUUEUEUUUJEUUUUEJUJUUUUEJJJJJUJEEEEJEJEJJEUJUUUUUJUJJJJJUUUEEJJJJEEEJJUUUUUUUUUUUUEJJUUJJJJJJUUUUJUUUEUUUSSSSSSSSSSuSSSSSSSSSaSSSauuuuuSSSSSaSaSuSSSSSSSSSSSSSSSSSuuuaauSaSuSSSSSSSSSSSaSSSSuuuuSaaSaSSSSSSSSSSSSSSSSSuSSSSSaSaaSSuSSuuuuSuaSSSSSaSaSSSSSSSSSuuuaaSuuaauSSSSSSSSSSSSSSSaSSSauaSuSuuuuauuSSuSSSSSSSSSSuaSSSSuaaSuSuauSSSSaSuSaSSSSSSSSSSSSuSaSSSauauauSaaaSSSSSSSSSSSSSSSSSSaaSaSSauuuuuaSSSSSSSSSSSSSuaSSuSuSSSauSSSSuaSaSSSSuaaaaaSauuuuauauaauSaSSSSSaSaaaaaSSSuuaaaauuuaaSSSSSSSSSSSSuaaSSaaaaaaSSSSaSSSuSSSAAAAAAAAAArAAAAAAAAApAAAprrrrrAAAAApApArAAAAAAAAAAAAAAAAArrrpprApArAAAAAAAAAAApAAAArrrrAppApAAAAAAAAAAAAAAAAArAAAAApAppAArAArrrrArpAAAAApApAAAAAAAAArrrppArrpprAAAAAAAAAAAAAAApAAAprpArArrrrprrAArAAAAAAAAAArpAAAArppArArprAAAApArApAAAAAAAAAAAArApAAAprprprApppAAAAAAAAAAAAAAAAAAppApAAprrrrrpAAAAAAAAAAAAArpAArArAAAprAAAArpApAAAArpppppAprrrrprprpprApAAAAApApppppAAArrpppprrrppAAAAAAAAAAAArppAAppppppAAAApAAArAAA o a aooooo a a o oooaao a o a oooo aa a o a aa o oooo oa a a oooaa ooaao a aoa o ooooaoo o oa oaa o oao a o a o a aoaoao aaa aa a aoooooa oa o o ao oa a oaaaaa aooooaoaoaao a a aaaaa ooaaaaoooaa oaa aaaaaa a o p n nppppp n n p pppnnp n p n pppp nn n p n nn p pppp pn n n pppnn ppnnp n npn p ppppnpp p pn pnn p pnp n p n p n npnpnp nnn nn n npppppn pn p p np pn n pnnnnn nppppnpnpnnp n n nnnnn ppnnnnpppnn pnn nnnnnn n p e eeeee e eee e e eeee e e eeee e eee ee e e e eeee ee e e e e e e e e e e e eeeee e e e e e e eeee e e e ee eee e e whenÿ sq_stringþÿÿÿ–EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy statistics-release-1.7.3/inst/datasets/carsmall.mat000066400000000000000000000260731475240274700224440ustar00rootroot00000000000000Octave-1-L Accelerationÿmatrixþÿÿÿd(@'@&@(@%@$@"@!@$@!@€1@'@&@%@&@$@ @ @#@$@.@/@/@0@-@€4@€1@-@€1@)@.@,@.@+@€2@/@fffffæ0@ÍÌÌÌÌÌ-@33333³1@š™™™™™.@*@*@ÍÌÌÌÌÌ+@š™™™™™)@ÍÌÌÌÌÌ.@-@š™™™™™1@š™™™™™1@3333336@š™™™™6@ffffff,@ffffff1@33333³1@5@3333330@ÍÌÌÌÌÌ1@ffffff(@1@ffffff0@333333+@ffffff/@ffffff*@fffffæ5@/@33333³0@333333(@(@.@,@š™™™™™3@š™™™™™2@2@3333330@0@2@ffffff0@€4@š™™™™™.@3333332@š™™™™™1@ffffff-@ÍÌÌÌÌL1@-@-@fffffæ0@.@ffffff/@3333330@ffffff0@1@-@ffffff-@ÍÌÌÌÌÌ+@*@ÍÌÌÌÌL1@333333/@š™™™™™8@333333'@š™™™™™2@ffffff3@ Cylindersÿmatrixþÿÿÿd @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @@@@@@@@@@@@ @ @ @ @@@@@@ @ @ @ @@@@@@@@@@@@@@@@@@ @@@@ @ @ @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Displacementÿmatrixþÿÿÿd0s@àu@às@s@àr@Ðz@`|@€{@p|@`x@ `@àu@ðu@ðw@€v@ðw@@u@àr@y@p|@@\@Àh@àh@i@@X@@X@€[@ÀZ@Z@@^@àh@€v@0s@às@s@ÀZ@]@€a@€X@@Y@s@às@s@ðu@ l@@o@i@m@@U@€X@€V@ÀV@ l@@o@@o@ p@@X@@U@@X@€a@@`@às@^@€c@e@àu@àu@àr@às@\@\@\@\@à`@àb@€a@àb@@Z@ÀV@ÀV@@Z@€X@^@ÀZ@[@ÀV@ÀV@ÀV@ f@`p@€c@m@b@à`@àb@€a@@X@à`@^@À]@ Horsepowerÿmatrixþÿÿÿd@`@ d@Àb@Àb@€a@Àh@€k@àj@ l@Àg@À\@ d@ c@àe@àe@@e@d@€a@Àb@ l@ÀW@ÀW@@X@@U@V@G@ÀU@€V@ÀW@@\@€V@àj@i@@j@ h@€U@@T@W@ÀS@ÀT@€a@Àb@^@c@Y@@Z@@T@€V@J@N@€Q@€J@Y@€S@€[@ÀW@ÀQ@€Q@ÀR@R@€Y@Àb@V@[@^@€f@ b@@`@Àb@V@V@V@@U@U@€V@W@øÿ€R@Q@Q@€O@€Q@V@ÀR@€Q@ÀP@ÀP@ÀP@€[@@U@W@\@X@U@€V@€U@J@U@ÀS@€T@MPGÿmatrixþÿÿÿd2@.@2@0@1@.@,@,@,@.@øÿøÿøÿøÿøÿ.@,@øÿ.@,@8@6@2@5@;@:@9@8@9@:@5@$@$@&@"@<@9@9@:@;@€1@0@/@-@6@6@8@€6@=@€8@=@€@@4@2@€2@€1@€=@@@<@€:@4@*@3@3@€0@€0@*@*@*@<@;@A@?@=@;@8@7@B@€B@?@C@B@B@B@A@C@@@C@9@C@:@6@@@B@;@;@F@@@<@?@Mfgÿ sq_stringþÿÿÿd cbpaffcppaccfpadpfcbtpafdvpasbafcdifofdrcdafpcfaccvhdfpavdtfvpptmccfdcccpdpfavmmpmnhthhdbocftdcfvdfchulmoohlomiholmolohuolmoaoeuammohohipooehomolhomhhooooomoaoooleoeahoohhhoooomoaaleiooooaulhooohooooheiycrreyncterycdyreiyycrtludawcred aerdnedcryerceelndrncltyrlyuyrderdeeendnrclzzyrsnynntidrryderldrevcm ddvmt rvdm gmdvcom dskgib dvg tldgavg dmvd vvkdgdt ksodvmgocivdgvvvtgtd kddmcsdoddscsydogvdkgdvrko roi or o eo rkto use re eure or rrsae i sut ooetelr errriei saaouaataaukms ter se ro u oua eo u u o au nwo o lo uo oow a wna uoadlo oooa a w urn a n ol a o w ol t ltc nl t t l t at l tl tl lla c a tt eal lllc c a ty be l a le h eh e h h e h g e e he eeg g h sce eee g h ir e g et t t t e t t t tte e - t ttt e l t e t n n n b n e n e n z Modelÿ sq_stringþÿÿÿd!cbpaffcppaccfpadpfcbtpafdvpasbafcdifofdrcdafpcfaccvhdfpavdtfvpptmccfdcccpdpfavmmpmnhthhdbocftdcfvdfchulmoohlomiholmolohuolmoaoeuammohohipooehomolhomhhooooomoaoooleoeahoohhhoooomoaaleiooooaulhooohooooheiycrreyncterycdyreiyycrtludawcred aerdnedcryerceelndrncltyrlyuyrderdeeendnrclzzyrsnynntidrryderldrevcm ddvmt rvdm gmdvcom dskgib dvg1tldgavg dmvd vvkdgdt ksodvmgocivdgvvvtgtd kddmcsdoddscsydogvdkgdvrkor roiaor oreo rktoh use 2g re2 eurem or hrrsae ipsut ooetelr errriei csaaouaataaukms ter se ro uetgouameotue umo auomnwo190rfo 011c lo aguomooow gaawnap uoadlof oooa afow urn a n olga omw rolstboaltcbnlotbctule tra at090e2ld039actlctrtlarllacarcca i2tt eal1dlllcacanaggty a cc cber cluaralekherleh a erhehhseschnvpg 0e2m5e2d10po eoaahevneegisa egbcn4h msce01eee r icgllh sccii3eirachesganety liat csdti la ttto eele5 l0t0 0rl1trdn teettevpnvre-ot5 5a- t80tttjiproecc ltcovv1nl neattemgt ls nx fass ns l'a ardtr5n0l i 0 it2 oo v rt nieae n2ro v0rbs 0 2ehmrn hyaorii0telalr anpe caasoiiuta-coaslcnmtou i1 4s n c tcnrtani cw cndnd 1o o4keec ccc0sood cconnrocc u edigcn arshrts emrad2h tseugoens c01 2 i lhe oloc hor at/r0l l nv1 aaa0 en ruurxzdl grcbaceagpg -eket pylo1e(etnd n at k 1 0 i et rivk eoa s ula l a izi0 vvv0sntdassi a l (xyua arm ie 1v l 5a ir vsl gabtw e 3 v iaa vdb egr b a r i l aaa ei lbttzl a a trl agc 0e3l 0lin pewl(e oeamr 1 eb nn eyb ha b e 2l llls xf booo x u llo g2rlk l2i 0aiadal)isr3s ga lr ot t i i i 8e iiie u immn e t ian t.o u l0t i pll tw 4scor d lo t t as t p 0 eee t t o ms 2 p e e lle e)s0 ank e eu e j r s rrrh u l m ) ism a e 3r l g e a r l i t e m sc ( 0l(i u mh m w2t a s ecd a o s 2osi x aa i a-c e dia l n w w e lm e gdh r el i c ) ) i r oob rl b o s b noa ai u u e u v rc o r d 8 k (n s a c d n l i ( a e s s s w s e ) i l c ) Model_Yearÿmatrixþÿÿÿd€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@€Q@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@S@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@€T@Originÿ sq_stringþÿÿÿdUUUUUUUUUUFUUUUUUUUUJUUUJGFGSGUUUUUIGUUFUUUUUUUUUUGJUUUUGJJUSUFJGUUUUUUUUUUUUGJJUUJJJJJJUUUUJUUUGUUUSSSSSSSSSSrSSSSSSSSSaSSSaereweSSSSSteSSrSSSSSSSSSSeaSSSSeaaSwSraeSSSSSSSSSSSSeaaSSaaaaaaSSSSaSSSeSSSAAAAAAAAAAaAAAAAAAAApAAAprarerAAAAAarAAaAAAAAAAAAArpAAAArppAeAaprAAAAAAAAAAAArppAAppppppAAAApAAArAAA n a amnmdm lm n ma maa d nam maa aaaaaa a m c n nacaea ya c an ann e cna ann nnnnnn n a e nennn n e n n n e n n n y y y y y y y y y Weightÿmatrixþÿÿÿd`«@Ú¬@ت@Òª@òª@õ°@±@ذ@I±@®@$¨@.°@„¯@F°@®@Ö«@2¬@2ª@b­@¨@ˆ¢@"¦@¬¥@6¤@¤ @¬œ@à¤@ü¢@Ž¢@t¡@°¤@²@±@±@|²@@£@X¡@¤@ž¡@4¡@w°@^°@ô®@w°@B©@2ª@ˆ§@¨@ÌŸ@è @Dž@ œ@†¬@ì«@z¬@ò¨@„œ@Ÿ@Ö @ ¤@œ¨@È®@Œ©@ä¦@Ø­@±@®¯@<®@V­@Z¤@ ¤@¶¢@¤@º£@^¥@b¦@¶§@ðž@¤Ÿ@Èž@š @š @à @:¡@Š¡@´ž@´ž@,Ÿ@§@ާ@2¤@&¦@Ò¤@„¢@ §@Ì¥@¤ @î¡@‚¤@@¥@statistics-release-1.7.3/inst/datasets/cereal.mat000066400000000000000000000363571475240274700221070ustar00rootroot00000000000000Octave-1-LCaloriesÿmatrixþÿÿÿM€Q@^@€Q@I@€[@€[@€[@@`@€V@€V@^@€[@^@€[@€[@€[@Y@€[@€[@€[@Y@€[@Y@Y@€[@€[@Y@^@^@€[@Y@€[@Y@€[@^@^@€[@€[@€[@€a@€[@Y@€[@Y@Àb@Àb@d@Y@^@€a@€V@@`@^@Y@I@I@Y@Y@^@Y@€V@€[@€[@T@€V@€V@€[@€[@€V@€[@€a@Y@€[@€[@Y@Y@€[@CarboÿmatrixþÿÿÿM@ @@ @,@%@&@2@.@*@(@1@*@*@(@6@5@*@(@$@5@5@&@2@&@,@,@(@,@*@&@.@.@1@*@(@'@,@1@4@5@(@(@0@0@0@1@.@.@5@2@+@&@4@*@$@,@ð¿,@%@.@7@6@0@3@4@"@0@.@5@.@0@5@*@1@1@0@CupsÿmatrixþÿÿÿM…ëQ¸Õ?ð¿…ëQ¸Õ?à?è?è?ð?è?q= ×£på?q= ×£på?è?ô?è?à?ð?ð?ð?ð?ð?à?ð?ð?è?è?ð?è?š™™™™™é?q= ×£på?q= ×£på?è?)\Âõ(ì?è?)\Âõ(ì?Ð?…ëQ¸Õ?ð?è?Ház®Gõ?ð¿è?ø?q= ×£på?ð?ð¿ð¿ð¿q= ×£på?ð?q= ×£på?q= ×£på?ð¿à?q= ×£på?ð?ð?ð¿à?q= ×£på?è?à?à?®Gázò?ð?ð¿q= ×£på?q= ×£på?è?ð?ð¿ð?ð?ð?è?ð?q= ×£på?ð?è?FatÿmatrixþÿÿÿMð?@ð?@@@ð?@@@@ð?ð?@ð?ð?@ð?ð?ð?@@ð?ð?ð?ð?@ð?ð?@@@ð?ð?@@ð?ð?@ð?@ð?ð?ð?ð?ð?ð?ð?ð?ð?FiberÿmatrixþÿÿÿM$@@"@,@ð?ø?ð?@@@@@ð?ð?@ð?ð?@ð?ð?ð?@@@@@@ð?ø?ð?@@@@@@@@ø?@ð?ð?@š™™™™™@@@@@@@ð?ð?@@@@@ð?Mfgÿ sq_stringþÿÿÿMNQKKRGKGRPQGGGGRKKGKNKGRKKKPKPPGPPPQGPKKGQGARRKGKKKGPKQQQQKGKRKNNNKKNGGGGGRGGNameÿcellþÿÿÿMÿ sq_stringþÿÿÿ 100% Branÿ sq_stringþÿÿÿ100% Natural Branÿ sq_stringþÿÿÿAll-Branÿ sq_stringþÿÿÿAll-Bran with Extra Fiberÿ sq_stringþÿÿÿAlmond Delightÿ sq_stringþÿÿÿApple Cinnamon Cheeriosÿ sq_stringþÿÿÿ Apple Jacksÿ sq_stringþÿÿÿBasic 4ÿ sq_stringþÿÿÿ Bran Chexÿ sq_stringþÿÿÿ Bran Flakesÿ sq_stringþÿÿÿ Cap n Crunchÿ sq_stringþÿÿÿCheeriosÿ sq_stringþÿÿÿCinnamon Toast Crunchÿ sq_stringþÿÿÿClustersÿ sq_stringþÿÿÿ Cocoa Puffsÿ sq_stringþÿÿÿ Corn Chexÿ sq_stringþÿÿÿ Corn Flakesÿ sq_stringþÿÿÿ Corn Popsÿ sq_stringþÿÿÿ Count Choculaÿ sq_stringþÿÿÿCracklin Oat Branÿ sq_stringþÿÿÿCream of Wheat (Quick)ÿ sq_stringþÿÿÿCrispixÿ sq_stringþÿÿÿCrispy Wheat & Raisinsÿ sq_stringþÿÿÿ Double Chexÿ sq_stringþÿÿÿ Froot Loopsÿ sq_stringþÿÿÿFrosted Flakesÿ sq_stringþÿÿÿFrosted Mini-Wheatsÿ sq_stringþÿÿÿ&Fruit & Fibre Dates, Walnuts, and Oatsÿ sq_stringþÿÿÿ Fruitful Branÿ sq_stringþÿÿÿFruity Pebblesÿ sq_stringþÿÿÿ Golden Crispÿ sq_stringþÿÿÿGolden Grahamsÿ sq_stringþÿÿÿGrape Nuts Flakesÿ sq_stringþÿÿÿ Grape-Nutsÿ sq_stringþÿÿÿGreat Grains Pecanÿ sq_stringþÿÿÿHoney Graham Ohsÿ sq_stringþÿÿÿHoney Nut Cheeriosÿ sq_stringþÿÿÿ Honey-combÿ sq_stringþÿÿÿJust Right Crunchy Nuggetsÿ sq_stringþÿÿÿJust Right Fruit & Nutÿ sq_stringþÿÿÿKixÿ sq_stringþÿÿÿLifeÿ sq_stringþÿÿÿ Lucky Charmsÿ sq_stringþÿÿÿMaypoÿ sq_stringþÿÿÿ Muesli Raisins, Dates, & Almondsÿ sq_stringþÿÿÿ!Muesli Raisins, Peaches, & Pecansÿ sq_stringþÿÿÿMueslix Crispy Blendÿ sq_stringþÿÿÿMulti-Grain Cheeriosÿ sq_stringþÿÿÿNut&Honey Crunchÿ sq_stringþÿÿÿNutri-Grain Almond-Raisinÿ sq_stringþÿÿÿNutri-grain Wheatÿ sq_stringþÿÿÿOatmeal Raisin Crispÿ sq_stringþÿÿÿPost Nat. Raisin Branÿ sq_stringþÿÿÿ Product 19ÿ sq_stringþÿÿÿ Puffed Riceÿ sq_stringþÿÿÿ Puffed Wheatÿ sq_stringþÿÿÿQuaker Oat Squaresÿ sq_stringþÿÿÿQuaker Oatmealÿ sq_stringþÿÿÿ Raisin Branÿ sq_stringþÿÿÿRaisin Nut Branÿ sq_stringþÿÿÿRaisin Squaresÿ sq_stringþÿÿÿ Rice Chexÿ sq_stringþÿÿÿ Rice Krispiesÿ sq_stringþÿÿÿShredded Wheatÿ sq_stringþÿÿÿShredded Wheat n Branÿ sq_stringþÿÿÿShredded Wheat spoon sizeÿ sq_stringþÿÿÿSmacksÿ sq_stringþÿÿÿ Special Kÿ sq_stringþÿÿÿStrawberry Fruit Wheatsÿ sq_stringþÿÿÿTotal Corn Flakesÿ sq_stringþÿÿÿTotal Raisin Branÿ sq_stringþÿÿÿTotal Whole Grainÿ sq_stringþÿÿÿTriplesÿ sq_stringþÿÿÿTrixÿ sq_stringþÿÿÿ Wheat Chexÿ sq_stringþÿÿÿWheatiesÿ sq_stringþÿÿÿWheaties Honey GoldPotassÿmatrixþÿÿÿM€q@à`@t@ t@ð¿€Q@>@Y@@_@Àg@€A@@Z@€F@@Z@€K@9@€A@4@@P@d@ð¿>@^@T@>@9@Y@i@Àg@9@D@€F@@U@€V@Y@€F@€V@€A@N@ÀW@D@ÀW@€K@ÀW@@e@@e@d@€V@D@@`@€V@^@@p@€F@.@I@€[@€[@n@€a@€[@>@€A@ÀW@€a@^@D@€K@€V@€A@Àl@€[@N@9@À\@€[@N@ProteinÿmatrixþÿÿÿM@@@@@@@@@@ð?@ð?@ð?@@ð?ð?@@@@@@ð?@@@ð?@ð?@@@ð?@ð?@@@@@@@@@@@@@@@@ð?@@@@@@ð?@@@@@@@@@@@ð?@@@ShelfÿmatrixþÿÿÿM@@@@@ð?@@ð?@@ð?@@@ð?ð?@@@@@@@@ð?@@@@ð?@@@@@ð?ð?@@@@@@@@@ð?@@@@@@@@@ð?@@@ð?ð?ð?ð?ð?@ð?@@@@@@ð?ð?ð?SodiumÿmatrixþÿÿÿM@`@.@@p@€a@i@€f@@_@@j@i@@j@€k@ r@@j@€a@€f@€q@ r@€V@€f@€a@T@€k@€a@Àg@@_@i@d@n@à`@€F@€q@€a@@e@ÀR@€k@@o@€f@@e@@e@@p@Àb@€f@ÀW@Àb@Àb@€k@Àg@€k@@e@@e@i@t@à`@@j@€a@n@ r@€Q@Àl@.@i@Àg@i@@o@€a@Àl@i@i@SugarsÿmatrixþÿÿÿM@ @@ @$@,@ @@@(@ð?"@@*@@@(@*@@@$@@*@&@@$@(@(@.@"@@@@&@$@&@@"@@@(@@&@&@*@@"@@@$@,@@@ð¿(@ @@@@.@@@@,@@@(@@@ @TypeÿmatrixþÿÿÿMð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð? Variablesÿcellþÿÿÿÿ sq_stringþÿÿÿNameÿ sq_stringþÿÿÿMfgÿ sq_stringþÿÿÿTypeÿ sq_stringþÿÿÿCaloriesÿ sq_stringþÿÿÿProteinÿ sq_stringþÿÿÿFatÿ sq_stringþÿÿÿSodiumÿ sq_stringþÿÿÿFiberÿ sq_stringþÿÿÿCarboÿ sq_stringþÿÿÿSugarsÿ sq_stringþÿÿÿShelfÿ sq_stringþÿÿÿPotassÿ sq_stringþÿÿÿVitaminsÿ sq_stringþÿÿÿWeightÿ sq_stringþÿÿÿcupsÿ sq_stringþÿÿÿ cereal nameÿ sq_stringþÿÿÿmanufacturer (e.g., Kellogg's)ÿ sq_stringþÿÿÿtype of cereal (cold/hot)ÿ sq_stringþÿÿÿcalories (number)ÿ sq_stringþÿÿÿ protein(g)ÿ sq_stringþÿÿÿfat(g)ÿ sq_stringþÿÿÿ sodium(mg)ÿ sq_stringþÿÿÿdietary fiber(g)ÿ sq_stringþÿÿÿcomplex carbohydrates(g)ÿ sq_stringþÿÿÿ sugars(g)ÿ sq_stringþÿÿÿ3display shelf (1, 2, or 3, counting from the floor)ÿ sq_stringþÿÿÿ potassium(mg)ÿ sq_stringþÿÿÿvitamins & minerals (0, 25, or 100,respectively indicating 'none added'; 'enriched, often to 25% FDA recommended'; '100% of FDA recommended')ÿ sq_stringþÿÿÿ0weight (in ounces) of one serving (serving size)ÿ sq_stringþÿÿÿcups per servingVitaminsÿmatrixþÿÿÿM9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@9@Y@Y@9@9@9@9@9@9@9@9@9@9@9@9@9@Y@9@9@9@9@9@9@9@9@9@Y@Y@Y@9@9@9@9@9@WeightÿmatrixþÿÿÿMð?ð?ð?ð?ð?ð?ð?Ház®Gõ?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ô?Ház®Gõ?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ÍÌÌÌÌÌô?ð?ð?ð?ð?ð¿ð¿ø?ð?ð?Ház®Gõ?ð?ô?Ház®Gõ?ð?à?à?ð?ð?Ház®Gõ?ð?ð?ð?ð?Âõ(\ê?ð?ð?ð?ð?ð?ð?ø?ð?ð?ð?ð?ð?ð?statistics-release-1.7.3/inst/datasets/examgrades.mat000066400000000000000000000113621475240274700227610ustar00rootroot00000000000000Octave-1-Lgradesÿmatrixþÿÿÿx@P@€N@@T@V@@Q@@V@€K@U@€U@U@ÀQ@@T@U@@T@€S@ÀP@X@€P@@R@ÀR@€M@ÀQ@@Q@€O@ÀS@S@€O@@U@ÀU@V@T@ÀQ@@P@U@ÀQ@ÀR@@T@ÀS@P@@P@U@@S@€Q@ÀR@U@ÀR@@R@W@€V@ÀS@T@ÀQ@@R@ÀQ@M@ÀS@@R@P@@S@€T@@T@€M@K@€T@€L@ÀS@ÀS@@R@€R@€T@€O@P@@R@@Q@ÀU@Q@@T@@R@ÀT@@R@T@@R@@R@ÀQ@€P@€S@P@€R@Q@ÀP@ÀR@ÀR@T@@U@€R@S@T@@S@@W@€Q@€U@T@@T@ÀT@Q@N@@U@P@€R@€T@@T@@S@€P@@U@ÀR@@T@@Q@N@ÀT@R@@S@€R@T@S@@S@@W@P@ÀT@ÀR@€T@€Q@V@€S@@S@€P@€R@ÀU@@Q@ÀR@Q@P@ÀQ@Q@ÀR@ÀT@T@ÀP@€S@U@ÀV@S@@R@ÀP@@V@ÀP@ÀR@R@@Q@ÀQ@R@R@@T@@S@ÀQ@@S@@S@ÀP@ÀR@ÀT@€R@€R@@T@ÀP@@Q@€O@@R@@S@ÀS@€U@@R@U@ÀQ@ÀP@U@ÀQ@€S@Q@Q@@S@U@€Q@@Q@@T@@T@@S@€P@S@ÀR@ÀT@@S@€S@@S@€R@R@Q@ÀR@€S@ÀQ@€R@€Q@V@€Q@€R@S@ÀQ@ÀS@O@@S@T@ÀQ@R@ÀS@S@@S@ÀQ@P@€R@€O@Q@€S@@Q@@R@ÀS@S@U@@V@€O@@Q@ÀS@ÀT@@Q@€Q@ÀQ@T@€R@€S@N@T@@T@€U@@R@T@T@@T@€V@@R@T@ÀR@P@€T@ÀP@@Q@Q@ÀR@ÀV@T@O@V@@U@@V@@R@S@@Q@@U@O@@R@L@@P@€S@@P@U@@U@T@ÀP@€M@ÀR@€T@ÀQ@V@@P@ÀT@@Q@€T@€T@€P@€Q@€R@ÀT@ÀS@S@€R@€P@ÀP@@Q@€O@€Q@ÀQ@€T@€S@€U@R@@S@@R@ÀQ@€S@€R@€S@€S@@V@€Q@S@ÀS@€Q@ÀR@U@@S@ÀR@ÀR@@R@ÀP@@T@R@R@€U@N@ÀS@€M@R@T@ÀP@@R@R@@T@€S@@S@€Q@R@€P@€S@R@Q@ÀR@€S@@R@@U@ÀT@@R@€R@€U@@S@ÀR@€P@€R@V@@Q@@S@I@@S@ÀU@W@@T@ÀS@€R@ÀT@U@S@€S@Q@S@@U@N@€S@N@@Q@€V@@Q@€P@ÀS@€V@@T@ÀR@€V@P@ÀT@@R@@P@@Q@P@@Q@ÀQ@€S@€U@€T@Q@€M@@U@ÀQ@@R@€S@€Q@ÀS@J@S@V@R@@Q@R@€Q@@W@ÀV@V@S@€P@€O@ÀP@€T@ÀU@€Q@@U@V@€S@ÀP@S@€R@@R@@Q@€R@Q@ÀV@ÀR@€Q@@T@€R@P@ÀR@€R@@S@€R@ÀQ@@R@@T@Q@R@U@@Q@T@N@ÀR@€S@€P@U@@U@@Q@€R@€T@€P@Q@ÀQ@S@S@€P@€S@@W@R@@T@@U@€Q@€P@€R@@Q@@Q@Q@ÀS@ÀS@S@T@€O@€S@ÀS@@U@ÀS@ÀT@T@ÀS@ÀR@R@€T@€Q@€R@ÀS@ÀP@ÀR@€P@@S@ÀU@S@R@ÀT@T@@S@@R@ÀS@@P@ÀS@ÀP@@S@Q@€Q@ÀQ@R@€S@€T@ÀS@ÀQ@€Q@ÀS@R@€R@€U@Q@@R@€R@€T@U@P@ÀQ@€Q@ÀS@U@€R@@S@€Q@€O@@S@@Q@S@S@S@ÀS@@S@€Q@€R@ÀQ@R@S@Q@€R@T@€U@@S@ÀQ@R@S@ÀQ@@R@€S@ÀQ@R@@R@ÀP@@T@€Q@€Q@€S@@R@@R@ÀP@ÀR@ÀT@@R@@T@ÀT@ÀR@@S@@S@€R@€R@ÀP@R@R@@R@ÀR@T@@R@@S@@U@@R@ÀR@€T@ÀR@statistics-release-1.7.3/inst/datasets/fail_load_model.mdl000066400000000000000000000115431475240274700237270ustar00rootroot00000000000000# Created by Octave 9.1.0, Sun Aug 18 14:52:45 2024 EEST # name: classdef_name # type: string # elements: 1 # length: 17 ClassificationKNN # name: variable_name # type: matrix # rows: 100 # columns: 2 4.7000000000000002 1.3999999999999999 4.5 1.5 4.9000000000000004 1.5 4 1.3 4.5999999999999996 1.5 4.5 1.3 4.7000000000000002 1.6000000000000001 3.2999999999999998 1 4.5999999999999996 1.3 3.8999999999999999 1.3999999999999999 3.5 1 4.2000000000000002 1.5 4 1 4.7000000000000002 1.3999999999999999 3.6000000000000001 1.3 4.4000000000000004 1.3999999999999999 4.5 1.5 4.0999999999999996 1 4.5 1.5 3.8999999999999999 1.1000000000000001 4.7999999999999998 1.8 4 1.3 4.9000000000000004 1.5 4.7000000000000002 1.2 4.2999999999999998 1.3 4.4000000000000004 1.3999999999999999 4.7999999999999998 1.3999999999999999 5 1.7 4.5 1.5 3.5 1 3.7999999999999998 1.1000000000000001 3.7000000000000002 1 3.8999999999999999 1.2 5.0999999999999996 1.6000000000000001 4.5 1.5 4.5 1.6000000000000001 4.7000000000000002 1.5 4.4000000000000004 1.3 4.0999999999999996 1.3 4 1.3 4.4000000000000004 1.2 4.5999999999999996 1.3999999999999999 4 1.2 3.2999999999999998 1 4.2000000000000002 1.3 4.2000000000000002 1.2 4.2000000000000002 1.3 4.2999999999999998 1.3 3 1.1000000000000001 4.0999999999999996 1.3 6 2.5 5.0999999999999996 1.8999999999999999 5.9000000000000004 2.1000000000000001 5.5999999999999996 1.8 5.7999999999999998 2.2000000000000002 6.5999999999999996 2.1000000000000001 4.5 1.7 6.2999999999999998 1.8 5.7999999999999998 1.8 6.0999999999999996 2.5 5.0999999999999996 2 5.2999999999999998 1.8999999999999999 5.5 2.1000000000000001 5 2 5.0999999999999996 2.3999999999999999 5.2999999999999998 2.2999999999999998 5.5 1.8 6.7000000000000002 2.2000000000000002 6.9000000000000004 2.2999999999999998 5 1.5 5.7000000000000002 2.2999999999999998 4.9000000000000004 2 6.7000000000000002 2 4.9000000000000004 1.8 5.7000000000000002 2.1000000000000001 6 1.8 4.7999999999999998 1.8 4.9000000000000004 1.8 5.5999999999999996 2.1000000000000001 5.7999999999999998 1.6000000000000001 6.0999999999999996 1.8999999999999999 6.4000000000000004 2 5.5999999999999996 2.2000000000000002 5.0999999999999996 1.5 5.5999999999999996 1.3999999999999999 6.0999999999999996 2.2999999999999998 5.5999999999999996 2.3999999999999999 5.5 1.8 4.7999999999999998 1.8 5.4000000000000004 2.1000000000000001 5.5999999999999996 2.3999999999999999 5.0999999999999996 2.2999999999999998 5.0999999999999996 1.8999999999999999 5.9000000000000004 2.2999999999999998 5.7000000000000002 2.5 5.2000000000000002 2.2999999999999998 5 1.8999999999999999 5.2000000000000002 2 5.4000000000000004 2.2999999999999998 5.0999999999999996 1.8 # name: Y # type: matrix # rows: 100 # columns: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 # name: NumObservations # type: scalar 100 # name: RowsUsed # type: matrix # rows: 100 # columns: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 # name: Standardize # type: bool 1 # name: Sigma # type: matrix # rows: 1 # columns: 2 0.82557846264289036 0.42476850498628405 # name: Mu # type: matrix # rows: 1 # columns: 2 4.9060000000000006 1.6760000000000006 # name: NumPredictors # type: scalar 2 # name: PredictorNames # type: cell # rows: 1 # columns: 2 # name: # type: sq_string # elements: 1 # length: 2 x1 # name: # type: sq_string # elements: 1 # length: 2 x2 # name: ResponseName # type: string # elements: 1 # length: 1 Y # name: ClassNames # type: cell # rows: 2 # columns: 1 # name: # type: sq_string # elements: 1 # length: 1 0 # name: # type: sq_string # elements: 1 # length: 1 1 # name: Prior # type: matrix # rows: 2 # columns: 1 0.5 0.5 # name: Cost # type: matrix # rows: 2 # columns: 2 0 1 1 0 # name: ScoreTransform # type: sq_string # elements: 1 # length: 4 none # name: BreakTies # type: string # elements: 1 # length: 8 smallest # name: NumNeighbors # type: scalar 10 # name: Distance # type: string # elements: 1 # length: 9 euclidean # name: DistanceWeight # type: function handle @ @(x) x # name: DistParameter # type: matrix # rows: 0 # columns: 0 # name: NSMethod # type: string # elements: 1 # length: 10 exhaustive # name: IncludeTies # type: bool 0 # name: BucketSize # type: scalar 50 statistics-release-1.7.3/inst/datasets/fail_loadmodel.mdl000066400000000000000000000115311475240274700235650ustar00rootroot00000000000000# Created by Octave 9.1.0, Sun Aug 18 14:52:45 2024 EEST # name: classdef_name # type: string # elements: 1 # length: 19 ClassificationModel # name: X # type: matrix # rows: 100 # columns: 2 4.7000000000000002 1.3999999999999999 4.5 1.5 4.9000000000000004 1.5 4 1.3 4.5999999999999996 1.5 4.5 1.3 4.7000000000000002 1.6000000000000001 3.2999999999999998 1 4.5999999999999996 1.3 3.8999999999999999 1.3999999999999999 3.5 1 4.2000000000000002 1.5 4 1 4.7000000000000002 1.3999999999999999 3.6000000000000001 1.3 4.4000000000000004 1.3999999999999999 4.5 1.5 4.0999999999999996 1 4.5 1.5 3.8999999999999999 1.1000000000000001 4.7999999999999998 1.8 4 1.3 4.9000000000000004 1.5 4.7000000000000002 1.2 4.2999999999999998 1.3 4.4000000000000004 1.3999999999999999 4.7999999999999998 1.3999999999999999 5 1.7 4.5 1.5 3.5 1 3.7999999999999998 1.1000000000000001 3.7000000000000002 1 3.8999999999999999 1.2 5.0999999999999996 1.6000000000000001 4.5 1.5 4.5 1.6000000000000001 4.7000000000000002 1.5 4.4000000000000004 1.3 4.0999999999999996 1.3 4 1.3 4.4000000000000004 1.2 4.5999999999999996 1.3999999999999999 4 1.2 3.2999999999999998 1 4.2000000000000002 1.3 4.2000000000000002 1.2 4.2000000000000002 1.3 4.2999999999999998 1.3 3 1.1000000000000001 4.0999999999999996 1.3 6 2.5 5.0999999999999996 1.8999999999999999 5.9000000000000004 2.1000000000000001 5.5999999999999996 1.8 5.7999999999999998 2.2000000000000002 6.5999999999999996 2.1000000000000001 4.5 1.7 6.2999999999999998 1.8 5.7999999999999998 1.8 6.0999999999999996 2.5 5.0999999999999996 2 5.2999999999999998 1.8999999999999999 5.5 2.1000000000000001 5 2 5.0999999999999996 2.3999999999999999 5.2999999999999998 2.2999999999999998 5.5 1.8 6.7000000000000002 2.2000000000000002 6.9000000000000004 2.2999999999999998 5 1.5 5.7000000000000002 2.2999999999999998 4.9000000000000004 2 6.7000000000000002 2 4.9000000000000004 1.8 5.7000000000000002 2.1000000000000001 6 1.8 4.7999999999999998 1.8 4.9000000000000004 1.8 5.5999999999999996 2.1000000000000001 5.7999999999999998 1.6000000000000001 6.0999999999999996 1.8999999999999999 6.4000000000000004 2 5.5999999999999996 2.2000000000000002 5.0999999999999996 1.5 5.5999999999999996 1.3999999999999999 6.0999999999999996 2.2999999999999998 5.5999999999999996 2.3999999999999999 5.5 1.8 4.7999999999999998 1.8 5.4000000000000004 2.1000000000000001 5.5999999999999996 2.3999999999999999 5.0999999999999996 2.2999999999999998 5.0999999999999996 1.8999999999999999 5.9000000000000004 2.2999999999999998 5.7000000000000002 2.5 5.2000000000000002 2.2999999999999998 5 1.8999999999999999 5.2000000000000002 2 5.4000000000000004 2.2999999999999998 5.0999999999999996 1.8 # name: Y # type: matrix # rows: 100 # columns: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 # name: NumObservations # type: scalar 100 # name: RowsUsed # type: matrix # rows: 100 # columns: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 # name: Standardize # type: bool 1 # name: Sigma # type: matrix # rows: 1 # columns: 2 0.82557846264289036 0.42476850498628405 # name: Mu # type: matrix # rows: 1 # columns: 2 4.9060000000000006 1.6760000000000006 # name: NumPredictors # type: scalar 2 # name: PredictorNames # type: cell # rows: 1 # columns: 2 # name: # type: sq_string # elements: 1 # length: 2 x1 # name: # type: sq_string # elements: 1 # length: 2 x2 # name: ResponseName # type: string # elements: 1 # length: 1 Y # name: ClassNames # type: cell # rows: 2 # columns: 1 # name: # type: sq_string # elements: 1 # length: 1 0 # name: # type: sq_string # elements: 1 # length: 1 1 # name: Prior # type: matrix # rows: 2 # columns: 1 0.5 0.5 # name: Cost # type: matrix # rows: 2 # columns: 2 0 1 1 0 # name: ScoreTransform # type: sq_string # elements: 1 # length: 4 none # name: BreakTies # type: string # elements: 1 # length: 8 smallest # name: NumNeighbors # type: scalar 10 # name: Distance # type: string # elements: 1 # length: 9 euclidean # name: DistanceWeight # type: function handle @ @(x) x # name: DistParameter # type: matrix # rows: 0 # columns: 0 # name: NSMethod # type: string # elements: 1 # length: 10 exhaustive # name: IncludeTies # type: bool 0 # name: BucketSize # type: scalar 50 statistics-release-1.7.3/inst/datasets/fisheriris.mat000066400000000000000000000322551475240274700230140ustar00rootroot00000000000000Octave-1-Lmeasÿmatrixþÿÿÿ–ffffff@š™™™™™@ÍÌÌÌÌÌ@ffffff@@š™™™™™@ffffff@@š™™™™™@š™™™™™@š™™™™™@333333@333333@333333@333333@ÍÌÌÌÌÌ@š™™™™™@ffffff@ÍÌÌÌÌÌ@ffffff@š™™™™™@ffffff@ffffff@ffffff@333333@@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@š™™™™™@ÍÌÌÌÌÌ@@š™™™™™@@@š™™™™™@š™™™™™@ffffff@@@š™™™™™@@ffffff@333333@ffffff@ffffff@333333@@@š™™™™™@š™™™™™@@@ÍÌÌÌÌÌ@333333@š™™™™™@ffffff@ÍÌÌÌÌÌ@@š™™™™™@@ffffff@ffffff@ÍÌÌÌÌÌ@ffffff@333333@ÍÌÌÌÌÌ@ffffff@š™™™™™@ffffff@333333@ffffff@š™™™™™@ffffff@333333@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@@@333333@@š™™™™™@@ÍÌÌÌÌÌ@333333@ffffff@@@ffffff@333333@@ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@ÍÌÌÌÌÌ@333333@333333@ffffff@333333@@ffffff@š™™™™™@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@š™™™™™@333333@ÍÌÌÌÌÌ@333333@š™™™™™@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@š™™™™™@ffffff@ÍÌÌÌÌÌ@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ffffff@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@š™™™™™@š™™™™™@333333@ffffff@ÍÌÌÌÌÌ@333333@š™™™™™@@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@333333@333333@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@@ÍÌÌÌÌÌ@š™™™™™@ @@š™™™™™ @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @333333@333333 @333333 @333333@ÍÌÌÌÌÌ@š™™™™™ @333333 @@@@š™™™™™@333333@ @ffffff@ffffff@333333 @š™™™™™ @ÍÌÌÌÌÌ @ffffff @333333 @@333333 @ @333333 @š™™™™™ @ÍÌÌÌÌÌ@333333 @ffffff@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™ @ @ÍÌÌÌÌÌ @@333333 @ @ffffff@š™™™™™ @ @ffffff@@ffffff@š™™™™™ @š™™™™™ @ffffff @š™™™™™ @š™™™™™ @ÍÌÌÌÌÌ@ffffff@ffffff@ffffff@ffffff @333333@333333@š™™™™™@@@š™™™™™@333333@333333@ÍÌÌÌÌÌ@@š™™™™™@š™™™™™@@š™™™™™ @ffffff@@ffffff@333333@@ffffff@@333333@ÍÌÌÌÌÌ@333333@333333@š™™™™™@š™™™™™@@333333 @ÍÌÌÌÌÌ@ffffff@@@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ffffff@š™™™™™@@333333@333333@@ffffff@ffffff @š™™™™™@@333333@@@@333333@@ÍÌÌÌÌÌ @š™™™™™ @š™™™™™@@@ffffff@š™™™™™ @@ffffff@ÍÌÌÌÌÌ@š™™™™™@š™™™™™ @ffffff@ffffff@š™™™™™@ffffff @š™™™™™ @ffffff@@ffffff@@ffffff@ffffff@ffffff@ffffff@ÍÌÌÌÌÌ@@333333 @ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@š™™™™™@š™™™™™ @ffffff @@@@333333 @@ffffffö?ffffffö?ÍÌÌÌÌÌô?ø?ffffffö?333333û?ffffffö?ø?ffffffö?ø?ø?š™™™™™ù?ffffffö?š™™™™™ñ?333333ó?ø?ÍÌÌÌÌÌô?ffffffö?333333û?ø?333333û?ø?ð?333333û?ffffffþ?š™™™™™ù?š™™™™™ù?ø?ffffffö?š™™™™™ù?š™™™™™ù?ø?ø?ffffffö?ø?333333ó?ÍÌÌÌÌÌô?ffffffö?ÍÌÌÌÌÌô?ø?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?š™™™™™ù?ffffffþ?ffffffö?š™™™™™ù?ffffffö?ø?ffffffö?ÍÌÌÌÌÌ@@š™™™™™@@ffffff@@ÍÌÌÌÌÌ@ffffff @ffffff@333333@ @ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ @š™™™™™@@ffffff@@333333@333333@@š™™™™™@ÍÌÌÌÌÌ@333333@š™™™™™@333333@@@ @ffffff@š™™™™™ @333333@ffffff@@@ÍÌÌÌÌÌ@š™™™™™@ffffff@@š™™™™™@ffffff@@ffffff @ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@333333@@ffffff@@ffffff@š™™™™™@ffffff@333333@ffffff@@333333@333333@ffffff@ffffff@333333@@@ffffff@333333@@ÍÌÌÌÌÌ@š™™™™™@@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌ@š™™™™™@ÍÌÌÌÌÌ@@333333@š™™™™™@ffffff@333333@ffffff@š™™™™™@ffffff@ffffff@ffffff@ffffff@ffffff@@333333@š™™™™™@ffffff@ffffff@ffffff@š™™™™™@ÍÌÌÌÌÌ@ÍÌÌÌÌÌ@@ÍÌÌÌÌÌ@š™™™™™@ffffff@š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™Ù?333333Ó?š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™¹?š™™™™™É?š™™™™™Ù?š™™™™™Ù?333333Ó?333333Ó?333333Ó?š™™™™™É?š™™™™™Ù?š™™™™™É?à?š™™™™™É?š™™™™™É?š™™™™™Ù?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™Ù?š™™™™™¹?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™¹?š™™™™™É?š™™™™™É?333333Ó?333333Ó?š™™™™™É?333333ã?š™™™™™Ù?333333Ó?š™™™™™É?š™™™™™É?š™™™™™É?š™™™™™É?ffffffö?ø?ø?ÍÌÌÌÌÌô?ø?ÍÌÌÌÌÌô?š™™™™™ù?ð?ÍÌÌÌÌÌô?ffffffö?ð?ø?ð?ffffffö?ÍÌÌÌÌÌô?ffffffö?ø?ð?ø?š™™™™™ñ?ÍÌÌÌÌÌü?ÍÌÌÌÌÌô?ø?333333ó?ÍÌÌÌÌÌô?ffffffö?ffffffö?333333û?ø?ð?š™™™™™ñ?ð?333333ó?š™™™™™ù?ø?š™™™™™ù?ø?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?333333ó?ffffffö?333333ó?ð?ÍÌÌÌÌÌô?333333ó?ÍÌÌÌÌÌô?ÍÌÌÌÌÌô?š™™™™™ñ?ÍÌÌÌÌÌô?@ffffffþ?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?š™™™™™@ÍÌÌÌÌÌ@333333û?ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?@@ffffffþ?ÍÌÌÌÌÌ@@333333@ffffff@ÍÌÌÌÌÌü?š™™™™™@ffffff@ø?ffffff@@@ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@š™™™™™ù?ffffffþ?@š™™™™™@ø?ffffffö?ffffff@333333@ÍÌÌÌÌÌü?ÍÌÌÌÌÌü?ÍÌÌÌÌÌ@333333@ffffff@ffffffþ?ffffff@@ffffff@ffffffþ?@ffffff@ÍÌÌÌÌÌü?speciesÿcellþÿÿÿ–ÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿsetosaÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ versicolorÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicaÿ sq_stringþÿÿÿ virginicastatistics-release-1.7.3/inst/datasets/gradespaired.mat000066400000000000000000000036701475240274700232760ustar00rootroot00000000000000Octave-1-L gradespairedÿmatrixþÿÿÿx@P@€N@@T@V@@Q@@V@€K@U@€U@U@ÀQ@@T@U@@T@€S@ÀP@X@€P@@R@ÀR@€M@ÀQ@@Q@€O@ÀS@S@€O@@U@ÀU@V@T@ÀQ@@P@U@ÀQ@ÀR@@T@ÀS@P@@P@U@@S@€Q@ÀR@U@ÀR@@R@W@€V@ÀS@T@ÀQ@@R@ÀQ@M@ÀS@@R@P@@S@€T@@T@€M@K@€T@€L@ÀS@ÀS@@R@€R@€T@€O@P@@R@@Q@ÀU@Q@@T@@R@ÀT@@R@T@@R@@R@ÀQ@€P@€S@P@€R@Q@ÀP@ÀR@ÀR@T@@U@€R@S@T@@S@@W@€Q@€U@T@@T@ÀT@Q@N@@U@P@€R@€T@@T@@S@€P@@U@ÀR@@T@@Q@N@ÀT@R@ÀP@€N@€T@ÀU@@R@@V@€J@@V@€U@ÀT@ÀQ@ÀS@@T@@V@ÀT@Q@@W@P@€R@€S@L@@P@@P@@P@@T@€S@€O@€U@€U@ÀV@S@@R@€O@ÀT@@R@ÀS@€S@ÀT@€P@€P@U@€S@ÀQ@S@@U@€S@€S@€W@€V@@T@@T@Q@S@R@€M@@T@€R@O@@S@ÀT@T@@P@J@@T@L@S@ÀS@€R@ÀS@€T@N@@Q@@S@€Q@U@Q@€T@ÀR@ÀT@ÀR@€T@ÀQ@R@€Q@€P@ÀS@€P@€P@Q@ÀQ@@R@€S@€T@€U@ÀR@R@@T@€T@€W@R@@U@@T@ÀT@@U@ÀP@€N@W@€M@@T@@U@€U@€R@@P@€U@€S@@S@R@M@@U@ÀR@statistics-release-1.7.3/inst/datasets/hald.mat000066400000000000000000000046731475240274700215600ustar00rootroot00000000000000Octave-1-L Descriptionÿ sq_stringþÿÿÿ:= M icccc hh SW"dv RHW= u noooo ee ooEu. eai l gllll aa uofr2 fllP t ruuuu tt rdfi4 edeo i emmmm csen r,yr p dnnnn (o e,cgn eA,t l i1234 cf :Ht o n. l e e:::: a . H. c,1a n lh ,oa1 e 9n r t3342 /a fr1 :S6d e sCCCC gr H d t0 g aaaa md .Ce( a.C r (OOOO )e on1 t e e %.... :n Smi9 i m s )ASAS i tpn3 s e s :lili n eog2 t n i 2O2O g is,) i t o O2O2 ni", c n 3 3 a ot a D (.( f uiIp l a d (tFb t ronp t a tree e ,nd. T a t ri2t r u1 h a icOa Hos2 e = ca3- 1 .ft0 o = al d 8 r7 r lc(i 0 SPi- y citc toa1 iuea d arl2 w umtl a rt 1 i m rc y kla4 t sai s ean. h aicu ,nd llam d E uil E n mccs Cn g iaii eg i ntul mi n aemi en e t) c ne e e aa te r ) lt r i ue oi n m) nn g i g n H A o eC p f ah p e te l r m i r Ei c i vs a t ot t e lr i ) vy o e, n d s , haldÿmatrixþÿÿÿ @ð?&@&@@&@@ð?@5@ð?&@$@:@=@L@?@J@€K@ÀQ@?@K@€G@D@€P@Q@@.@ @ @@"@1@6@2@@7@"@ @N@J@4@€G@€@@6@@F@6@:@A@(@(@ S@33333“R@33333Z@fffffæU@š™™™™ùW@ÍÌÌÌÌL[@ÍÌÌÌ̬Y@ R@fffffFW@š™™™™ù\@33333óT@33333S\@š™™™™Y[@heatÿmatrixþÿÿÿ  S@33333“R@33333Z@fffffæU@š™™™™ùW@ÍÌÌÌÌL[@ÍÌÌÌ̬Y@ R@fffffFW@š™™™™ù\@33333óT@33333S\@š™™™™Y[@ ingredientsÿmatrixþÿÿÿ @ð?&@&@@&@@ð?@5@ð?&@$@:@=@L@?@J@€K@ÀQ@?@K@€G@D@€P@Q@@.@ @ @@"@1@6@2@@7@"@ @N@J@4@€G@€@@6@@F@6@:@A@(@(@statistics-release-1.7.3/inst/datasets/heart_scale.dat000066400000000000000000000660261475240274700231110ustar00rootroot00000000000000+1 1:0.708333 2:1 3:1 4:-0.320755 5:-0.105023 6:-1 7:1 8:-0.419847 9:-1 10:-0.225806 12:1 13:-1 -1 1:0.583333 2:-1 3:0.333333 4:-0.603774 5:1 6:-1 7:1 8:0.358779 9:-1 10:-0.483871 12:-1 13:1 +1 1:0.166667 2:1 3:-0.333333 4:-0.433962 5:-0.383562 6:-1 7:-1 8:0.0687023 9:-1 10:-0.903226 11:-1 12:-1 13:1 -1 1:0.458333 2:1 3:1 4:-0.358491 5:-0.374429 6:-1 7:-1 8:-0.480916 9:1 10:-0.935484 12:-0.333333 13:1 -1 1:0.875 2:-1 3:-0.333333 4:-0.509434 5:-0.347032 6:-1 7:1 8:-0.236641 9:1 10:-0.935484 11:-1 12:-0.333333 13:-1 -1 1:0.5 2:1 3:1 4:-0.509434 5:-0.767123 6:-1 7:-1 8:0.0534351 9:-1 10:-0.870968 11:-1 12:-1 13:1 +1 1:0.125 2:1 3:0.333333 4:-0.320755 5:-0.406393 6:1 7:1 8:0.0839695 9:1 10:-0.806452 12:-0.333333 13:0.5 +1 1:0.25 2:1 3:1 4:-0.698113 5:-0.484018 6:-1 7:1 8:0.0839695 9:1 10:-0.612903 12:-0.333333 13:1 +1 1:0.291667 2:1 3:1 4:-0.132075 5:-0.237443 6:-1 7:1 8:0.51145 9:-1 10:-0.612903 12:0.333333 13:1 +1 1:0.416667 2:-1 3:1 4:0.0566038 5:0.283105 6:-1 7:1 8:0.267176 9:-1 10:0.290323 12:1 13:1 -1 1:0.25 2:1 3:1 4:-0.226415 5:-0.506849 6:-1 7:-1 8:0.374046 9:-1 10:-0.83871 12:-1 13:1 -1 2:1 3:1 4:-0.0943396 5:-0.543379 6:-1 7:1 8:-0.389313 9:1 10:-1 11:-1 12:-1 13:1 -1 1:-0.375 2:1 3:0.333333 4:-0.132075 5:-0.502283 6:-1 7:1 8:0.664122 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.333333 2:1 3:-1 4:-0.245283 5:-0.506849 6:-1 7:-1 8:0.129771 9:-1 10:-0.16129 12:0.333333 13:-1 -1 1:0.166667 2:-1 3:1 4:-0.358491 5:-0.191781 6:-1 7:1 8:0.343511 9:-1 10:-1 11:-1 12:-0.333333 13:-1 -1 1:0.75 2:-1 3:1 4:-0.660377 5:-0.894977 6:-1 7:-1 8:-0.175573 9:-1 10:-0.483871 12:-1 13:-1 +1 1:-0.291667 2:1 3:1 4:-0.132075 5:-0.155251 6:-1 7:-1 8:-0.251908 9:1 10:-0.419355 12:0.333333 13:1 +1 2:1 3:1 4:-0.132075 5:-0.648402 6:1 7:1 8:0.282443 9:1 11:1 12:-1 13:1 -1 1:0.458333 2:1 3:-1 4:-0.698113 5:-0.611872 6:-1 7:1 8:0.114504 9:1 10:-0.419355 12:-1 13:-1 -1 1:-0.541667 2:1 3:-1 4:-0.132075 5:-0.666667 6:-1 7:-1 8:0.633588 9:1 10:-0.548387 11:-1 12:-1 13:1 +1 1:0.583333 2:1 3:1 4:-0.509434 5:-0.52968 6:-1 7:1 8:-0.114504 9:1 10:-0.16129 12:0.333333 13:1 -1 1:-0.208333 2:1 3:-0.333333 4:-0.320755 5:-0.456621 6:-1 7:1 8:0.664122 9:-1 10:-0.935484 12:-1 13:-1 -1 1:-0.416667 2:1 3:1 4:-0.603774 5:-0.191781 6:-1 7:-1 8:0.679389 9:-1 10:-0.612903 12:-1 13:-1 -1 1:-0.25 2:1 3:1 4:-0.660377 5:-0.643836 6:-1 7:-1 8:0.0992366 9:-1 10:-0.967742 11:-1 12:-1 13:-1 -1 1:0.0416667 2:-1 3:-0.333333 4:-0.283019 5:-0.260274 6:1 7:1 8:0.343511 9:1 10:-1 11:-1 12:-0.333333 13:-1 -1 1:-0.208333 2:-1 3:0.333333 4:-0.320755 5:-0.319635 6:-1 7:-1 8:0.0381679 9:-1 10:-0.935484 11:-1 12:-1 13:-1 -1 1:-0.291667 2:-1 3:1 4:-0.169811 5:-0.465753 6:-1 7:1 8:0.236641 9:1 10:-1 12:-1 13:-1 -1 1:-0.0833333 2:-1 3:0.333333 4:-0.509434 5:-0.228311 6:-1 7:1 8:0.312977 9:-1 10:-0.806452 11:-1 12:-1 13:-1 +1 1:0.208333 2:1 3:0.333333 4:-0.660377 5:-0.525114 6:-1 7:1 8:0.435115 9:-1 10:-0.193548 12:-0.333333 13:1 -1 1:0.75 2:-1 3:0.333333 4:-0.698113 5:-0.365297 6:1 7:1 8:-0.0992366 9:-1 10:-1 11:-1 12:-0.333333 13:-1 +1 1:0.166667 2:1 3:0.333333 4:-0.358491 5:-0.52968 6:-1 7:1 8:0.206107 9:-1 10:-0.870968 12:-0.333333 13:1 -1 1:0.541667 2:1 3:1 4:0.245283 5:-0.534247 6:-1 7:1 8:0.0229008 9:-1 10:-0.258065 11:-1 12:-1 13:0.5 -1 1:-0.666667 2:-1 3:0.333333 4:-0.509434 5:-0.593607 6:-1 7:-1 8:0.51145 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.25 2:1 3:1 4:0.433962 5:-0.086758 6:-1 7:1 8:0.0534351 9:1 10:0.0967742 11:1 12:-1 13:1 +1 1:-0.125 2:1 3:1 4:-0.0566038 5:-0.6621 6:-1 7:1 8:-0.160305 9:1 10:-0.709677 12:-1 13:1 +1 1:-0.208333 2:1 3:1 4:-0.320755 5:-0.406393 6:1 7:1 8:0.206107 9:1 10:-1 11:-1 12:0.333333 13:1 +1 1:0.333333 2:1 3:1 4:-0.132075 5:-0.630137 6:-1 7:1 8:0.0229008 9:1 10:-0.387097 11:-1 12:-0.333333 13:1 +1 1:0.25 2:1 3:-1 4:0.245283 5:-0.328767 6:-1 7:1 8:-0.175573 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.458333 2:1 3:0.333333 4:-0.320755 5:-0.753425 6:-1 7:-1 8:0.206107 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.208333 2:1 3:1 4:-0.471698 5:-0.561644 6:-1 7:1 8:0.755725 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.541667 2:1 3:1 4:0.0943396 5:-0.557078 6:-1 7:-1 8:0.679389 9:-1 10:-1 11:-1 12:-1 13:1 -1 1:0.375 2:-1 3:1 4:-0.433962 5:-0.621005 6:-1 7:-1 8:0.40458 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.375 2:1 3:0.333333 4:-0.320755 5:-0.511416 6:-1 7:-1 8:0.648855 9:1 10:-0.870968 11:-1 12:-1 13:-1 -1 1:-0.291667 2:1 3:-0.333333 4:-0.867925 5:-0.675799 6:1 7:-1 8:0.29771 9:-1 10:-1 11:-1 12:-1 13:1 +1 1:0.25 2:1 3:0.333333 4:-0.396226 5:-0.579909 6:1 7:-1 8:-0.0381679 9:-1 10:-0.290323 12:-0.333333 13:0.5 -1 1:0.208333 2:1 3:0.333333 4:-0.132075 5:-0.611872 6:1 7:1 8:0.435115 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.166667 2:1 3:0.333333 4:-0.54717 5:-0.894977 6:-1 7:1 8:-0.160305 9:-1 10:-0.741935 11:-1 12:1 13:-1 +1 1:-0.375 2:1 3:1 4:-0.698113 5:-0.675799 6:-1 7:1 8:0.618321 9:-1 10:-1 11:-1 12:-0.333333 13:-1 +1 1:0.541667 2:1 3:-0.333333 4:0.245283 5:-0.452055 6:-1 7:-1 8:-0.251908 9:1 10:-1 12:1 13:0.5 +1 1:0.5 2:-1 3:1 4:0.0566038 5:-0.547945 6:-1 7:1 8:-0.343511 9:-1 10:-0.677419 12:1 13:1 +1 1:-0.458333 2:1 3:1 4:-0.207547 5:-0.136986 6:-1 7:-1 8:-0.175573 9:1 10:-0.419355 12:-1 13:0.5 -1 1:-0.0416667 2:1 3:-0.333333 4:-0.358491 5:-0.639269 6:1 7:-1 8:0.725191 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.5 2:-1 3:0.333333 4:-0.132075 5:0.328767 6:1 7:1 8:0.312977 9:-1 10:-0.741935 11:-1 12:-0.333333 13:-1 -1 1:0.416667 2:-1 3:-0.333333 4:-0.132075 5:-0.684932 6:-1 7:-1 8:0.648855 9:-1 10:-1 11:-1 12:0.333333 13:-1 -1 1:-0.333333 2:-1 3:-0.333333 4:-0.320755 5:-0.506849 6:-1 7:1 8:0.587786 9:-1 10:-0.806452 12:-1 13:-1 -1 1:-0.5 2:-1 3:-0.333333 4:-0.792453 5:-0.671233 6:-1 7:-1 8:0.480916 9:-1 10:-1 11:-1 12:-0.333333 13:-1 +1 1:0.333333 2:1 3:1 4:-0.169811 5:-0.817352 6:-1 7:1 8:-0.175573 9:1 10:0.16129 12:-0.333333 13:-1 -1 1:0.291667 2:-1 3:0.333333 4:-0.509434 5:-0.762557 6:1 7:-1 8:-0.618321 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.25 2:-1 3:1 4:0.509434 5:-0.438356 6:-1 7:-1 8:0.0992366 9:1 10:-1 12:-1 13:-1 +1 1:0.375 2:1 3:-0.333333 4:-0.509434 5:-0.292237 6:-1 7:1 8:-0.51145 9:-1 10:-0.548387 12:-0.333333 13:1 -1 1:0.166667 2:1 3:0.333333 4:0.0566038 5:-1 6:1 7:-1 8:0.557252 9:-1 10:-0.935484 11:-1 12:-0.333333 13:1 +1 1:-0.0833333 2:-1 3:1 4:-0.320755 5:-0.182648 6:-1 7:-1 8:0.0839695 9:1 10:-0.612903 12:-1 13:1 -1 1:-0.375 2:1 3:0.333333 4:-0.509434 5:-0.543379 6:-1 7:-1 8:0.496183 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.291667 2:-1 3:-1 4:0.0566038 5:-0.479452 6:-1 7:-1 8:0.526718 9:-1 10:-0.709677 11:-1 12:-1 13:-1 -1 1:0.416667 2:1 3:-1 4:-0.0377358 5:-0.511416 6:1 7:1 8:0.206107 9:-1 10:-0.258065 11:1 12:-1 13:0.5 +1 1:0.166667 2:1 3:1 4:0.0566038 5:-0.315068 6:-1 7:1 8:-0.374046 9:1 10:-0.806452 12:-0.333333 13:0.5 -1 1:-0.0833333 2:1 3:1 4:-0.132075 5:-0.383562 6:-1 7:1 8:0.755725 9:1 10:-1 11:-1 12:-1 13:-1 +1 1:0.208333 2:-1 3:-0.333333 4:-0.207547 5:-0.118721 6:1 7:1 8:0.236641 9:-1 10:-1 11:-1 12:0.333333 13:-1 -1 1:-0.375 2:-1 3:0.333333 4:-0.54717 5:-0.47032 6:-1 7:-1 8:0.19084 9:-1 10:-0.903226 12:-0.333333 13:-1 +1 1:-0.25 2:1 3:0.333333 4:-0.735849 5:-0.465753 6:-1 7:-1 8:0.236641 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.333333 2:1 3:1 4:-0.509434 5:-0.388128 6:-1 7:-1 8:0.0534351 9:1 10:0.16129 12:-0.333333 13:1 -1 1:0.166667 2:-1 3:1 4:-0.509434 5:0.0410959 6:-1 7:-1 8:0.40458 9:1 10:-0.806452 11:-1 12:-1 13:-1 -1 1:0.708333 2:1 3:-0.333333 4:0.169811 5:-0.456621 6:-1 7:1 8:0.0992366 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.958333 2:-1 3:0.333333 4:-0.132075 5:-0.675799 6:-1 8:-0.312977 9:-1 10:-0.645161 12:-1 13:-1 -1 1:0.583333 2:-1 3:1 4:-0.773585 5:-0.557078 6:-1 7:-1 8:0.0839695 9:-1 10:-0.903226 11:-1 12:0.333333 13:-1 +1 1:-0.333333 2:1 3:1 4:-0.0943396 5:-0.164384 6:-1 7:1 8:0.160305 9:1 10:-1 12:1 13:1 -1 1:-0.333333 2:1 3:1 4:-0.811321 5:-0.625571 6:-1 7:1 8:0.175573 9:1 10:-0.0322581 12:-1 13:-1 -1 1:-0.583333 2:-1 3:0.333333 4:-1 5:-0.666667 6:-1 7:-1 8:0.648855 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.458333 2:-1 3:0.333333 4:-0.509434 5:-0.621005 6:-1 7:-1 8:0.557252 9:-1 10:-1 12:-1 13:-1 -1 1:0.125 2:1 3:-0.333333 4:-0.509434 5:-0.497717 6:-1 7:-1 8:0.633588 9:-1 10:-0.741935 11:-1 12:-1 13:-1 +1 1:0.208333 2:1 3:1 4:-0.0188679 5:-0.579909 6:-1 7:-1 8:-0.480916 9:-1 10:-0.354839 12:-0.333333 13:1 +1 1:-0.75 2:1 3:1 4:-0.509434 5:-0.671233 6:-1 7:-1 8:-0.0992366 9:1 10:-0.483871 12:-1 13:1 +1 1:0.208333 2:1 3:1 4:0.0566038 5:-0.342466 6:-1 7:1 8:-0.389313 9:1 10:-0.741935 11:-1 12:-1 13:1 -1 1:-0.5 2:1 3:0.333333 4:-0.320755 5:-0.598174 6:-1 7:1 8:0.480916 9:-1 10:-0.354839 12:-1 13:-1 -1 1:0.166667 2:1 3:1 4:-0.698113 5:-0.657534 6:-1 7:-1 8:-0.160305 9:1 10:-0.516129 12:-1 13:0.5 -1 1:-0.458333 2:1 3:-1 4:0.0188679 5:-0.461187 6:-1 7:1 8:0.633588 9:-1 10:-0.741935 11:-1 12:0.333333 13:-1 -1 1:0.375 2:1 3:-0.333333 4:-0.358491 5:-0.625571 6:1 7:1 8:0.0534351 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.25 2:1 3:-1 4:0.584906 5:-0.342466 6:-1 7:1 8:0.129771 9:-1 10:0.354839 11:1 12:-1 13:1 -1 1:-0.5 2:-1 3:-0.333333 4:-0.396226 5:-0.178082 6:-1 7:-1 8:0.40458 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.125 2:1 3:1 4:0.0566038 5:-0.465753 6:-1 7:1 8:-0.129771 9:-1 10:-0.16129 12:-1 13:1 -1 1:0.25 2:1 3:-0.333333 4:-0.132075 5:-0.56621 6:-1 7:-1 8:0.419847 9:1 10:-1 11:-1 12:-1 13:-1 +1 1:0.333333 2:-1 3:1 4:-0.320755 5:-0.0684932 6:-1 7:1 8:0.496183 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.0416667 2:1 3:1 4:-0.433962 5:-0.360731 6:-1 7:1 8:-0.419847 9:1 10:-0.290323 12:-0.333333 13:1 +1 1:0.0416667 2:1 3:1 4:-0.698113 5:-0.634703 6:-1 7:1 8:-0.435115 9:1 10:-1 12:-0.333333 13:-1 +1 1:-0.0416667 2:1 3:1 4:-0.415094 5:-0.607306 6:-1 7:-1 8:0.480916 9:-1 10:-0.677419 11:-1 12:0.333333 13:1 +1 1:-0.25 2:1 3:1 4:-0.698113 5:-0.319635 6:-1 7:1 8:-0.282443 9:1 10:-0.677419 12:-0.333333 13:-1 -1 1:0.541667 2:1 3:1 4:-0.509434 5:-0.196347 6:-1 7:1 8:0.221374 9:-1 10:-0.870968 12:-1 13:-1 +1 1:0.208333 2:1 3:1 4:-0.886792 5:-0.506849 6:-1 7:-1 8:0.29771 9:-1 10:-0.967742 11:-1 12:-0.333333 13:1 -1 1:0.458333 2:-1 3:0.333333 4:-0.132075 5:-0.146119 6:-1 7:-1 8:-0.0534351 9:-1 10:-0.935484 11:-1 12:-1 13:1 -1 1:-0.125 2:-1 3:-0.333333 4:-0.509434 5:-0.461187 6:-1 7:-1 8:0.389313 9:-1 10:-0.645161 11:-1 12:-1 13:-1 -1 1:-0.375 2:-1 3:0.333333 4:-0.735849 5:-0.931507 6:-1 7:-1 8:0.587786 9:-1 10:-0.806452 12:-1 13:-1 +1 1:0.583333 2:1 3:1 4:-0.509434 5:-0.493151 6:-1 7:-1 8:-1 9:-1 10:-0.677419 12:-1 13:-1 -1 1:-0.166667 2:-1 3:1 4:-0.320755 5:-0.347032 6:-1 7:-1 8:0.40458 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.166667 2:1 3:1 4:0.339623 5:-0.255708 6:1 7:1 8:-0.19084 9:-1 10:-0.677419 12:1 13:1 +1 1:0.416667 2:1 3:1 4:-0.320755 5:-0.415525 6:-1 7:1 8:0.160305 9:-1 10:-0.548387 12:-0.333333 13:1 +1 1:-0.208333 2:1 3:1 4:-0.433962 5:-0.324201 6:-1 7:1 8:0.450382 9:-1 10:-0.83871 12:-1 13:1 -1 1:-0.0833333 2:1 3:0.333333 4:-0.886792 5:-0.561644 6:-1 7:-1 8:0.0992366 9:1 10:-0.612903 12:-1 13:-1 +1 1:0.291667 2:-1 3:1 4:0.0566038 5:-0.39726 6:-1 7:1 8:0.312977 9:-1 10:-0.16129 12:0.333333 13:1 +1 1:0.25 2:1 3:1 4:-0.132075 5:-0.767123 6:-1 7:-1 8:0.389313 9:1 10:-1 11:-1 12:-0.333333 13:1 -1 1:-0.333333 2:-1 3:-0.333333 4:-0.660377 5:-0.844749 6:-1 7:-1 8:0.0229008 9:-1 10:-1 12:-1 13:-1 +1 1:0.0833333 2:-1 3:1 4:0.622642 5:-0.0821918 6:-1 8:-0.29771 9:1 10:0.0967742 12:-1 13:-1 -1 1:-0.5 2:1 3:-0.333333 4:-0.698113 5:-0.502283 6:-1 7:-1 8:0.251908 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.291667 2:-1 3:1 4:0.207547 5:-0.182648 6:-1 7:1 8:0.374046 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.0416667 2:-1 3:0.333333 4:-0.226415 5:-0.187215 6:1 7:-1 8:0.51145 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.458333 2:1 3:-0.333333 4:-0.509434 5:-0.228311 6:-1 7:-1 8:0.389313 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.166667 2:-1 3:-0.333333 4:-0.245283 5:-0.3379 6:-1 7:-1 8:0.389313 9:-1 10:-1 12:-1 13:-1 +1 1:-0.291667 2:1 3:1 4:-0.509434 5:-0.438356 6:-1 7:1 8:0.114504 9:-1 10:-0.741935 11:-1 12:-1 13:1 +1 1:0.125 2:-1 3:1 4:1 5:-0.260274 6:1 7:1 8:-0.0534351 9:1 10:0.290323 11:1 12:0.333333 13:1 -1 1:0.541667 2:-1 3:-1 4:0.0566038 5:-0.543379 6:-1 7:-1 8:-0.343511 9:-1 10:-0.16129 11:1 12:-1 13:-1 +1 1:0.125 2:1 3:1 4:-0.320755 5:-0.283105 6:1 7:1 8:-0.51145 9:1 10:-0.483871 11:1 12:-1 13:1 +1 1:-0.166667 2:1 3:0.333333 4:-0.509434 5:-0.716895 6:-1 7:-1 8:0.0381679 9:-1 10:-0.354839 12:1 13:1 +1 1:0.0416667 2:1 3:1 4:-0.471698 5:-0.269406 6:-1 7:1 8:-0.312977 9:1 10:0.0322581 12:0.333333 13:-1 +1 1:0.166667 2:1 3:1 4:0.0943396 5:-0.324201 6:-1 7:-1 8:-0.740458 9:1 10:-0.612903 12:-0.333333 13:1 -1 1:0.5 2:-1 3:0.333333 4:0.245283 5:0.0684932 6:-1 7:1 8:0.221374 9:-1 10:-0.741935 11:-1 12:-1 13:-1 -1 1:0.0416667 2:1 3:0.333333 4:-0.415094 5:-0.328767 6:-1 7:1 8:0.236641 9:-1 10:-0.83871 11:1 12:-0.333333 13:-1 -1 1:0.0416667 2:-1 3:0.333333 4:0.245283 5:-0.657534 6:-1 7:-1 8:0.40458 9:-1 10:-1 11:-1 12:-0.333333 13:-1 +1 1:0.375 2:1 3:1 4:-0.509434 5:-0.356164 6:-1 7:-1 8:-0.572519 9:1 10:-0.419355 12:0.333333 13:1 -1 1:-0.0416667 2:-1 3:0.333333 4:-0.207547 5:-0.680365 6:-1 7:1 8:0.496183 9:-1 10:-0.967742 12:-1 13:-1 -1 1:-0.0416667 2:1 3:-0.333333 4:-0.245283 5:-0.657534 6:-1 7:-1 8:0.328244 9:-1 10:-0.741935 11:-1 12:-0.333333 13:-1 +1 1:0.291667 2:1 3:1 4:-0.566038 5:-0.525114 6:1 7:-1 8:0.358779 9:1 10:-0.548387 11:-1 12:0.333333 13:1 +1 1:0.416667 2:-1 3:1 4:-0.735849 5:-0.347032 6:-1 7:-1 8:0.496183 9:1 10:-0.419355 12:0.333333 13:-1 +1 1:0.541667 2:1 3:1 4:-0.660377 5:-0.607306 6:-1 7:1 8:-0.0687023 9:1 10:-0.967742 11:-1 12:-0.333333 13:-1 -1 1:-0.458333 2:1 3:1 4:-0.132075 5:-0.543379 6:-1 7:-1 8:0.633588 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.458333 2:1 3:1 4:-0.509434 5:-0.452055 6:-1 7:1 8:-0.618321 9:1 10:-0.290323 11:1 12:-0.333333 13:-1 -1 1:0.0416667 2:1 3:0.333333 4:0.0566038 5:-0.515982 6:-1 7:1 8:0.435115 9:-1 10:-0.483871 11:-1 12:-1 13:1 -1 1:-0.291667 2:-1 3:0.333333 4:-0.0943396 5:-0.767123 6:-1 7:1 8:0.358779 9:1 10:-0.548387 11:1 12:-1 13:-1 -1 1:0.583333 2:-1 3:0.333333 4:0.0943396 5:-0.310502 6:-1 7:-1 8:0.541985 9:-1 10:-1 11:-1 12:-0.333333 13:-1 +1 1:0.125 2:1 3:1 4:-0.415094 5:-0.438356 6:1 7:1 8:0.114504 9:1 10:-0.612903 12:-0.333333 13:-1 -1 1:-0.791667 2:-1 3:-0.333333 4:-0.54717 5:-0.616438 6:-1 7:-1 8:0.847328 9:-1 10:-0.774194 11:-1 12:-1 13:-1 -1 1:0.166667 2:1 3:1 4:-0.283019 5:-0.630137 6:-1 7:-1 8:0.480916 9:1 10:-1 11:-1 12:-1 13:1 +1 1:0.458333 2:1 3:1 4:-0.0377358 5:-0.607306 6:-1 7:1 8:-0.0687023 9:-1 10:-0.354839 12:0.333333 13:0.5 -1 1:0.25 2:1 3:1 4:-0.169811 5:-0.3379 6:-1 7:1 8:0.694656 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.125 2:1 3:0.333333 4:-0.132075 5:-0.511416 6:-1 7:-1 8:0.40458 9:-1 10:-0.806452 12:-0.333333 13:1 -1 1:-0.0833333 2:1 3:-1 4:-0.415094 5:-0.60274 6:-1 7:1 8:-0.175573 9:1 10:-0.548387 11:-1 12:-0.333333 13:-1 +1 1:0.0416667 2:1 3:-0.333333 4:0.849057 5:-0.283105 6:-1 7:1 8:0.89313 9:-1 10:-1 11:-1 12:-0.333333 13:1 +1 2:1 3:1 4:-0.45283 5:-0.287671 6:-1 7:-1 8:-0.633588 9:1 10:-0.354839 12:0.333333 13:1 +1 1:-0.0416667 2:1 3:1 4:-0.660377 5:-0.525114 6:-1 7:-1 8:0.358779 9:-1 10:-1 11:-1 12:-0.333333 13:-1 +1 1:-0.541667 2:1 3:1 4:-0.698113 5:-0.812785 6:-1 7:1 8:-0.343511 9:1 10:-0.354839 12:-1 13:1 +1 1:0.208333 2:1 3:0.333333 4:-0.283019 5:-0.552511 6:-1 7:1 8:0.557252 9:-1 10:0.0322581 11:-1 12:0.333333 13:1 -1 1:-0.5 2:-1 3:0.333333 4:-0.660377 5:-0.351598 6:-1 7:1 8:0.541985 9:1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.5 2:1 3:0.333333 4:-0.660377 5:-0.43379 6:-1 7:-1 8:0.648855 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.125 2:-1 3:0.333333 4:-0.509434 5:-0.575342 6:-1 7:-1 8:0.328244 9:-1 10:-0.483871 12:-1 13:-1 -1 1:0.0416667 2:-1 3:0.333333 4:-0.735849 5:-0.356164 6:-1 7:1 8:0.465649 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.458333 2:-1 3:1 4:-0.320755 5:-0.191781 6:-1 7:-1 8:-0.221374 9:-1 10:-0.354839 12:0.333333 13:-1 -1 1:-0.0833333 2:-1 3:0.333333 4:-0.320755 5:-0.406393 6:-1 7:1 8:0.19084 9:-1 10:-0.83871 11:-1 12:-1 13:-1 -1 1:-0.291667 2:-1 3:-0.333333 4:-0.792453 5:-0.643836 6:-1 7:-1 8:0.541985 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.0833333 2:1 3:1 4:-0.132075 5:-0.584475 6:-1 7:-1 8:-0.389313 9:1 10:0.806452 11:1 12:-1 13:1 -1 1:-0.333333 2:1 3:-0.333333 4:-0.358491 5:-0.16895 6:-1 7:1 8:0.51145 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:0.125 2:1 3:-1 4:-0.509434 5:-0.694064 6:-1 7:1 8:0.389313 9:-1 10:-0.387097 12:-1 13:1 +1 1:0.541667 2:-1 3:1 4:0.584906 5:-0.534247 6:1 7:-1 8:0.435115 9:1 10:-0.677419 12:0.333333 13:1 +1 1:-0.625 2:1 3:-1 4:-0.509434 5:-0.520548 6:-1 7:-1 8:0.694656 9:1 10:0.225806 12:-1 13:1 +1 1:0.375 2:-1 3:1 4:0.0566038 5:-0.461187 6:-1 7:-1 8:0.267176 9:1 10:-0.548387 12:-1 13:-1 -1 1:0.0833333 2:1 3:-0.333333 4:-0.320755 5:-0.378995 6:-1 7:-1 8:0.282443 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.208333 2:1 3:1 4:-0.358491 5:-0.392694 6:-1 7:1 8:-0.0992366 9:1 10:-0.0322581 12:0.333333 13:1 -1 1:-0.416667 2:1 3:1 4:-0.698113 5:-0.611872 6:-1 7:-1 8:0.374046 9:-1 10:-1 11:-1 12:-1 13:1 -1 1:0.458333 2:-1 3:1 4:0.622642 5:-0.0913242 6:-1 7:-1 8:0.267176 9:1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.125 2:-1 3:1 4:-0.698113 5:-0.415525 6:-1 7:1 8:0.343511 9:-1 10:-1 11:-1 12:-1 13:-1 -1 2:1 3:0.333333 4:-0.320755 5:-0.675799 6:1 7:1 8:0.236641 9:-1 10:-0.612903 11:1 12:-1 13:-1 -1 1:-0.333333 2:-1 3:1 4:-0.169811 5:-0.497717 6:-1 7:1 8:0.236641 9:1 10:-0.935484 12:-1 13:-1 +1 1:0.5 2:1 3:-1 4:-0.169811 5:-0.287671 6:1 7:1 8:0.572519 9:-1 10:-0.548387 12:-0.333333 13:-1 -1 1:0.666667 2:1 3:-1 4:0.245283 5:-0.506849 6:1 7:1 8:-0.0839695 9:-1 10:-0.967742 12:-0.333333 13:-1 +1 1:0.666667 2:1 3:0.333333 4:-0.132075 5:-0.415525 6:-1 7:1 8:0.145038 9:-1 10:-0.354839 12:1 13:1 +1 1:0.583333 2:1 3:1 4:-0.886792 5:-0.210046 6:-1 7:1 8:-0.175573 9:1 10:-0.709677 12:0.333333 13:-1 -1 1:0.625 2:-1 3:0.333333 4:-0.509434 5:-0.611872 6:-1 7:1 8:-0.328244 9:-1 10:-0.516129 12:-1 13:-1 -1 1:-0.791667 2:1 3:-1 4:-0.54717 5:-0.744292 6:-1 7:1 8:0.572519 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.375 2:-1 3:1 4:-0.169811 5:-0.232877 6:1 7:-1 8:-0.465649 9:-1 10:-0.387097 12:1 13:-1 +1 1:-0.0833333 2:1 3:1 4:-0.132075 5:-0.214612 6:-1 7:-1 8:-0.221374 9:1 10:0.354839 12:1 13:1 +1 1:-0.291667 2:1 3:0.333333 4:0.0566038 5:-0.520548 6:-1 7:-1 8:0.160305 9:-1 10:0.16129 12:-1 13:-1 +1 1:0.583333 2:1 3:1 4:-0.415094 5:-0.415525 6:1 7:-1 8:0.40458 9:-1 10:-0.935484 12:0.333333 13:1 -1 1:-0.125 2:1 3:0.333333 4:-0.339623 5:-0.680365 6:-1 7:-1 8:0.40458 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.458333 2:1 3:0.333333 4:-0.509434 5:-0.479452 6:1 7:-1 8:0.877863 9:-1 10:-0.741935 11:1 12:-1 13:1 +1 1:0.125 2:-1 3:1 4:-0.245283 5:0.292237 6:-1 7:1 8:0.206107 9:1 10:-0.387097 12:0.333333 13:1 +1 1:-0.5 2:1 3:1 4:-0.698113 5:-0.789954 6:-1 7:1 8:0.328244 9:-1 10:-1 11:-1 12:-1 13:1 -1 1:-0.458333 2:-1 3:1 4:-0.849057 5:-0.365297 6:-1 7:1 8:-0.221374 9:-1 10:-0.806452 12:-1 13:-1 -1 2:1 3:0.333333 4:-0.320755 5:-0.452055 6:1 7:1 8:0.557252 9:-1 10:-1 11:-1 12:1 13:-1 -1 1:-0.416667 2:1 3:0.333333 4:-0.320755 5:-0.136986 6:-1 7:-1 8:0.389313 9:-1 10:-0.387097 11:-1 12:-0.333333 13:-1 +1 1:0.125 2:1 3:1 4:-0.283019 5:-0.73516 6:-1 7:1 8:-0.480916 9:1 10:-0.322581 12:-0.333333 13:0.5 -1 1:-0.0416667 2:1 3:1 4:-0.735849 5:-0.511416 6:1 7:-1 8:0.160305 9:-1 10:-0.967742 11:-1 12:1 13:1 -1 1:0.375 2:-1 3:1 4:-0.132075 5:0.223744 6:-1 7:1 8:0.312977 9:-1 10:-0.612903 12:-1 13:-1 +1 1:0.708333 2:1 3:0.333333 4:0.245283 5:-0.347032 6:-1 7:-1 8:-0.374046 9:1 10:-0.0645161 12:-0.333333 13:1 -1 1:0.0416667 2:1 3:1 4:-0.132075 5:-0.484018 6:-1 7:-1 8:0.358779 9:-1 10:-0.612903 11:-1 12:-1 13:-1 +1 1:0.708333 2:1 3:1 4:-0.0377358 5:-0.780822 6:-1 7:-1 8:-0.175573 9:1 10:-0.16129 11:1 12:-1 13:1 -1 1:0.0416667 2:1 3:-0.333333 4:-0.735849 5:-0.164384 6:-1 7:-1 8:0.29771 9:-1 10:-1 11:-1 12:-1 13:1 +1 1:-0.75 2:1 3:1 4:-0.396226 5:-0.287671 6:-1 7:1 8:0.29771 9:1 10:-1 11:-1 12:-1 13:1 -1 1:-0.208333 2:1 3:0.333333 4:-0.433962 5:-0.410959 6:1 7:-1 8:0.587786 9:-1 10:-1 11:-1 12:0.333333 13:-1 -1 1:0.0833333 2:-1 3:-0.333333 4:-0.226415 5:-0.43379 6:-1 7:1 8:0.374046 9:-1 10:-0.548387 12:-1 13:-1 -1 1:0.208333 2:-1 3:1 4:-0.886792 5:-0.442922 6:-1 7:1 8:-0.221374 9:-1 10:-0.677419 12:-1 13:-1 -1 1:0.0416667 2:-1 3:0.333333 4:-0.698113 5:-0.598174 6:-1 7:-1 8:0.328244 9:-1 10:-0.483871 12:-1 13:-1 -1 1:0.666667 2:-1 3:-1 4:-0.132075 5:-0.484018 6:-1 7:-1 8:0.221374 9:-1 10:-0.419355 11:-1 12:0.333333 13:-1 +1 1:1 2:1 3:1 4:-0.415094 5:-0.187215 6:-1 7:1 8:0.389313 9:1 10:-1 11:-1 12:1 13:-1 -1 1:0.625 2:1 3:0.333333 4:-0.54717 5:-0.310502 6:-1 7:-1 8:0.221374 9:-1 10:-0.677419 11:-1 12:-0.333333 13:1 +1 1:0.208333 2:1 3:1 4:-0.415094 5:-0.205479 6:-1 7:1 8:0.526718 9:-1 10:-1 11:-1 12:0.333333 13:1 +1 1:0.291667 2:1 3:1 4:-0.415094 5:-0.39726 6:-1 7:1 8:0.0687023 9:1 10:-0.0967742 12:-0.333333 13:1 +1 1:-0.0833333 2:1 3:1 4:-0.132075 5:-0.210046 6:-1 7:-1 8:0.557252 9:1 10:-0.483871 11:-1 12:-1 13:1 +1 1:0.0833333 2:1 3:1 4:0.245283 5:-0.255708 6:-1 7:1 8:0.129771 9:1 10:-0.741935 12:-0.333333 13:1 -1 1:-0.0416667 2:1 3:-1 4:0.0943396 5:-0.214612 6:1 7:-1 8:0.633588 9:-1 10:-0.612903 12:-1 13:1 -1 1:0.291667 2:-1 3:0.333333 4:-0.849057 5:-0.123288 6:-1 7:-1 8:0.358779 9:-1 10:-1 11:-1 12:-0.333333 13:-1 -1 1:0.208333 2:1 3:0.333333 4:-0.792453 5:-0.479452 6:-1 7:1 8:0.267176 9:1 10:-0.806452 12:-1 13:1 +1 1:0.458333 2:1 3:0.333333 4:-0.415094 5:-0.164384 6:-1 7:-1 8:-0.0839695 9:1 10:-0.419355 12:-1 13:1 -1 1:-0.666667 2:1 3:0.333333 4:-0.320755 5:-0.43379 6:-1 7:-1 8:0.770992 9:-1 10:0.129032 11:1 12:-1 13:-1 +1 1:0.25 2:1 3:-1 4:0.433962 5:-0.260274 6:-1 7:1 8:0.343511 9:-1 10:-0.935484 12:-1 13:1 -1 1:-0.0833333 2:1 3:0.333333 4:-0.415094 5:-0.456621 6:1 7:1 8:0.450382 9:-1 10:-0.225806 12:-1 13:-1 -1 1:-0.416667 2:-1 3:0.333333 4:-0.471698 5:-0.60274 6:-1 7:-1 8:0.435115 9:-1 10:-0.935484 12:-1 13:-1 +1 1:0.208333 2:1 3:1 4:-0.358491 5:-0.589041 6:-1 7:1 8:-0.0839695 9:1 10:-0.290323 12:1 13:1 -1 1:-1 2:1 3:-0.333333 4:-0.320755 5:-0.643836 6:-1 7:1 8:1 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.5 2:-1 3:-0.333333 4:-0.320755 5:-0.643836 6:-1 7:1 8:0.541985 9:-1 10:-0.548387 11:-1 12:-1 13:-1 -1 1:0.416667 2:-1 3:0.333333 4:-0.226415 5:-0.424658 6:-1 7:1 8:0.541985 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.0833333 2:1 3:0.333333 4:-1 5:-0.538813 6:-1 7:-1 8:0.267176 9:1 10:-1 11:-1 12:-0.333333 13:1 -1 1:0.0416667 2:1 3:0.333333 4:-0.509434 5:-0.39726 6:-1 7:1 8:0.160305 9:-1 10:-0.870968 12:-1 13:1 -1 1:-0.375 2:1 3:-0.333333 4:-0.509434 5:-0.570776 6:-1 7:-1 8:0.51145 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.0416667 2:1 3:1 4:-0.698113 5:-0.484018 6:-1 7:-1 8:-0.160305 9:1 10:-0.0967742 12:-0.333333 13:1 +1 1:0.5 2:1 3:1 4:-0.226415 5:-0.415525 6:-1 7:1 8:-0.145038 9:-1 10:-0.0967742 12:-0.333333 13:1 -1 1:0.166667 2:1 3:0.333333 4:0.0566038 5:-0.808219 6:-1 7:-1 8:0.572519 9:-1 10:-0.483871 11:-1 12:-1 13:-1 +1 1:0.416667 2:1 3:1 4:-0.320755 5:-0.0684932 6:1 7:1 8:-0.0687023 9:1 10:-0.419355 11:-1 12:1 13:1 -1 1:-0.75 2:-1 3:1 4:-0.169811 5:-0.739726 6:-1 7:-1 8:0.694656 9:-1 10:-0.548387 11:-1 12:-1 13:-1 -1 1:-0.5 2:1 3:-0.333333 4:-0.226415 5:-0.648402 6:-1 7:-1 8:-0.0687023 9:-1 10:-1 12:-1 13:0.5 +1 1:0.375 2:-1 3:0.333333 4:-0.320755 5:-0.374429 6:-1 7:-1 8:-0.603053 9:-1 10:-0.612903 12:-0.333333 13:1 +1 1:-0.416667 2:-1 3:1 4:-0.283019 5:-0.0182648 6:1 7:1 8:-0.00763359 9:1 10:-0.0322581 12:-1 13:1 -1 1:0.208333 2:-1 3:-1 4:0.0566038 5:-0.283105 6:1 7:1 8:0.389313 9:-1 10:-0.677419 11:-1 12:-1 13:-1 -1 1:-0.0416667 2:1 3:-1 4:-0.54717 5:-0.726027 6:-1 7:1 8:0.816794 9:-1 10:-1 12:-1 13:0.5 +1 1:0.333333 2:-1 3:1 4:-0.0377358 5:-0.173516 6:-1 7:1 8:0.145038 9:1 10:-0.677419 12:-1 13:1 +1 1:-0.583333 2:1 3:1 4:-0.54717 5:-0.575342 6:-1 7:-1 8:0.0534351 9:-1 10:-0.612903 12:-1 13:1 -1 1:-0.333333 2:1 3:1 4:-0.603774 5:-0.388128 6:-1 7:1 8:0.740458 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.0416667 2:1 3:1 4:-0.358491 5:-0.410959 6:-1 7:-1 8:0.374046 9:1 10:-1 11:-1 12:-0.333333 13:1 -1 1:0.375 2:1 3:0.333333 4:-0.320755 5:-0.520548 6:-1 7:-1 8:0.145038 9:-1 10:-0.419355 12:1 13:1 +1 1:0.375 2:-1 3:1 4:0.245283 5:-0.826484 6:-1 7:1 8:0.129771 9:-1 10:1 11:1 12:1 13:1 -1 2:-1 3:1 4:-0.169811 5:-0.506849 6:-1 7:1 8:0.358779 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.416667 2:1 3:1 4:-0.509434 5:-0.767123 6:-1 7:1 8:-0.251908 9:1 10:-0.193548 12:-1 13:1 -1 1:-0.25 2:1 3:0.333333 4:-0.169811 5:-0.401826 6:-1 7:1 8:0.29771 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.0416667 2:1 3:-0.333333 4:-0.509434 5:-0.0913242 6:-1 7:-1 8:0.541985 9:-1 10:-0.935484 11:-1 12:-1 13:-1 +1 1:0.625 2:1 3:0.333333 4:0.622642 5:-0.324201 6:1 7:1 8:0.206107 9:1 10:-0.483871 12:-1 13:1 -1 1:-0.583333 2:1 3:0.333333 4:-0.132075 5:-0.109589 6:-1 7:1 8:0.694656 9:-1 10:-1 11:-1 12:-1 13:-1 -1 2:-1 3:1 4:-0.320755 5:-0.369863 6:-1 7:1 8:0.0992366 9:-1 10:-0.870968 12:-1 13:-1 +1 1:0.375 2:-1 3:1 4:-0.132075 5:-0.351598 6:-1 7:1 8:0.358779 9:-1 10:0.16129 11:1 12:0.333333 13:-1 -1 1:-0.0833333 2:-1 3:0.333333 4:-0.132075 5:-0.16895 6:-1 7:1 8:0.0839695 9:-1 10:-0.516129 11:-1 12:-0.333333 13:-1 +1 1:0.291667 2:1 3:1 4:-0.320755 5:-0.420091 6:-1 7:-1 8:0.114504 9:1 10:-0.548387 11:-1 12:-0.333333 13:1 +1 1:0.5 2:1 3:1 4:-0.698113 5:-0.442922 6:-1 7:1 8:0.328244 9:-1 10:-0.806452 11:-1 12:0.333333 13:0.5 -1 1:0.5 2:-1 3:0.333333 4:0.150943 5:-0.347032 6:-1 7:-1 8:0.175573 9:-1 10:-0.741935 11:-1 12:-1 13:-1 +1 1:0.291667 2:1 3:0.333333 4:-0.132075 5:-0.730594 6:-1 7:1 8:0.282443 9:-1 10:-0.0322581 12:-1 13:-1 +1 1:0.291667 2:1 3:1 4:-0.0377358 5:-0.287671 6:-1 7:1 8:0.0839695 9:1 10:-0.0967742 12:0.333333 13:1 +1 1:0.0416667 2:1 3:1 4:-0.509434 5:-0.716895 6:-1 7:-1 8:-0.358779 9:-1 10:-0.548387 12:-0.333333 13:1 -1 1:-0.375 2:1 3:-0.333333 4:-0.320755 5:-0.575342 6:-1 7:1 8:0.78626 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:-0.375 2:1 3:1 4:-0.660377 5:-0.251142 6:-1 7:1 8:0.251908 9:-1 10:-1 11:-1 12:-0.333333 13:-1 -1 1:-0.0833333 2:1 3:0.333333 4:-0.698113 5:-0.776256 6:-1 7:-1 8:-0.206107 9:-1 10:-0.806452 11:-1 12:-1 13:-1 -1 1:0.25 2:1 3:0.333333 4:0.0566038 5:-0.607306 6:1 7:-1 8:0.312977 9:-1 10:-0.483871 11:-1 12:-1 13:-1 -1 1:0.75 2:-1 3:-0.333333 4:0.245283 5:-0.196347 6:-1 7:-1 8:0.389313 9:-1 10:-0.870968 11:-1 12:0.333333 13:-1 -1 1:0.333333 2:1 3:0.333333 4:0.0566038 5:-0.465753 6:1 7:-1 8:0.00763359 9:1 10:-0.677419 12:-1 13:-1 +1 1:0.0833333 2:1 3:1 4:-0.283019 5:0.0365297 6:-1 7:-1 8:-0.0687023 9:1 10:-0.612903 12:-0.333333 13:1 +1 1:0.458333 2:1 3:0.333333 4:-0.132075 5:-0.0456621 6:-1 7:-1 8:0.328244 9:-1 10:-1 11:-1 12:-1 13:-1 -1 1:-0.416667 2:1 3:1 4:0.0566038 5:-0.447489 6:-1 7:-1 8:0.526718 9:-1 10:-0.516129 11:-1 12:-1 13:-1 -1 1:0.208333 2:-1 3:0.333333 4:-0.509434 5:-0.0228311 6:-1 7:-1 8:0.541985 9:-1 10:-1 11:-1 12:-1 13:-1 +1 1:0.291667 2:1 3:1 4:-0.320755 5:-0.634703 6:-1 7:1 8:-0.0687023 9:1 10:-0.225806 12:0.333333 13:1 +1 1:0.208333 2:1 3:-0.333333 4:-0.509434 5:-0.278539 6:-1 7:1 8:0.358779 9:-1 10:-0.419355 12:-1 13:-1 -1 1:-0.166667 2:1 3:-0.333333 4:-0.320755 5:-0.360731 6:-1 7:-1 8:0.526718 9:-1 10:-0.806452 11:-1 12:-1 13:-1 +1 1:-0.208333 2:1 3:-0.333333 4:-0.698113 5:-0.52968 6:-1 7:-1 8:0.480916 9:-1 10:-0.677419 11:1 12:-1 13:1 -1 1:-0.0416667 2:1 3:0.333333 4:0.471698 5:-0.666667 6:1 7:-1 8:0.389313 9:-1 10:-0.83871 11:-1 12:-1 13:1 -1 1:-0.375 2:1 3:-0.333333 4:-0.509434 5:-0.374429 6:-1 7:-1 8:0.557252 9:-1 10:-1 11:-1 12:-1 13:1 -1 1:0.125 2:-1 3:-0.333333 4:-0.132075 5:-0.232877 6:-1 7:1 8:0.251908 9:-1 10:-0.580645 12:-1 13:-1 -1 1:0.166667 2:1 3:1 4:-0.132075 5:-0.69863 6:-1 7:-1 8:0.175573 9:-1 10:-0.870968 12:-1 13:0.5 +1 1:0.583333 2:1 3:1 4:0.245283 5:-0.269406 6:-1 7:1 8:-0.435115 9:1 10:-0.516129 12:1 13:-1 statistics-release-1.7.3/inst/datasets/kmeansdata.mat000066400000000000000000000430551475240274700227550ustar00rootroot00000000000000Octave-1-LXÿmatrixþÿÿÿ0ë'_!u8 Àß(Bm¶ À¿0.îÃàû¿rV-äs¶ ÀÕˆàFÓâý¿kãX”×Àò¹ÒÀEs¡½ëÀ˜“aàB ÀjŸE…uØ À白“ˆÀB–¢n ðÀ›·†Œj ú¿‚U–+ÔÉ ÀO…ȰîÀp9*»Rû¿ØüÞ£Ò ÀÃz’MÀ²;@|‡›Àwp¶ 8Àæ…[ÑoŽ ÀhÛ r)ýý¿e!Ë/ûÓ ÀÕ†c ÎdÀÔ2ö˜óVâ¿ÎgÞuôþ¿r™nh û¿0bZ¬Ò² À`ÖàõfãÀ~¬Ÿ ˆoÀ—$;ˆÀz¶Ô«²ÛÀIKA–àÀd¶wB÷ô¿îÄg\ÿ¿V·ušŽú ÀßFÊyœÿ¿Òáè:› Àª¦ÊGcÀY(ÁÙ¼= Àã¢õ¼À¢¢Å¹³Vï¿1ûÞBÏÀTΡG‰!À:‡ý¾˜õ¿gº-ÁÀ2¸Žý…Wÿ¿N}Hì6òÀ(’éFI(À¹÷9µG ÀÕ5jö ñ¿¥éðRòÀ" ÊbÀÂj䣱Àê—«KùžÀÂá<‘ßÀ ­¹vg¿ÀÜ·ˆöãÀñ9ÈÀbVÕÀ€OZSc˜Àgê ©öŒÀhª¦û˜ÎÀ|/OËgÀ‹ácð•áÀ?O¸ }ÀøhclÖVþ¿Õ|4+bÀó–†ÉÑÀ÷»Î11 À˜u2ê*ÀÚ÷,ÉÛÀ`ÚfÅvÀRøè7ÝÀ†Töež5ý¿ÜÒ~©nÀ^oQi¬À$Q]ÈÍÀ⇣µyÈÀ  Ùæ*@¦ãÀvA @K¦vë3@ì&ÖžL@2ÈŸOÃ@¶bÊ2Ñ@ êš‚¯@h «Vª³î?%"VˆN @Zpï¸ùgý?ÅOÄ<@ FÌö ƒ@’7ÂÐì@úÚ‘ˆ@Iw6Ø@Mïµ @hïÉv¾– @x;Ø®@¯;Uÿ"ú@´ðÆ.@|žA•¿0@&½ÖUˆàõ?D¡*2zN@5\þB "ü?ªãJ|šý?BŒ™­Ûz @ ÁJa·Qÿ?ò®C~@‘pã¹§@zdÂè~@Èý\rÅÏÕ?‡Á÷ÔE@Ö—z<_ù?¶‹r7Ø@ˆë%¯„@X¶ò·¥@êº?qm@LÊ|Ç·@'uc%™@¦Jë—F@.Éñ|Ì@ÆË¬ìÁ(@*éàíºF@%ÓKoqh@Ù7Ãj»@[Ýi#c@L^µK@„^æk_¤@>~ƒÑ ˜@pG" ê@z6àÙ©0@\È3«†Xÿ?Jõ#2û?X[¹ìr»÷?ïEhÍ÷?gª¿~Î@Çíß›ó@ؼà3ƒW @†‘†p€Ò@ºÔí·~s@:ýc¿{õ?üÀöšàÿ?¯ÉO9“oñ?ö¡…c—@L(÷—- @H9­{® @nâ¹?s @{‚,ëÛ‡ @NC’T£/ @²˜³æuû?—ÿ­>k½ @G*‹Æ"@P³†ø@ áþžû @Ÿò#Á_þ?Kµ ¯\5@Qä,ˆE.@P’¹¦eÆ@Þæ´t b@—4á¦dþ?¬Ð|‚-@}Aôí @Šþ¨Z/´ @ç¶x…@fzÎÖ·&@ªœEPÂ@˜ÛؼHõ @°8åÆ @Mf‚þý@îP¢}-ûù?ç6 µ^@ì‚þG‹1 @–j³È¸› @B2ÍD!ÿ? ²ß@Ê<>ß@ÏdF%V@2¿ñ.t@$Á±’soæ?ôÍö,ãÌ@îg÷0 @úO€½@à¾6Ý @w=´šŠƒ@å+óSÁ@¼pv½7#@::Õ÷!ù?q±V²4@Ù,^@p¦ê”Ǩî?h¥¹¶åú@(g½ y@'¯1Ã5 @<`RFœÆý?k"ZL  @R,Œp)@Þ—…€ @šø:Œ @ù_‚]Œ@\qeb@[¥%ª³@(äYsͧ@‘÷oæá¥ú?Þq>¶@à@HMåŠø?Øž( ' @1˜ëœðô@í4“;= @ÞÜ„ª(ë?ÔÓ]Î á?ô„Þ›Ë@ã’ò©ú@$½qš?õ?7G—•§² @bøU¯ @I@Â;ƒÖ@m¸"Çãü?Œ£ÚkL@Övt}±ˆ @4ÁIõ–¢ @dBÅ€ÅÄ @XZq¶ _@a91 @1>XÕdü?Âë?@Û ÚO@w‘»¢ @@ý\! @ †Û´j @ü‡z¯¹@Ašùóð@ª%®%-ý?{jóF@L|Ò?Ë?@àÉï~J @9=qÚuý?¥Ì_î±@¨_ ˜þ@Ð2^ÉÝò@i%ñŒô@ß(Ë¥@èɹ‰ÆÑ?Þžn™• @tÓí`‰7@Uä`q‡@\Fê* @æ‹\t@+K«DçR@ÑBË.– @#\?³œ@T¶Á#$cÿ?Ú^åõ®@üÒò©Ó @ªÕ.«ÉÝô?pvú *¿@µIÔIK @º¤”;Ô@“iäîš”û?È¥…@ {?„„@®ŒÞBc¾ @i˜øÃ€ @Ü;Ô¬Š @$©™i¢ @’cY>¿C@³6ØbN@ ¼Àº(o @¤ÕØç3@L‚bM?@©Ftãxþ?"Í™Zt" @Nú˜¯4[ù?w8,Gñ¿@› ª¼'@H’x¹ƒ@è¶H`m˜þ?5¹jŽÄ@ákœ2ö)@ ÖØ÷ÞA@z/·|ï?Ö±Ì,ó³ù¿j9îq@À68“€C ÀŽþ_¼¸ À,¼#²ÙZÀ0¸()šØ À«Ì&âŽDÀO Zyu‘Ày_aAh¢ÀBgXw|$ÀL/Õ|Þ ÀæŸÏ*ºh Àó©Ã…$Àó)ÁD/ À1w÷O˜ü¿‡€Nœ ÀºwæîÊ™ÀEŽ8DI ÀמÔV}À sÆãêÀ /Šsg鿹€ðòÀȶŒÈç”À¬„u9"ÀGÉX Ÿ ÀðT-71ÀbÎ äÀh¶º û¿R~ XWÀ±„Ë+ ÀÏP/)µ;ô¿²—W ½À Ô«3™Ùý¿D[ëuÀ¼É‚ÞÀ`½·†ýùü¿ Ñý³ð$À€ƒãðÍèÀ@'§Q Àù7äÒ%…ü¿((§ÑãnÀ¹¡õ ÀomZxðºÀn QMÀ4¯.5 À9€M˜#À3`d Àùó´ ¬ À€”t–<ÀkâHùðû¿Î5S¬Àåjè¬ À¾‹Ø¾m¸ú¿è9Æh~2 ÀiºÅêí” À1þŸ=´ÀpkÍÕa‡þ¿ÿ0¿el ÀH…0$Öö¿j×TãZ` À×j`¢Ð8À;¥³3tQ À.üd›¼ À^4^ªMÀðfhí( ï¿;gè|ÀбÕç ÀF¶a¦À—ÔëÏN Àbë°ÀV·ÚSbÀÔ±Þ8þ ÀÛ®î]´› À6eÀªÜÔ À€†’ðý\«¿µ÷#œûÛÀ4ÉÈTcÀëä vU À`ð+ê´ø¿ïŠnÀÆü7²ø¿÷VÙô÷ú¿xŸbƒWDÀ— ¯ð—ŸÀΜöyÉO ÀV7ÍŒ§À´y¹ÂÀºævx—À¸°¬ ÀáˆäSî| ÀB5/ÕK.ÀSLÕ®ã¿Æ6ÒOÀÃҼǘ{Àê• Ç.Àò+TN¢dÀTÄyvžcç¿ÛŒG¨ÇÀcQg?À­I ¼ø¿Þ¯þ—¥À¶–_W À8 —ÆÀùÈ<ÀEzùÀQÀÎúÐ@¬\ À©,ª¿À(ÔmåÖ俼_mÔ”!À‹~]`ìÉÀÓ ›ˆš!ÀÛzYgÀ ‹136bÀ0¡KZ¥Ö À€&§å—eÀؚξvI À‹Æ£ßt'ÀRúQaãþ Àz%}¥™ÜÀ_ùi5À DmKxÀbZSù|_À62L’ûÀ0Ïp½5ŽÀ±C9nÀ6ãßL%BÀ“¶[¶! ÀUT1ó“Àb‘¯GŒ1ÀZï7pèÀ{êì À迾 kâÀ×ð޳À„‰ŒgUÀ%©ÃžñlÀ’ Ö¬h$ À†»™øDR À ?úÀ9u5»ÀôçÞ½–{ À<™+à À÷¶£#,À)ô®–ÀƒZsîŸÀ;ûU”ZÀ·Ìl·ý•Àsªè À;í9ÁÀd&ðuã"Àþ|×aÚÿÀ…à§iT@DmÝJ¹@ú$ã6%€@Bé"% @Ú–¬ƒ%ÿ?…©¶‡j@øXÊ4 @`®ù[@bkò"µ[@žo©D¤Ý@®>þŸ@ÃR…ƒ-ü?¹tIçŒ@×#‹u @¿ÂÖñ¼Ûû?±O0/ˆö?ܧóÖKW@+v.ô-@š gŸº @~A”¡P†@替h @¨(fT']@ØÞ¢#@_r¯ o…@ js­€ô?À|ÆÂÐã?Þ„2ªj@$ŠÀ™À@~›ÑåÍN @ ?¡€ëø?{ã.ÐG@hþ, •@8j¹%@¢ÌcŠq@Ó°wÓ/0@ Õ ÔOü?ýæJùý?=¤’î6¾@‚/²6’Š@ݤëÝ5@®r-mµÿ?ö}ã[ÔR@ãÓo‰×@°ï|µ° @ %K£ëÖ @`Weˆ¢2@?r¥ûç@˜Å—;gÖ @TšñÇMy@ÎQÞ+7@mü¼ ù³ @޵:ÙŽ@^»óêy @Œì+[ù@½æÇñê@DùÞà Ì@¼ '9Mc @=KŇGù?(Ã@2ƒ @¢Ð²¢œ@ö¦…kªl @e›MB[V@ß$CѬñ?é?‡=¶@is^Z؈@h—R[Ķ@f:@Wô“À+@Ÿ1ÝT @VOÄPã~@¿DÈlÄ@¾E¨aì‹@ᵕ»¡@p‚sçþ3 @CúÿÕ¸@8õòäÿ?[ËÇ @Ȫ)Ïõù?&»:>%>ÿ?7•áuµ?@éÕª[@hWù–žº@4¡8%Òá @doH%Ãÿ?·vGq‘ž@ð¸¶)÷Øû?猳\ëé@øÖÁ¿V€@¢ÀI¥’B @m¬}Ê @`*õ¸qŽú? ’„Æ Y @‰-x8”û?˜JÜðTw@J¬h\ @ï¾Ö÷@ª÷Ó¢âg@¯øå‡@ ?–ò?³ì‡ V'@@m`3s±ö?Èd<å©/ @@Zp÷+Ž @b êŸ@2TE 4@^昘)@X3ÜÖËæ@=ü¥¦@Â;>‹@óaÒ7l‚@äx+B› @(3¸‘(@mðØ|Yõ@ÎààòELý?JжiÇ@—´'©ßO@•>5ƒ¨@HgÁªVå?fÆ:7ûãþ?×A6·@б‡s@¡­ÿQ@ŠòQù…e @†#»å @,hEM?@V€,† @Ø"¯ÉcŽ@ÌÆ¼eÅ4@†ì±›Ô @Õð=¢÷? ë·üJ À3°ññùÀ3šO À P2Ù µÀybò¨ß1 À¤ÏKŒÀc{6À,m Ã_äá¿<ü»6ƒø¿$sa‚ìÀ-WÞ) ÀÚ¿ÙMSò ÀÑTXຠÀÀè!g À'Ò„Q¢¸À85É©ÏÈâ¿N2ðÓ° Àºn{1› Àr6êJ\uÀï+÷‹ À"ÿJÒÓzÀßß“1vf@ý”ú–˜· @Wº5žƒ÷?òøüÛXø?ê1â…@¶‚©¾7@~â.¹½† @/€÷?2~„ë@‚A[Õ—E@UÀ(BA.þ?”Të…,7@è1ìSu @ 7›F¹@~A½Ö@…#» ÊW @Ü<¾Eò @ØQ7‚@(ñÜ$‡þ?®ûˆ]Ÿ@ p¥Æý?= <~ø?æãÊ›^rý?WƒÛ ø@ÈBirl@¦Ó˜.Æ@Ô^aUH÷?3¤¼þ@ÆäÓ42ðý?5ãà—@–ùTKKÿð?"QlÃ^«@$@EQZ@BÀùæ°ý?YBh8Q @ÙécîÊù?A#*qÄ×ü?ºCO_@“ðDzpâ@„°·˜O@€=è†)@^JË:xV @+O÷o @ÙúÚ­@„ÎD÷y.@K×pñ,ý?ï#ðÝÛ¯ @RÿNTo¢ù?^§!¥=™@&D•Ë@7йPö?{‡è Ô«ÿ?hš¤×D9é?.c&™³tó?é¶gÿÜþ?u½*†@ ÐÀÌÒ>@~À€7Ä @xôPöD.@¦¾¹3’@Fò=>;´ò?›Øàð¦x@ŽÓMáö?É‘Òiý?ê³È(î@Hô1Æy@`àÈ‘@¬Ká)oC@ Òãÿö@Ú*1$(©@ÃG”* @ð¯]äGOý?îz¦.ß @´Èb•œ\ @#ÿv†7@ÞÜz|¨@ öfÏ@,¬Á¿½@Ù¹BÅH@¼® ‡ð¥è?¶×Mí?½¾&LÓ©@¸Íƒ'…ù@'dÌà@Ç Îóþ?;€Ôw@zý? puJ @º_8èß@=ñn@eßRV0Ç@Ú—ÆZV@éQ:O&@î#šg‘@æw± è1@¯1±j@Ù=G €[ @ó€6¶fäý?× äŽ{@N6¦÷?¾ìôcÞÔ @ÑA›„2@!›ys@pSh%† @¦Ò9vÈ5@ÍþBj„ý?oÔ§Qý @t@þ‘ü@åÜñ@ÀÖø•C@ ,&ù?"j–£ˆþ?Œíž^+@ÿ³°‘%õ@ºØO6)ë?Î ç`@f ¢=€@y¯töÒQ @îšG¯'Ü @Zõýî¸ @,ãŸuäø@ÌT¸ý?^öĽQ¯ @!\ÑBXI@`Bféy@¸võöuú?`–bD°å@Ðáˆè¤ @bÌ|/;:@ÚX#„Òþ?Sñ©rá›@ÀlË˶£@©¨!ì@iµË‰A©÷?¦O]*%ò @ôVƒ@ñekR][@¸™û‘@ñ»WˆB @Z÷pR\?@¼aé±Q8@ª>¿W:ý?Èyjú @×RŽ>ñ÷?ÀºÁ²­´Ø?¾å÷DI @¤-%À×µ@J ˜² ‚@á?ùwV@S³/hå @gS徿ô?ù9Бö@€Û‚#šcó?ßš{b @¬ŽÇKv@b¬Îƒ…@|…-Ü o@¦ún´]_@9Út§@z@²gÛ|@зš¸2Á@æJ/mºý?€ &Ûk§Ú?ªT-ýãÖ @i},f(È @nêyí{ @=|i<@._IŰ•ÿ?=Ì8Š€@FAŠ´lú?Q­ê'…¹ÿ?T¿ƒ#w @ÊEÝ…Ò @Lw]œzA@¬ÔFš­@á[Ú–Dð@7€Tæ5@¦3µX@¸éosùž÷?ÇG¹bÕv@àM?ô@ahıp@Úš¿9µ @ä!â½\j@+gm_@:œe= @Žg³Ã£@3¶6«ü@%^&í@/vå-Qg @ âµ›Ìö?tˆÁo @2Ö:å!ü?f1á´¶ @Ðàû¾¼Ñ @¼RQô÷?tÝöÔù?Ú(œø @»éz5‡@ à~÷78 @륀ƒ@JÁ“;bÂ@g&Ó®×ù?°qÀÙÙ@ŸCY+3:@’ÕÖ°Žù@k=ñóÖ@Їh/[ÿ?, w‹D@J0ÔÊ6 @^ôm—Ç´õ?a ]ó@m‹ Õ@þ³šGç’@€æîU ¼ @ˆy°+¾Ø@oLlgå/@öQòÛW @fÞžBó?ïO4&ãù?K @3·¦“Œ @1dÏ)=6@Œ><³„·õ?Ôä~Ž’2@HñîãdÙ?j×[å¿ @RtËü|y@s/Éç[@Êcr9þm @àûÆJ Ìò?LÆ•f‰@õy$;Rj@ÁÖ~¢G @&Çp÷«‚ú?RøK,W@n؃qǬ@k%>÷ý@ÔÊZCJ¯@Ü2™öÛ@(Ïð¬œj@öÞžI@vÆáö @ä"îé5°@¤;>°L@•õ):È@Ñv$@Í@ȬŠV @ÿ¶õ¬ç·@ò}§çø?Ï©€õ)@Ë»¯ u @;þäi(g@¦‹N\Qò @Æj™õ{@È„¤ÅïYÿ?¬¡Í<5ù?ï¥È Çh@Š~YPvú?fX*p¯@ôdf @ñDo|®ß?設ìòû?ƒä1ˆ…y@—ˆ+=Ê@qLII=@¤_ÌQŽ@wêTK)@yÃ„Ì @Ìf_àR™@ëŒ7«é?|$w~ @ÁÀN}F @z¿øxêú?&—:3Bú@oj“=ë @‘WS!@` [3”'@"|ôž†ü?ãÒX[º‰@¡HòW@æ¬n. Û @fe6‹Å7@t±Þçö?y:6¨Á@TÏNßú?è!ŸI+@xá|¥üg @:“ ûLæ@•E­a|³@8 ¯Ó@nßì°*ñý?n+dÎo@÷P8>µ@â;’b@·ÿµ @Îx“¬¥û?iŠ×-‚@R½GÌw@åXí—ùy@ælÜÙsÜ@ê5‚uÉPÿ?S{ï3@Døa£ @O÷Æ’EP @Ì^<Í @F¨”M™ @}–"{H@VoÝ·EE@èѨt)¹ù?ÁZ§s†® @<~cK@Ìtϳòý?¹¶œ1EÄ@iÝv/RØþ?„*Tùó?42ËB-Cì?±[üc j@› å@ˆ¶¥DŽß?‹Êô]¤ÿ?_­¦vP@“ýe¡û?‹ï»GÓõ?½gmÞ¶#ô?ÿÙï’>Î@ùìÓ­~@½Ölao@¥™_ @¬BÈÕA @¦N³¨\ÿ?™Z””@¼Ø“Ý^÷?ævèˆ,@sÖA©¬ù @‡±6Ù‚Û@‚®p7@¹Œ±IÒ@¯l38@"ÔÂÕ¨@Kc½fK @6FÈzãN@»ÄÌ#)@±ÓO£ô?XËGm7hð?ήÎü‚7÷?c{VG/ø@ÒdÑEX¸@ë\‡z1 @íaR­]É@’„*j @µ³ªl²a À–”ÛÓø¿â'V{ Àv‡ó©ŠÕÀÃo žgÀ•Ó…¬ÌÐõ¿¤~ìÄýõ Àïýëìþ¿Ø³3 À›šÛð¾À~}¥€_À„•c… ÀX¾.ÙƒÀvÊáíä Àðþ f[öñ¿ª´C' K ÀMú$;o7û¿ÿY„bœL ÀœܳÀÜ_ Vƒ¨À™Æ!ÍóeÀ\(¬~c1ö¿5i¬š~Ð Àßžòµâ7þ¿LìúKdö¿ Ù¦a«˜À |M&‹Àò«¬ÚfÈÀ{Ó‹/lÀŠkX0•}ÀÑ5á èþ¿N‘AÈú ÀhBž¨ÝËÀÊeÄ2]µÀíAsDæõ ÀýöEM™ÀjÌæŸÀ À|ªÎ`ÀïÜ.î´êÀ, ײÀyÇÿRÀÖ“š4²{ Àš¨|ª rú¿ ïŽkñ ÀnÛ¼225ÀcwÊÓa ú¿>óa0Àbý&ëõ¿÷.©j°ú¿^,÷ÎGKÀF¼a–õÀbìb¯Ìñì¿)d”âaj À@ë˜s8À GèóÀU#tÒÀ”Ú¶ÖàÀFŒI¶ž²ñ¿¤¢œ>Z ÀðâÝ®*À°¹À”÷uˆÿ¿íÄÀôˆu Àó…®Xp;À‚,v°‡»À€ß¯Ûô@ÀN޵»÷¿üÈŸ~ÇÀÙ‰ÿ¿ϬçÀJâ 8 À#»u¨Â÷Àª 9MªÀjÓþë ÀEt‚¶t¯Àì=±Y/ À€åríÀN8˜ÏjÀ³Ô±øóÀM¢ÄÍ› ÀÛ•; sÀD1s_®À®´ên®ÿ¿Vé° ØÖÀ IN…/¨ ÀäïBG{ÙÀ0V7Ï”ÀJ½˜USSÀvR¦MBÀèVÈÀÃ[Vg±ºþ¿+‡ ´<£À”§#Ç9¡ã¿ÜcéÀÃp±}ê|À†6Žâ ÀV;AívX À˜ÂSGÁ:ò¿«O€ô‚ÀÍçÅøÀ+"´$À}O"Îa7À SÃýâ¿ÁPíÐb À^%Þo|ÙÀl-ôNÀæ}Qù®À‘MÂ2À<û¶¼x À´˜…Kó Àç )ßmB À jAÞ À¾…|,[?ÀB蓳 ÀæäÕ¥ÀÙ‰­¡VÀÙ~ „è ÀûeœÜ[À|òhBQ÷ À£/#¬dÀøå}Þ·øþ¿K$ÔÄû¿öõoÐÄÀPGF5À¨’Ï1ÊN À,k²¨­Q ÀzÏúµùÀÀïŽ =8÷¿®Ò*?Ò›ÀNyO…„Àä³±å¿3ñ…˜À-˜Ñ~ªÀuÙH ÀfŒ}íÀJF«…¼×À¤DÞØNU÷¿¹‚XÀÒDx…8ìÀ(˜GE¡ü¿J÷ n†) À&ÒVƒŽÀÎPŠÎ„û¿QaC–¹ºÀ…ý™U-ó¿{Ó5ú˜ ÀŽMü ׉ À§ÍIE^Ãø¿‚À¾Ré˜ À\%ô†¬ÀÃñ^”Ý Ày*û&éÀyì_e ¡Àjr¹ÛüÊÀ¶?»¥û±÷¿Ì–,ÄÀ•šœf¤ãÀÎûZ?Vÿ¿cXhšóÀöQ9TXçÀÚ0<ÉuÀµ/ŸÂzoÀdýg³ î¿€ó} ÀM™D=Õ‹Àü[WŽ]ÓÀ Iq`° À;§€õwÀ³Q^·0À¶’ •ÀwŒiÀS¼fÂÌñ ÀÂÑ –bQÿ¿Wß³ú¸ÀrUpì¹xÀ?Ù¹µ¹À@ŠëŽ#ÀyÕeùk Àв5wÀÖßÛþÀ+¬ê†ÜÀÛ¹#þ¿:¥ ¢úk À‘Ùã2dà À‡™ãÎ×øÀ8×î ·ÔÀ]Š÷Õ·2 @6 Pì @€òøê¿@hmÁ€TÕç?Ÿâ{àêé@¸~EŽ@ˆ36•5@ˆJdèýø?úªÐ?´s @f»Uþ?ÙuõKé@ÿaÑî%@0ÃÈŽZý?Hÿ`{¨” @ûÕüªUþ?ûÑ—îñ@ 9® ýF @—?Œ–ÒE@ÀlB…ø?/2è@}7ÝM‰@6K-04"@x½_ER@ÆÇýî‚@òÈŠ¬ˆø?•@sœ@®g G@ø9v/<þ?~É ;G@@†žg·_;@šå5N,ø?gÖûþ @!r¬2©Ö@ô†T¿«Ðë?çêSÓý@ƨüÅ/ü?Τröú'@D½½IŸ¥@ÖL×ó³9@%ùýbº@D·Õ‚à2@Tp´v×@òÑ=Õ@VÅš°_n@ÓæNZz3@oA ]÷ó?ÿá’] @žZ =î‰@±®¹õŸ@ÔGÍ€Î÷?ÅŠS»Óö?²5vQs"@й¹Á/I@dª^§©ê@.Òým¦û?~w @œ§ý”^@uÜ>€@Ð)dÀ[ï?=ìŽî‰@ve*eÔ@•¹`ÿqÅ @‹›µ˜Æmù?vå3'w%@Ó¨ú @L…¿6Ú@b75#O @(sÚ @BÐݲ~Ì@Õ%LUZŒ@ìqL5sóÿ?3Ÿ­¨˜@ÑSknf@1N$m,ù@6õ`èâÿ?Ö¸°]qü?p”Ûªg@&ä§Rº@æ6nÒö@mŽ ?Û @Óÿ¸U@Ï53½ø@éHRêD@¾•žòº@ ,ÞrËšù?4€`û§{@4«š @NH)¥R«@˜…‹lo@ª‰ '7é@ÚÍLôÅ›@¹€ÄÑY4 @%dNå@ÊÐê¬ý²û?üæÖžŽ@âãÜdùþ?S4eL#ã@ƒd·tLÊ@ý)»ÝÒø?|ÍC@Ö¨í"¢ @ÅwÙ75@=t’9(¡ @pä×Èkî @%*¿ý?6¨ÃíQú?ðž5/›@r•ñv92@1—×®¢ @-}c”ò×õ?ô[ëaJ.@s]ƒÆ @pµèÃ÷@‰¦ŸÆ’ö?PÖ¡,“¥@ éÛ¶o¦ @C"Ð|åÀ@ר¤v}@ÏÓžQ% @twk˜ @ÐÁÝv!R@öÿ«¥Õþ?9Žá=õö?ß*ªWk@1´QÆPª@ØÓßÒ.Ç@ªµ'|Þ@gi[B@dÚSÁ@h,èPªô?wV§ª´£@ÿ¬ü²Äú?A#–ÈÄ'@Ð~6eÄ @n¯2σ @`mËuÓY @èãr§V6ù?lZ£àc @™(ä¹× @ú¼Šgü[@HéüK@¨ø½.‡Š @•ô@Í»@@Ð)Ÿ“ê?¾êš¹ì×@ øÕˆ9& @Z¦B<¯‘@7 u @8M“·òý@Å01i—z@Òqbv•F @Ö™Únãü?rS4Šù6 @¯!?ðŸ@¿wRþã@lœ”ùñ]ö?Â)ÎA@òÖgñr @è~¬Ìý@ÌÉ–ùÎŽ@¦l¸T!@¢À?P=ð?6Òô*ô@Ä>êÔÚ¯ @Œ„fKx| @`ªQW @F+¥Ç›V @X•9úc@y<}Q@ÏË_È @5—_”ž@œ·žÑ–)@ÎúŠôNÞü?ê÷ã¹ýû?u ÂZÁô?py†¸æ@âôÔûר@&Íœ|ø?XuB0cø@aÅÍ%bæ@êVLÕXº@ôn×ç @÷W›‡o @àWÔú«ƒ @FÐO"h@6b8T†@1ôÃ/Rø?gQ@=S @ö’yŒý?Ê7;iåþ÷?ö@ë¤)Í@^ˆLŪ @o_U‚,@{†•‹ @;ª¨ß×z@S[2(@Ÿ>V²T@o ¯¨º%@ŒáœJÖµ @Ê¿—K\@n¼‚ç Àt(McÊÀ\p›UÛßÀ=vT€IÀPZn“- À.ŸŽÔ>ªÀ~`3ÛzÞÀ¸ØxûZ ÀÅ®`#þ¿ê“žx[è ÀrîF«…ÆÀ BöA ÀV!¢ú$‡ ÀŸâyíšÀó­—<ÀC®!…À£Òí@ÀÜßgž™þÀ¶õ¢º¸Àx+7ÏLŽ ÀC?G2wéð¿" ‡"öÀ€˜ˆ‹$À}öoï›cü¿iÛJ îÀañN¨Rtÿ¿ý! ì§¤ñ¿»¡ªÈAÄö¿.kl=GçÀ6Óó`·ÀP÷Âà­²ô¿4s<|âÀ ø›>1 À6ÏU`†ÀûN>ãzÀµ29·™ÀX–ÈÑÀX‰ÂþùºÀ} v÷%À°ÑÌ1Z ÀyO¯uÌÀI0ìÀ‘zPêÙýÀ»oéâxÀnQÇï3ù¿×/— cíÀѰÞšGÀƒµ¹”C˜À”ƒ*'ºÀàÎ<@¿\ö¿:1ÓÞTÀ¹ï¸Á¥]À§v»,sÚÀé=\ïò ÀŸ…Éy. À*€%âÂ"Àkf$˜7À- -Ö ›ÀX •g þ¿ #ü‡ºÀÿ®,U§—ÀjÞèÒ—m ÀÇä8K´ŽÀ¬W ÔÌ Àf€’]¯iø¿êÀTð¿$ECÀ)1Y]#¨ÀõßBšÀdv“ÖúúÀ5Z¶KD@û¿Ž¿Ÿv¿À¨kþ`Øý¿³†î(+À€Â]'bɱ¿Þ4P)ÀёɊ"ÀÖIôÀâÀר E\ý¿fæ_²rþ¿,"ŒCkÙ迃·2æîoü¿'CÄå´À·CžcÇÀ¾ÕÉÊÖ›qk@0&^Ä @ÚS(3þ?àû,· @ ·± ÜÝ @NxÈ,`ü?风µÁ/@È|(› @bY«§þn@uzSq @c ë)8ö @~ û…e@Ý“wÓ¾Ø@ˆVæMÑ@IM½yg@TC V6‚@Å >ÑV.@s)J8–õ@_·Ž¼ @±Ðýj @æÀÂ}j* @DPÏ‚ÕÎ@Â3¹iì@b ïœù5@« â á§@´ÿàõÝÈ@–´(ƒù?@ÐØÏ’?@Lf·aY@@­cIf¤¸?¦Fü±Ëõý?iŒ¹fg @@’å @›Ï¶ ‹(ÿ?±‘]™ @4n•̤@ôí×[Ñ£@8]v*v%@²ò™ïÁ @ØgeÆ¿= @í>ÉõÌ;@N€Y ¬@>g“õi @ÜXvÁq @/GÄs“@kaÞ€ ð?‘]‡×Üv@¹97 þ@ÓÊ‹Ÿlúó?ô;I|²€@# ”Á(ÿ?*ÂÀ@øï**2y @µü³IoÀ@aÐg€°×@†¨5Zò?Q-õúÊÕ @^ë(Å´§@âWF)º @]`©%H+@Ûtò@J9þ?f2Ñc õ@¾±/Ép @@á´?qCû?`î Dkþ?šn ]ë?öÂËõM@pMŒ*î?Ú¸¨{@ @â³”É+û@ôÇš’@n‰… Ä@–Çeað@!€HÕõ?pxT @øCýö¾@Ü  ð?ýâD÷ˆa@óxa›+Ÿ @î0¿ FZ @ÍPqK@̱€‰ @-£·1@D7Q‘Îr@º0_i@¹²À´™ì@t›„Œ•@¤ÍðÛ!k @ƒPÿäuù?| §&¾·@Tßíç@l’«ýð?7¬°*õ @×o5dØ|@¡×¨qÕ” @¿”ŠáHð@דlÈ@6‡ú&×ÀfúÚ«µÀ~;Ä]¡†À²`ñ”NÿÀ·yÿcö À˜³WõÀØ}§ÙMÀý,ÚéÇÀ™)†há] ÀÔõ §µÀ]“¿SÀ•B »G À%6ñÎÒó¿~ªÎ± Àɦ  ÀyZ§þ˜À¤É%Ü8ãÀñÐË©À0˜Iµ ÀœŽÛhQ ÀF‚ã–qø À3}X¿ÇÀEÜ1 ÀšBNªÙVÀøO{¡Ëê¿<úå«Ié¿>jÿ–®4ÀQG’q´À­‘Ñ>²-ÿ¿¤( Í$À¡)i'´ç¿‘ü©»> À°W<»§Àí¿,v [Ö(ÀvTð\¥ À’X‹”F>À˜âsÜXÿ¿>¼šmÀšQ?@ãP À`Á–ôM(ÀŽ µ€\ÿ¿F¾F'’±ñ¿D âdÖ„À± ŽGû€ü¿–Öù9\ý¿·œ½ 9(Àl †\5ú À‹a|´ÏÀ:%â‹÷¿_•d€xúÀ=¾ÏYU Àãá!ºSú¿ùåãÀÜKÙŒ‰QÀþâþl€ÀòÁÛº»Ãò¿dADYWn À4¹5[ À4ÏJ„XÀ l*ÃWÀ³u¹½ÖÀ•zTJ[æÀ°ƒÂÿ±ÀOåw s ÀºjýlSþÀ¼*·t2 ÀÄ ø³µ;À\ó#+=¸ÀäA(VÊ* À¶ï½Z_ÀF™{§£ÿÀ!¤4 43þ¿»?IH]ÀõvœùÖÀ¡@êv…oö¿Ãƒ•¹ÒÀ-ƒšèï À8{8Ñ7 À¬E%ýTHÀ…a]añÀ– × R @ …ÅYM @[ˆ‘†Aü?À×(†8KÝ?³¢«ÚF¾@è]$ˆ@ÍÅÌt»@к¿ Û¿Œ-p¸!@ K šô{ü?p¹á.úÿ?p¶ø@€@G»Nw%@}ÇÜãѵ @­Ïq4<Þ @3óO_ 0@ôú9×@Š”Š6@žÁãë!Ó@I&ƒ°Ã+@„ UÔ;@&«\_½d@ñ=ÕU¤À@Á÷z¦O ü?#Mé0qäÿ?»Ö)%õ@\MÏ2@é.O6<Û @"ºqû·ÿ?jö„Úöy@˽š—1&ô?^šµ…ª’@1 8ý?‹®ðÔ¦Ë?†x-$é@‹”í @åï£=@y¨-3ö @d. þa€@vj’ñ,{@‘GÅ“ì @ã®àƒKdñ?ìçùtT@'Ö é @P(Â/ÆÜü?ë!¾Ö±ù?‚S®Ýj@vh9«n@¶úêaß_@€¯Ï¯Þ‹@ÔÏÒ?§‹@ë•‹5¡%û??ã¢T‘rð?ð¬GáOçþ?™AþPÝ@¯ˌ |@H'|·\i@Fåø°~Í@šÆ4ÊL@Êî¬m„@cߎÇv@QU¿¡i@/?«ü˜Î@!Ñ!­»¾ø?€Eë\®:@—v?R@9½Ü¾„)@“ºŽ€n @µ²™]°@ . Eu•@ÖX¨»w@Ûb²3›ˆ@;³k@ÜEßIé@Íöù=Ù;@ëúÔ;N÷?ö‹ˆÎýž@e':õYœ @ÜeÙclñ@‚u"]÷?fÄ+˜FÕü?s³ÿa;·@¿—±£Ûæ @²²5áBâ@ŠvbL@ãX+‘‹è@ ¨#Ý•@š)Û-¥×@ÏÚÃà„º@ºžöÕ³ @/®0õ9 @»Ö5 ëä@Dë:!~@bp@à(þ?‹š« @7بšf<†å‚ @-œ f3@Xa…ô[@TbŸÝ—@p}¨ñ1@èÊbB#ƒ@RRú®Ðo@L’$ É’ @œ;`;Cw@Î$Gº®@k{u·iÑ@€ z0¬´@*cÚÆ=ßü?pŸ¥É @²‘.5|N@¨ÊíDZ¿ @T®aF+‡ @öÃeN @àDU&7›¸¿GnÛvÿ@öøyú³î@ÜÒÄÉ@g·aÑAH@ÀEÏP" @-1›/ï@Rÿ¶ä@LY¸öé @®„Lt&ã@7!©‹¹@üMryR@¼[›³Q@( k¼¤é@“!·ü@ƒ! aw‰ü?Ši•àS@Ó‡•Ôâ @Müñ$êk@‘3Ö¢(<@{ÿ•i/@Ì ZInˆ@oþ’FJ@9ÏŒ˜õ­@HM…_!@µtåÇÿ @6‘J´@ç Å-E@èG€¨n@Jh4†)y@_m€BC@g8F ˜´@Àå"ul¸ @^¤üU– @ ðiG§_@z#‡Ð @q “ÄG‹@ùT0ly¬@jˆWʃb@Ôc ÝsV@à z°ùí?Þ5Ê2òsû?.Ò[›)Ñ @®Wiè@Æ_))ùV @‹qAXò@ Gî7Þÿ?¸‹ñ˜ø@à!S{;@ðÌŠD–é?rôAùe@nø‰L¥@Ç÷¬Òú?ðlðæ @È ÐØò4 @¦Ö’Q¦@¨Gb}@þ–c_A8@ÆM,·ó @”P}µÁú?ðOÑ®Õo@ºm…½*‡ @«°&åS‰@8'Œ þ?Ï–Û€Ø @£8œ†@À«Û÷8„@‘‘‹”U @ø˜çS`ç?>— ƒŒ@œ‰Õ‹v @°X @pž_[âì?3;\]å@P§Äçµ@¹vZJ @@¸:ZNÂ@¦”ˆë@äãàâè#ö?…iCN @•k°ð1w@Oýüggü?xI[ËLÍ@|uT˜>¥ø?„géÊI @$I˜ ²@8AZåÁ@òÎ:pg@á6ˆ:¨f@pSra @ÖÍ ô·¥ @Ì@Á@¬;÷?‚á'Dšú?vÅ:ƒ¯@BO3G ÷?éÊÌæ•dö?“©èÖØc@Ñ’Ét©€@Ì!Æ(Ûö?ZoÀð@€qaÛ¯@Ûà30 @©E4Ø>@‡À½2@<føÙE@&!8ÎÅ„@àÐ,bÄq@ œÿÁÆë?ˆŠ’Í8 @©Ùûw¥ @W™ö/#ü@õ܇Ð@²–‘f@šU+HQô?êùÕIÎ û?;]º®¢Ñó?Âv±¦Øá@ë:Ї@st3Qí@º±!P@J~W>//õ?sÆŸõ+ @|õ[Tÿ?´õÎ8s§@´™Ø,@‰#_–@›Ê]‡Â@N´cùµú?´fmÝ2ÿ?";èA.@=Ï¥o«@ŠGË> @³r•Êm@ C†Ð±@å‹fJnh@3©H‹Êx @s¨]Σ @ŸAâÅà@ò±E‚œš @Uµá³¶ @ñ¯ßœ®@tõm¦½@:Už­–q @€&°@˜ ÁÚ@ÏÕ±^k@é³[üzm@@ DÓ @Õ§Óò7Æ@W÷Äù-@1ßÇhî @Ðæ«òƒô?@Ú‡G›f¬?  ·ñûÆý?ÚŽñÀ¯@:b2J4åõ?g%å§@¾Om&ÔÃ@<PhúA@`š²IVóú?FØë@c’,ên@æi)³‡±@òØS§6L@c‹rõ½Ô@dÔ“ƒÄ@¤ÁA?¶@Ô–—”Oæ?\;ØÕ@šÊ:[öZ @¼þÄa @ïH‚!W@ Õ$Èd0@<¦}yõ @(evF<ÿ @bBεӊ@Ì­n»Y„@5‚„Wã@ï›O¬Ä@hÙ)Òü?__-qQ @i×Ñ>éö?»×ÜæÑƒó?ÉÑ ±+÷@‚þ_£Ò6 @_üR0°¸@È„´n¥-Õ?m´{»r@NŸÎ[‰ ÀðçþÀŠÿæ-ÌÊÀ«1 Våmû¿è\úÐtÀ€Ô¥¬ño À°šÏƒ\a À (ªçÀå©}™ÝØÀ€Ë»¥Ý¿.±üuÀ JeõÀ À?5Å\3 ÀȈG YÇ À2 ^ÄÀˆ ÈW@À«ƒÏÀæ-€?¡ÀÛ ²ùQŽÀÁy²ËèÀø(š3 À…Ôûá3ù¿rÁY§ˆ— À½XÊc›ÀXÂtæk÷—ìô¿ 1ª­ý¿—A`”ù/À“äðÁ’Àe®Ö‚ISÀ…ÈN\µÜÀNʤüÀ¢[NJâÀ³¬!´|ÀFÕâ¯fVÀô滯KVÀÐå͆½m Àˆ1þ¬ÿDÀ#£ÀávOÁW=ú¿FÐ1‰•ÉÀñ!i(óÀò̵ÛÍæÀm*A¼e¢À´83°80ý¿è2‘,ZÀnç°„JcÀîÖñÌ#Àék¹s°LÀð k_†À;—½˜v2Àn¥’.-ˆÀðP\ùªÁ Àšq uÀÞšÇ\Ï ÀÑ­ ÕÌLÀ8´öxgÓÿ¿’õƒ`ÀÒµÿî0‹À0©3¨ 2 À\ ¾BÍ÷ÀîÙº«+ ÀFcñÃÅ Àà ²T‘žõ¿±BH%âì¿ýf~‘ßÀ*¡E' éêÖ§ Àß©”5ZÌ À.sò:HÀ¾ôaå¸ý¿>D ×ÜIý¿ù‘•KýÀ~À/T°­Àú,ÎG| Àfå¤tÀÊò©+ÃrÀXñ¹þÀ(—óá:ü¿¥&¼~ôÉ ÀžàÍý‘6Àstatistics-release-1.7.3/inst/datasets/lightbulb.mat000066400000000000000000000046251475240274700226210ustar00rootroot00000000000000Octave-1-L lightbulbÿmatrixþÿÿÿd§¸¶ŽÓ¿@-Rd ™›À@W 2@´@mH3à yÅ@+ÄŸØoÄ@÷èÅœ É@ÔS&…3î®@nžÉ–V¨½@‡¹G~ Á@[t.å à½@” |Ý£ŠÀ@²už˜½@½r[.ŽÀ@ÁY€ÂÇéº@­>mHéÀ@Á [ áœ@Êq¬­uÆ@b¨ýõ.”É@ž"áåÒpÉ@Uõ›“aË@B.UË3äÂ@dÖ0`´%Â@Âõ­9[‘Ã@c½5ìþ=¹@†gákE¾@'§ja£ä¸@Ž0n†–°@®ñÉDÜž§@Ÿ¯vvÇ@Üw«·b€È@äkI×»@ô¡n´ëÊ@Ý‚î…çÜÀ@úÅáÇÀ@ä;=]»´@®¸ø‰Á@)Såü]Ã@Ü?á½¼@¤Ü²é!º@/ìÎ ­óÀ@‘’ç‰@äÃ@˜¢)r³-È@[·Íc²¿@®)‚Ó̆Å@䂜è„Ì@’±tÍã–¹@£êŒ,îÅ@g¤Œ3q?Â@¶'’…&¼@ Z¯Ã@ƾÑAK–@åHmT™6Ÿ@ø~€Ä’@¶t°ÁÂR•@œSn ¶@<ô6ºŸ@úc¥ñ‘@œîÚi œ¡@MFsAE @ËäfÏ}˜@h¡Üe ”@qVºl›‘@zYìJf›–@ED3¿W‹@xó¸÷Š5@\Ñ2?Ï·}@i6?¡ÿ”@hÃ;q]˜@=ÚEC¯³ž@ž &'ÿ‚’@! Eæ‡@˵—JH–@#ê…æ_Ÿ@™Ó·‡æ¹™@ö¦¼½ äœ@A2ʉ`™@ŸÂÕ¹¼ñ•@ü¤`h3”@j«TV'˜•@—ùggï‡@a&®Òa”@(Ö&Ó%‚@®U™lõ‘@–ð0«vs€@Ì42Œ^š@'gÙûÚ$‘@:R+Úï/™@ƒYî2§<‘@ /ªOº@Ã@jK @Éh¼«|š@™BÜ“Κ@–\ƒ»}R‘@é1/£Vì‰@üÁð—@’4$A=q@øõoð8)‘@HÜ·  ¤@™b•¿ÿ°’@ÿÛF½ —@ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?statistics-release-1.7.3/inst/datasets/mileage.mat000066400000000000000000000003031475240274700222350ustar00rootroot00000000000000Octave-1-Lmileageÿmatrixþÿÿÿfffff¦@@33333³@@33333s@@ÍÌÌÌÌL@@@@@€@@@A@ffffffA@fffffæ@@33333³@@š™™™™Ù@@33333ó@@33333³B@ffffffB@ÍÌÌÌÌÌB@ÍÌÌÌÌLB@€B@š™™™™YB@statistics-release-1.7.3/inst/datasets/morse.mat000066400000000000000000000256461475240274700220000ustar00rootroot00000000000000Octave-1-LY0ÿmatrixþÿÿÿ$Ð?è?è?à?è?à?è?Ð?è?à?è?Ð?Ð?à?è?è?à?à?à?è?à?è?è?è?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?š™™™™™É?333333ã?š™™™™™Ù?š™™™™™Ù?š™™™™™É?333333ã?š™™™™™É?š™™™™™é?š™™™™™Ù?š™™™™™É?š™™™™™É?333333ã?š™™™™™É?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?333333ã?š™™™™™Ù?333333ã?š™™™™™É?š™™™™™Ù?š™™™™™É?š™™™™™Ù?š™™™™™É?š™™™™™Ù?333333ã?š™™™™™é?ð?š™™™™™é?333333ã?š™™™™™Ù?š™™™™™É?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?š™™™™™Ù?ð?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?ð?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?ð?š™™™™™Ù?ð?š™™™™™Ù?ð?š™™™™™Ù?š™™™™™Ù?ð?ð?š™™™™™Ù?š™™™™™Ù?ð?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?ð?ð?ð?ð?ð?š™™™™™Ù?ð?ð?ð?ð?š™™™™™Ù?ð?ð?ð?ð?š™™™™™Ù?ð?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?š™™™™™Ù?V-²?Év¾Ÿ/­?j¼t“¶?Év¾Ÿ/½?çû©ñÒMÂ?ÙÎ÷SãÅ?š™™™™™É?Zd;ßOÍ?¦›Ä °rÐ?çû©ñÒMÒ?j¼t“Ô?ÁÊ¡E¶óÕ?X9´Èv¾×?š™™™™™Ù?Ûù~j¼tÛ?sh‘í|?Ý?´Èv¾Ÿß?¦›Ä °rà?ÇK7‰A`á?ƒÀÊ¡Eâ?333333ã?Tã¥›Ä ä? /Ý$å?ÁÊ¡E¶óå? +‡Ùæ?-²ï§Æç?yé&1¬è?š™™™™™é?ºI +‡ê?•C‹lë?'1¬Zì?sh‘í|?í?“V-î?ßO—nï?ð?dissimilaritiesÿmatrixþÿÿÿvàd@ e@àc@€f@€d@`d@`d@ÀR@ d@€c@ e@`a@^@ d@€d@Àd@``@€b@ e@À`@Àd@c@€d@@d@ e@€e@àd@@e@@e@Àd@àd@Àd@€e@@f@Àe@X@ÀS@`d@ÀW@`a@@Z@ d@ a@@]@€N@@d@`d@àb@À^@@`@ a@@a@e@@`@@_@ a@;@@]@ÀT@àb@Àa@€b@€\@@X@2@@W@ `@ c@`e@ a@Àd@À\@ a@b@@d@À\@]@ÀW@€d@€d@b@€U@€Z@À`@€d@f@@d@b@ `@@Y@=@ÀV@€b@`a@@^@àa@c@À\@€^@\@ a@Àc@€e@a@Y@€\@àc@àb@9@@R@@d@b@àc@€`@àc@ÀZ@@_@ e@€`@b@@_@^@b@_@Àd@`d@`d@@c@`c@€a@àb@ d@`e@Àe@àf@Àf@`e@`d@`f@Àf@àd@Àf@@f@ e@Àe@`f@ e@àe@T@àf@ f@€e@€f@ e@ f@Àe@`f@Àe@àe@€f@`f@ e@ f@g@àf@€c@]@@e@€]@€b@€Y@@d@ d@ c@ÀW@@c@[@@b@ f@@`@€_@À\@À`@À^@Àa@ d@àa@@X@ `@@[@``@ `@àc@@e@€e@`d@€e@à`@À^@€b@àa@d@C@ÀX@€b@@`@f@ f@ c@@d@À]@ b@``@@^@@b@c@ d@`c@€e@€d@c@ a@`b@àc@Àc@ c@Àc@€]@@e@€c@Àc@b@ d@Àa@ÀU@ e@€[@@T@ b@`a@Àb@ c@€d@€d@@b@@]@D@€_@àb@Àd@@e@Àe@`e@Àe@@c@e@àa@@e@@e@ f@@c@@a@àc@`c@àe@àd@ f@ e@Àd@ e@c@ e@Àd@@e@`e@àd@@f@@f@ f@À_@€[@@d@€e@À[@;@€Y@ b@f@e@@d@ c@ a@À]@@[@@^@@R@€T@À\@@a@@c@àb@€]@\@^@€_@@]@@c@€c@^@@b@`@à`@€e@`e@@]@€a@À\@@Q@ b@`a@ d@@d@@c@àc@ e@Àb@ b@ c@`e@`e@e@ e@ c@€U@À\@\@d@@e@``@]@À`@Z@ `@€O@b@€b@Àa@ a@@[@€]@ÀY@ b@`d@Àe@Q@àd@€d@ d@ b@€f@ f@ d@€f@@b@e@àc@€d@Àd@f@`e@f@`f@`e@`d@àe@ d@g@Àd@ d@ e@€a@d@Àe@ c@€f@ d@€e@àd@àd@ d@ f@Àe@àe@`f@ e@@e@Àe@`d@Àf@À[@^@àb@ e@€e@@d@`c@@^@€]@``@``@ a@ a@d@Àd@Àd@ b@Àb@Àb@`b@àa@T@€]@€d@€d@b@`a@@`@À^@€X@@T@ÀY@ÀV@[@b@ b@@a@[@ a@€]@àa@àc@Àf@€f@Àd@àc@€a@U@ÀR@€E@@\@^@ a@`a@ d@€^@@X@@S@@\@€`@ b@ f@@]@`a@I@@c@€c@`b@`c@d@àb@ d@ d@c@ c@ d@€e@ f@f@€T@ c@Àc@ d@ e@€e@e@@f@ e@ d@``@ d@@e@Àe@f@ g@àe@ f@Àe@Àf@f@Àe@ e@ f@ e@`f@ f@ e@€e@àe@Àd@€f@X@€_@`c@€d@d@e@@e@àc@``@àc@d@Àd@ e@Àe@`f@ a@@[@@`@Àc@@c@ c@À`@€G@@W@€\@`a@€d@`f@@f@À`@€_@`a@@b@Àa@`b@àb@€d@€c@àb@€d@Àd@`e@@U@]@@b@àa@``@]@^@P@@]@€a@@c@àd@[@@_@@Y@ÀZ@a@`c@€[@À]@ÀZ@ `@ b@ `@Àa@À`@€d@ c@À[@L@ÀS@`a@@b@H@`a@d@Àb@ b@`@ÀX@€N@@R@€L@€`@€d@ b@@_@`@@_@@b@Y@À^@ÀZ@ a@Àa@d@ d@Q@@[@À_@c@ d@ e@À]@`a@d@`e@Àe@€E@ `@`d@@b@F@€\@ b@€O@@Z@:@ morseCharsÿcellþÿÿÿ$ÿ sq_stringþÿÿÿAÿ sq_stringþÿÿÿBÿ sq_stringþÿÿÿCÿ sq_stringþÿÿÿDÿ sq_stringþÿÿÿEÿ sq_stringþÿÿÿFÿ sq_stringþÿÿÿGÿ sq_stringþÿÿÿHÿ sq_stringþÿÿÿIÿ sq_stringþÿÿÿJÿ sq_stringþÿÿÿKÿ sq_stringþÿÿÿLÿ sq_stringþÿÿÿMÿ sq_stringþÿÿÿNÿ sq_stringþÿÿÿOÿ sq_stringþÿÿÿPÿ sq_stringþÿÿÿQÿ sq_stringþÿÿÿRÿ sq_stringþÿÿÿSÿ sq_stringþÿÿÿTÿ sq_stringþÿÿÿUÿ sq_stringþÿÿÿVÿ sq_stringþÿÿÿWÿ sq_stringþÿÿÿXÿ sq_stringþÿÿÿYÿ sq_stringþÿÿÿZÿ sq_stringþÿÿÿ1ÿ sq_stringþÿÿÿ2ÿ sq_stringþÿÿÿ3ÿ sq_stringþÿÿÿ4ÿ sq_stringþÿÿÿ5ÿ sq_stringþÿÿÿ6ÿ sq_stringþÿÿÿ7ÿ sq_stringþÿÿÿ8ÿ sq_stringþÿÿÿ9ÿ sq_stringþÿÿÿ0ÿ sq_stringþÿÿÿ.-ÿ sq_stringþÿÿÿ-...ÿ sq_stringþÿÿÿ-.-.ÿ sq_stringþÿÿÿ-..ÿ sq_stringþÿÿÿ.ÿ sq_stringþÿÿÿ..-.ÿ sq_stringþÿÿÿ--.ÿ sq_stringþÿÿÿ....ÿ sq_stringþÿÿÿ..ÿ sq_stringþÿÿÿ.---ÿ sq_stringþÿÿÿ-.-ÿ sq_stringþÿÿÿ.-..ÿ sq_stringþÿÿÿ--ÿ sq_stringþÿÿÿ-.ÿ sq_stringþÿÿÿ---ÿ sq_stringþÿÿÿ.--.ÿ sq_stringþÿÿÿ--.-ÿ sq_stringþÿÿÿ.-.ÿ sq_stringþÿÿÿ...ÿ sq_stringþÿÿÿ-ÿ sq_stringþÿÿÿ..-ÿ sq_stringþÿÿÿ...-ÿ sq_stringþÿÿÿ.--ÿ sq_stringþÿÿÿ-..-ÿ sq_stringþÿÿÿ-.--ÿ sq_stringþÿÿÿ--..ÿ sq_stringþÿÿÿ.----ÿ sq_stringþÿÿÿ..---ÿ sq_stringþÿÿÿ...--ÿ sq_stringþÿÿÿ....-ÿ sq_stringþÿÿÿ.....ÿ sq_stringþÿÿÿ-....ÿ sq_stringþÿÿÿ--...ÿ sq_stringþÿÿÿ---..ÿ sq_stringþÿÿÿ----.ÿ sq_stringþÿÿÿ-----statistics-release-1.7.3/inst/datasets/patients.mat000066400000000000000000000660711475240274700224770ustar00rootroot00000000000000Octave-1-LAgeÿmatrixþÿÿÿdC@€E@C@D@€H@G@€@@D@<@?@€F@E@9@€C@B@H@@@;@€B@I@H@€C@€D@F@<@9@€C@9@B@>@€F@D@9@€G@F@H@F@€A@€@@C@€C@F@F@€B@€F@€B@>@€C@E@E@€H@F@€E@€G@I@C@€D@€F@B@C@=@<@>@<@=@B@€F@@@?@H@9@D@€C@€D@€@@?@€A@@@E@H@A@€C@<@=@@@€C@€B@€H@?@€B@C@€F@>@H@H@9@F@€H@€F@H@ Diastolicÿmatrixþÿÿÿd@W@@S@ÀT@ÀR@T@€Q@V@€T@€S@€U@@S@Q@€R@ÀW@ÀS@W@ÀW@ÀS@@S@S@ÀR@ÀS@V@€V@X@@S@T@S@ÀT@@V@W@ÀT@T@U@W@ÀT@€V@@U@€V@€R@W@T@@V@X@@V@@S@@T@S@ÀT@€S@ÀW@ÀV@ÀV@€U@@V@ÀS@€R@€T@S@@T@@S@@R@@U@S@T@T@ÀS@€T@ÀS@€T@ÀR@ÀV@€R@€S@@U@U@ÀR@€S@@T@ÀS@@U@ÀS@€T@T@T@W@W@X@ÀU@@T@€V@@S@ÀV@ÀS@@R@ÀX@W@€R@@W@€U@Genderÿcellþÿÿÿdÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿFemaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleÿ sq_stringþÿÿÿMaleHeightÿmatrixþÿÿÿdÀQ@@Q@P@ÀP@P@Q@P@Q@Q@€P@Q@€P@ÀQ@R@@P@ÀQ@@Q@@Q@€Q@Q@@P@P@O@€P@@P@€Q@€O@€O@Q@ÀP@€Q@€P@P@€Q@ÀQ@€P@ÀQ@€P@€P@€O@ÀQ@@Q@€Q@€Q@ÀP@@P@Q@O@€Q@ÀP@Q@O@P@€P@R@€O@€P@€Q@ÀQ@Q@€O@@P@ÀP@€P@Q@ÀQ@€Q@N@P@P@€P@P@R@@P@ÀP@R@P@Q@€P@P@Q@@Q@@Q@P@€O@Q@@P@€O@€P@@P@Q@ÀQ@€Q@ÀQ@€P@@Q@@Q@€Q@Q@€P@LastNameÿcellþÿÿÿdÿ sq_stringþÿÿÿSmithÿ sq_stringþÿÿÿJohnsonÿ sq_stringþÿÿÿWilliamsÿ sq_stringþÿÿÿJonesÿ sq_stringþÿÿÿBrownÿ sq_stringþÿÿÿDavisÿ sq_stringþÿÿÿMillerÿ sq_stringþÿÿÿWilsonÿ sq_stringþÿÿÿMooreÿ sq_stringþÿÿÿTaylorÿ sq_stringþÿÿÿAndersonÿ sq_stringþÿÿÿThomasÿ sq_stringþÿÿÿJacksonÿ sq_stringþÿÿÿWhiteÿ sq_stringþÿÿÿHarrisÿ sq_stringþÿÿÿMartinÿ sq_stringþÿÿÿThompsonÿ sq_stringþÿÿÿGarciaÿ sq_stringþÿÿÿMartinezÿ sq_stringþÿÿÿRobinsonÿ sq_stringþÿÿÿClarkÿ sq_stringþÿÿÿ Rodriguezÿ sq_stringþÿÿÿLewisÿ sq_stringþÿÿÿLeeÿ sq_stringþÿÿÿWalkerÿ sq_stringþÿÿÿHallÿ sq_stringþÿÿÿAllenÿ sq_stringþÿÿÿYoungÿ sq_stringþÿÿÿ Hernandezÿ sq_stringþÿÿÿKingÿ sq_stringþÿÿÿWrightÿ sq_stringþÿÿÿLopezÿ sq_stringþÿÿÿHillÿ sq_stringþÿÿÿScottÿ sq_stringþÿÿÿGreenÿ sq_stringþÿÿÿAdamsÿ sq_stringþÿÿÿBakerÿ sq_stringþÿÿÿGonzalezÿ sq_stringþÿÿÿNelsonÿ sq_stringþÿÿÿCarterÿ sq_stringþÿÿÿMitchellÿ sq_stringþÿÿÿPerezÿ sq_stringþÿÿÿRobertsÿ sq_stringþÿÿÿTurnerÿ sq_stringþÿÿÿPhillipsÿ sq_stringþÿÿÿCampbellÿ sq_stringþÿÿÿParkerÿ sq_stringþÿÿÿEvansÿ sq_stringþÿÿÿEdwardsÿ sq_stringþÿÿÿCollinsÿ sq_stringþÿÿÿStewartÿ sq_stringþÿÿÿSanchezÿ sq_stringþÿÿÿMorrisÿ sq_stringþÿÿÿRogersÿ sq_stringþÿÿÿReedÿ sq_stringþÿÿÿCookÿ sq_stringþÿÿÿMorganÿ sq_stringþÿÿÿBellÿ sq_stringþÿÿÿMurphyÿ sq_stringþÿÿÿBaileyÿ sq_stringþÿÿÿRiveraÿ sq_stringþÿÿÿCooperÿ sq_stringþÿÿÿ Richardsonÿ sq_stringþÿÿÿCoxÿ sq_stringþÿÿÿHowardÿ sq_stringþÿÿÿWardÿ sq_stringþÿÿÿTorresÿ sq_stringþÿÿÿPetersonÿ sq_stringþÿÿÿGrayÿ sq_stringþÿÿÿRamirezÿ sq_stringþÿÿÿJamesÿ sq_stringþÿÿÿWatsonÿ sq_stringþÿÿÿBrooksÿ sq_stringþÿÿÿKellyÿ sq_stringþÿÿÿSandersÿ sq_stringþÿÿÿPriceÿ sq_stringþÿÿÿBennettÿ sq_stringþÿÿÿWoodÿ sq_stringþÿÿÿBarnesÿ sq_stringþÿÿÿRossÿ sq_stringþÿÿÿ Hendersonÿ sq_stringþÿÿÿColemanÿ sq_stringþÿÿÿJenkinsÿ sq_stringþÿÿÿPerryÿ sq_stringþÿÿÿPowellÿ sq_stringþÿÿÿLongÿ sq_stringþÿÿÿ Pattersonÿ sq_stringþÿÿÿHughesÿ sq_stringþÿÿÿFloresÿ sq_stringþÿÿÿ Washingtonÿ sq_stringþÿÿÿButlerÿ sq_stringþÿÿÿSimmonsÿ sq_stringþÿÿÿFosterÿ sq_stringþÿÿÿGonzalesÿ sq_stringþÿÿÿBryantÿ sq_stringþÿÿÿ Alexanderÿ sq_stringþÿÿÿRussellÿ sq_stringþÿÿÿGriffinÿ sq_stringþÿÿÿDiazÿ sq_stringþÿÿÿHayesLocationÿcellþÿÿÿdÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿSt. Mary's Medical Centerÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿ VA Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General Hospitalÿ sq_stringþÿÿÿCounty General HospitalSelfAssessedHealthStatusÿcellþÿÿÿdÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿPoorÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿ Excellentÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairÿ sq_stringþÿÿÿGoodÿ sq_stringþÿÿÿFairSmokerÿ bool matrixþÿÿÿdSystolicÿmatrixþÿÿÿd_@@[@@_@@]@€^@@^@@`@À\@À\@€]@€\@À\@À_@@`@€\@@`@_@À^@À]@@_@@^@À^@€\@`@ `@€\@@\@@_@^@À_@À`@@^@À\@À_@@^@À_@a@@]@_@^@`@]@€`@ a@@]@]@À]@À^@]@_@ `@@`@€`@@]@ `@€]@^@@a@@]@@\@€^@À\@^@@]@À^@À^@À]@€[@@^@@a@@_@€^@^@@]@@_@_@@^@€]@^@€]@€]@€^@À`@``@@\@@_@à`@`@À^@€^@@a@_@@`@À^@ `@`@_@À]@a@€\@Weightÿmatrixþÿÿÿdf@`d@``@ `@À]@Àa@Àa@€f@àf@€`@`@ a@Àe@@i@ `@ f@àg@``@`f@€e@ `@@]@ a@@b@À^@ g@àa@€\@Àd@@g@€_@ a@@a@`g@ h@ a@h@€]@€f@`@€d@àf@ e@@h@€e@à`@Àf@@^@Àc@`f@@e@a@à`@`b@@g@_@À`@@e@€f@@`@@`@À_@ a@À[@À`@ g@ a@a@@`@ a@@g@À_@f@À_@À\@@f@``@àf@@h@€_@@g@€g@ g@^@€`@Àf@^@À^@ a@ `@g@ f@_@Àe@À`@`e@€g@@g@€e@ f@statistics-release-1.7.3/inst/datasets/popcorn.mat000066400000000000000000000003031475240274700223120ustar00rootroot00000000000000Octave-1-Lpopcornÿmatrixþÿÿÿ@@@@@@@@@@@@ @@@@@@statistics-release-1.7.3/inst/datasets/rundist.mat000066400000000000000000000227101475240274700223300ustar00rootroot00000000000000MATLAB 5.0 MAT-file, Platform: PCWIN, Created on: Fri Mar 31 11:35:28 2006 IM@%xœŒ™ <+õ; @‰%ìß81ëÃÁ ßJŸfÛÉ™ÊA`ýÿôádÐgõJŸE ™ÇCAF{?±ç¯13&}¸~ëC'­Ë"mÐ[ˆ¥ÆdýÉÜYMÜýW_[&}¹ô•ÿ»/mñÌ<ÀEˆ HúÂ Ä SM@Ö=NàN"Ë?™õçaПøwœy2 ªÅ¸˜êò ñ@#%`4›ìÞ /ÿzN™ôçeÐ_éïþðõ3@(×Þ³b,k6α•SwrR> qI {±Ý`gÝnñOfóð1˜Gåïyd`hœÐßù€G§9´ÞôIîƒÀ¡Þƒ÷ƒßÛߤ šEO¼…Ü"Vú<éLæáÿmúúxÿú+ß—[Vôí2‰D“ùÒÓtuN?ZGWÝ7‰hZÛžö˜¤ý¾Â#DS-7£P1š–~™¯¡_çz^d¥)EŒtd”^WÄVÿ‰î/—‰¿Õ üùþîÚ¿¢†2ûè}m3‘Ût?O"TÇhê˜n¹G„6/Ѳp³*M_’Ù: ´t$˜Ó®Ããl$-iÚù6õ›!M«ïM8èÐ4ÞŸ“sHï :L¬ø,dâS€ÏÐß|R¨“tu¬r ¤ûô­h¦é™àTkÚ<1ÚÇ%õ@ Vq$yƉv½ëÙÐÉ0( ð5÷NÁ‡·jµSA Q»;K»/{Øz2·(±Þ  Ý_rüÚGzxÁP@¤û-eâW_Êo~k©Tº–Š%ÓýšrMÒ´mt'AÆáÌ÷Û¼/W h~r?Wmÿvt÷võÖ×€\k<ÜRøfo|×kÎ[®bá­ °ïÄFëKO@ 4*ùpí~A¶LÒp3娷íŠïJ&¾…øŽÿÍ÷U€îWå,çušæG£t¾ƒ‡Jí@À×}®œçÂu%öÔ‚”±èú“ƒ`­JZ nø˜+ÈÛ¥¤ÀåjÚ¹íÞ#ùrqÕ~z$ åB!‹¸¡Ü¶§.>,`Èa:m1Ù9E¢OA þø’ϱ³+þk™øfàÿÌoþ§¨²tÿS7ÉiŸÎ¤LóϪ-oAY“A@ÃV£Øù xFa #ÛA*ʵ&—«@Ekt½ ]aÈñˆ¯Üi èSºˆ×2¨gZfI¿5dk«Tæ:3(4D*^UN²í¾6.ÿD23>!ÍVCËE{]66­ähd’C„AŽôßrÌS•iþ)ÞÑ_»iŠí½h ­?s n€À7gýCB@‚õpHý”T˜±ë¡5;¼ 6­2u…8Y¢]²¢ ¬¬e‘3P\¬ç_¹K§ÃOçÐËеêÇi™]— ²¼Ö™+Ug sœª÷9A&B=ÖÏâ¡›^%cæŸvÑrÕmÏâg‡èyZ™äe'÷·<‹T=úótn(d†¦·ƒ¾–ù€€üéŽpñ&ÚóÜu'æ¶d±n&>€ ñéÉò7‡C/ÝVŤg@E¡RiòÈ=HËýãfŠí èrO~˜–b7ôéaÊ»”A¨wéÓpÛÕ!脈b¤þ÷^¾L<Ô>ñêOÔ @?A‡m@qräÐiõDÃ-Œ¶NgÂö_$­äêb’KŒA®Âßr-SMiyúÛÕùiï'ú¶ ) áÉ‚1Ïx¦¦ãÛÂ’´u"ÌiˆgÍ*×]‡,o ˜¶B› =ÉG3'!›MÞ0¸¡ó¼ ¸ÎZ-vu®dÌɯė÷ŠÏúE¬ Âü>ý¯Û×ÃWïµzuˆÀ»tõ7ßä`µi{±ø¶AH;½§’–s~ÿeÔJ¾&ùÄä+ý-•z€–8æ0®•Êu„?K@¢%^*…Ú®÷‹Æ»@Iò¹{{K  Óz‚1ý] 9Ë^ª’ ¶Ú±]kᇬ>¬¾gž/…»u[¬a¿Ýu¢l®p®ù¾¯/Þ°(Á>æS¬ßWû´³Äv3<ïxô¹+œÒ{ÔxÕ¶FÇd#4H°‹zcÉm9ØÏ¹ hÖybqÜdÀ®\½’sŒIN 9+ÍI¡R}h9•·Åùèƒ@A¾Ÿd ¸q6³gÜ6ä¿Q@9›˜Éq„åý8ޙרf*¼á†³+æFHg5aͬ¹@!xÆ/>æXl16faÜ’«Y‡ðôŸº óßù¹ ez>ùòª~Yx%œødyQÿ*˜^4¯€ÝZ|9l‹ás»³>eÀ¤øB™høáŽQ]mgXX>¢[ÓCö3·ìÿ±’wŠIÞ5 òÖþš—öñtŒPLš®Û‚@™N‡Õ‡Ipvû#ùJGˆ+`š‹pñÈígyòõ(¾›r¸Öó–%\ ¿cÔþn­ªG>o’°³;žP?ˆy¡×°ž©4xÍq^W•/VÖð žP!PS¨pnÆé†46$¸µgJU€ ‹ýN}iµ—òó-fOÃÍ^—F®+ÀFf¸:Û÷œè o¬.€ÞÎ íBaÀd!ß¿A¦çžg’{-ƒÜ¿æ RShë¼,²&Ê$°Nl¢|€\yy&ãH êæ$xþ9]’M Ükºn·þ¶*ÈÍn ª~`t0žKý¾Eðl]—ê&NÁkM"L”‚ønÄ{úvž<ÒZ²D¹X£Èª˜$}›…¸g%ù BFÈùe©ÖRS¤)5mgD±rïÉ&È.F–ç"½‚Ì5‘¶IóÏ»äËrBI€ùHèåê÷ùÏ¿1M 0¸Ëuɼš* _ÉO÷å—d¿õ×üSTj Ð_^çòð8L»¾*Cç:Z/Ý€NTŠP%?òÀÁé¬N.ØÁN[¿ðpdÁÛ…šïÈÉ´Ãk_RÍ® ² "šÁk¢´‰1³ìý·2iŠë8"⌈E}¦V(‡"|½ñ,ñÈ’c£ü€JR¦Yõ¶§#ér¯î2ŽóáMfsç‘ÛPù¨ÎÓÈ¡©öçoCf±ËgF#*Í×¹'L‘·6tß‚°Ë*/Ÿ>²/Þ™É/«}¸Â‹ )º~å0O¥–€UÇÄ·4€”Ú@ÿôYSQ’ÕyÁÙ U°D¿N2Pƒùm“K™…ãˆzWϧ¼„]Ÿ&¼.Qæ@ú²Oˆùi"­Rä!‘n'¤kȶÌÞ5ñ˜½ã&Kò,­Uöºh¢ªœ¶yþòµÁb×°v7¢0T 6E!_ƒŒgï*"^½9)×Fiµö›3éTîùwˆüZ>kq‘§È£UÆuÖP".e1$¦xI_ ^¿™ì‹|Æ´-ùIÈ&ëµm©Bç!È„‡4¿òX¤RkHÀGWÕk@«3o©Îû@#ÎÜ/„¾AÄ!;‹³ðh‰;ûy¬ê˸¿–c ðUˆ7¿¢T¸LœˆõFÄ’‡ÔÊø3>OÁ¼Zäú×çbk ‡y{øƒ×˜Þ@ƒ£«†_pPJCÍ R”"Jy[6a5·æjáPCƒ"ç»™ª¢ÛöG#̶ ±*皎Ðç¶>I¡Õÿ˜d]3‡´‹}ž´Ò‹øù⣒…åHˆª„f»O bðÐ?â–,´Âe .ëpû•Ë2•ÚB„L®ñxêÜìVø‘µ‰lçÆeÖÂ-ìZXâà'á)ã̓ð‰*ã9]¯­Èƒ*³5ê"£ºwã“|ĆÃjØÛ†dØ\4×ïü¬{=îë²}º©¦ÄI=÷5Ò æ‚9ÚÞ,0×âŒrÞSÉ¢ù£ùÏ_y†£zvssÊg£dggwÓhìñ«“çÃŽ¡oÙ›ŸÚŒž%)v‡Ð¤„¡Oùr6èZ ƒ«5Aú¨ÏÉ]˜núzTø‰]v÷¹Hàð34Éø9BH¼:ÚØ»Â‡À„ >S¿ò¡R©ý¤ZÓŸ{%“§Á-¤q¬Ÿ%PdÝ.‰¡#zpl÷rº‰I| ç‡Ë†d7Pðz{D|2½îc.÷6ÙÒ»%ìh^Ï‹bK:¶êèse6÷Þ½èήÄsoÆBѽ±~©¾2ièåÃÛk]G] }E·ÅU¢–·òçv×£Aéïש„¼B«2' ý÷¼FÓõ­®%oEçEôó£_ z ¹·ìëPyQyÿèïwÑ≥GNWPß>ÎH¢8ôËtú¬ì*\rŸÍ!¡"µ7aEA´ò\–3;ñå '"N² 8Íÿ‰¶½ N’TBã•÷ð@SÉæ‹-Ð(Â\Zè tÐÆsìoá”'WÌŒïè"ëç«ë &#êq÷à½íH¹ÀR” zÁû>ºBèA]óÍö¬~(­ßâr產b^¥)S‰¾ÿÑ0vÉûO”·oüËö Tî8G]à*ãœßþEc5V\ïøªã™¦|døì¸¥,féÐÍw[ž%´VõÉb{‡e\õ‚×`5eÞÀal =¨?UÀVkp§ë×}D߯½"I¿Fÿ°=T½O­ ÍrˆþpW0íc‰D/îA†›ê'„Е¿2áE`À럋^´í •J2z°÷Ú‡š Ð µâ帥IHiñoÿ…x¸·xpL;™ ¹ÕL²ç²õ@„C" šÌ!v†Ï"Ä9P×Ï2XðŒ¶xÆÌ^ G#®7¡’bÐîv4#Kª`è:!]?[-†‰E' ­U°cs2~’0Æã©->g´«VùúÙæìA¬âÞ¦ûjž˜Êè1‘µÏ|1iî3bÁž~XkÑ£Ž[¼±~¹‚fvXQˆûþ^Lcth'ÐK¿þNzTB;—p‹[ÔG;Üî+¸€÷qnùЊ†%½iÉByºû·%جpÓbÂm=n\¿p£mo¨ qmüÑþœ]Ž‘ª°,®VªN¯½  ±ø[§­ˆK*Bt:Ø_x(ç•ø‘äÎÆ?µÑ бuaAúJÎ<',é.*j×·ƒá=á×´IZ;¹¡¤/¤G»b]²ÓÞ[>÷x¤üŠ?ÆzªW~wVV8¦ÖådyKó^­t+Œu k»‡Y?|¿.ªÁÆ‚¿ªJê0ñÈÞÐĆÇÇù’«¡N±\+ïLûr,cbÛRØu,ÓÝøAÕî ØèÓÜAöã˜âXirF´ æ¸8‹Õ<±ÜÇ3¦«­Áòíy$µí=úî~¿ã|gÎ ?}&üäðü…m{D•'„7fŽkZCyÆ»)YËÊpÎYE Gvx|Ûè[!kÄO>¾µ¡é<ò<áàt:Ry:<Ë =Ûº¸MbºýžþT¿ø ݼq¿úù=ìkdšMª‘1&±¶y Kl ¶™hYslñ&SR_ús¦Sý¦Þ§}²[劳@ ÃØÍ›3Ź]ß°ÚªÎá{çXñ,ãç‹d)>ü2kXÍgaa¼s眢zü׎þÏû„ñŒçbJÞòáýϼuDØð³CC3\³ëÕ—-|ÒÃØSëí6R/±…í¯šôÌJ±ÌR¾Éáú$LÓ¯®ÍYê F}¿ø\iQ 3¿ï ŸÚ2ºÂgÂqŽk~áHÛ^Q•AäýjS7|¡Ø*ÇÙeSØ>év÷<Ï<<™¦[w–‚l g{-:…T{E\¯1A ‰îä›»sQ{›ÛªÉÇFPïL¹ŸG<6`9*B;œ­ì°Œ`M΄Éxìô¥¥J‚Ì]líŒå«S.o0ëƒ[žÚ sØŽŸ âòR"øðóÝç]”ñWüϋ᭻ñÝÕ½î5¡Æ8[¾wÁB¤-þNÞ©ÁÕÁ /Ü$+ÊçŠkenÓ¶ntÅw؈>ÕpÆ=âê×Õ<Ý×.f|ßfñ úk@xµ§©÷‰ ®(HÝ_-÷‰Þ|•ú3»ä½ì–߉¼Šw\J+ÆFû&úx\‚0òþÕìÇÊØÊÿ ˜ð”gÀ“ð OÚöŒªšu$šŒ/ÇC3‰î\ZTØåš˜?dýóÂ+ ‘‡¼FuÁÃҨ܌ìˆÏ1Ôð§›l–WÚö–Ï0JG ³ïã’sÒÙ‡ýÑrñ¾ä™4¬Q>)ØYºãuwjK¦±&Ö Q—$𸖺öo©ÚøWØ›õý[kü^•ƒŸÞ¾µwIÔë$>¢&%÷¬ó~¾(×E²¾ÒaݱH,ÅC-Š+7¤Và=§ ÷UTâaío J|Pˆó§S9Þ-/`0 p }Àë–“wÌZ»ŸÅ­‰=Üž\Q¸*4—¢å‚Kï“.¯Ý„âßl2Ø Hr¸­ì HðÔ,6Z±ìóI½+«“w YájË„«®Ä_¸JóÐV~ó.T¹&úRŠp¦¦Àï"mM 2“ÀG´¸Ù†L©W<ž°$£{g6—é)– ï©„ì†þUØ]ÑŸ•‹昃Šo ÖQß/e]3Ö™þ´È½‰Ï^›¼-RÁ)ÓmÇ·äÚàA ßýŽÿ8}ÓòñªLÜX—»¶w¸ן²=tqäž¹sëi›Aüö×VuÊÓ¸éØŒš‰Îþñ %'ʼn¼ÁΟGl†‡¼Ñ¼©³â' ›/³á%ó^35´g'§\IÁL––pëD»ïoݧqͬ2ÇpáÓ“»ò_à’’Uã?Eïà’·MŒFSqïÔÆØ¢7þbã#-C '‡š`g4¸ñíü„êÏÅWø:2ố_•_ø~¢u{$† ŸÉTC8ËcR¨%ðÇ´ÈqQîÈAµ ÎE$U¼/RpG0*|¢/¼éGMJEvuåjb#-.fTŸÄÒñÀjYMØÓˆd„4'®t‘xM³w®ØÅþº»ön{bKH]äEÜL¾ãø$ç#¼OÝ ÍìÇ/è¼;6[û¿˜&>ÙrJ”,|î\ß³™ŒÅ|té’7«=Ÿ’É®ÒPšYwQC\Äm?ùQ³ÌgÉi½Ioú¢œÈésè‡[?ï<•c3lCÞõáEÞ+S²éägrI‰¼&zÍ÷úC*ä”Û9ÎYkÉVd™/[«‚R…_ÐUï&yÏfy!ï?IÌò/Æ ¼°8£ù‡œoˆäD$ó~¯™y¯âqÐÔ•D>™ðÕ§åÝ>rX½c;Ç‚?™Œ®Ï½±>–,^¼UŒCçÙmTUK×0Ÿ¼."y£=ç-òøŒTÍòÌ2ÁÃ×='¢‚lú“ÖW#fß5¸/–‘ïFîÒã©Ì¯šõa çf­¤_eSþìÆ{½¹€¼¬kè-_ +²œÛÄ”ÌÖö°°“# lj©ËøW‡ ‡™\ÔHFa]ìGÜU.&!{w.·{snYV)žÀ5·sHý6¾ I¶¿:Ì÷ASa‡pÓîþ½}ç6ãõ— Ü7‡1±Ç9~)Y-™?@ð'ìÿnNÂ÷aûß­ôoˆÿËáú7ØÿaA@³Šgø…/Ú’YÆAjôÓíX]kìPr±Ø¶Ç8¸U^ÿ|¾ùò&ÎèÑeòê¶Ppù4%ÉêÃ`õZ…‹,R^!ù) )œöÂÜ]ÔsR“Š1›‹}¦3X·™ågUœÑ“4ç|ØϦ‡DDŠ„àþá ç ¬t\Íðþ9®èfü¥f$U¦lßFÈÕÊMd &us©’`ÉŠÇjJC øleá.ÕNáß Zy¸"'Ä&Û <¯²¿Ô¥bJ›«Þn·Mî³ñCÅöà›[Ðzƒ;pÔß¿µîÕ>)öš‚àö츪ûF °iìÖdªȹ³I5.^HÙHü½Åžlí«ÝÆÁÄÎvé¦ñ'äÇ4kz&^xÑÚ¨„ÕgWlЉZ¸Îyé¡§Ï>ÿì?èýoýMÿ¦¿ö?~V€—aXbLqú•,I^«dtpUÍØÀpïXOá’qÏù~T—I¶_Ù+¿•’—¤“í~3RÿíÁÓ³F,߇éµôÃV7×cʹ1:vCZ& ˜Ma+J7xÛK•qþQí¯ÿœÀ›£ÓÞó=OÀ9Éÿþ k-Îwr¥é­,åV¤Kmt´[¼±œ‘-ɆàaÎL‰ Vé˜ÉÑKç@£Qê¶ÎÏQÀÒ#y=ç˜Ý¸ü4UïàVíö1(ÉæwüÒ^ƒ®4¹Ñ¥Œ·çEð”‚%­ãCŒÞÇÀÇ댒]K½»£ì¬GHg2N¼KÏ’<äDEÀÿÊÈ¥"/0Û§2w8Îì’ó¿PtJ¸Yì(ö-Ý2\÷À4Nà€»jð°#kÒò/ãù[ú½÷à?"þà°í7–¿:Þª!ÅRhxºÎbR¡H›$–ÕÁX51–T®Ÿ”%w’ŒLõèÍä(ÖIÛ¿Ì)/Üx§’yí¨–•&fC”Úe«|SÓãït"}Àª<¾E­oUĵªŽ£:g|gËeöÉLœ_~VaqC7l]Öï6Æ ýt¸nÓU5ûJ xÊ!°ãÃÆ§ âgF˜Ó0F½” ªïÂ|°"æc~µøÚè‹úªõûkü^HŒŸ‘Áœ «yÐÄPY¿G„§·xŒIqÂ-Åu†2Ÿø`㎽7¿´ôp ¥aüpâá¾vy..(RËÉ+º DÖV̳´Âä–ÔûK.¨F±<…I/b,D4SaC9³z „ãe9â,`¼B¶È§}*°txßóƒp,ùü}ž½D)¯ŸNô?8©ýÆÉÿ§Â󶕬ã¢FÀB¼¡lo&©­zª~bµÝxß_c#%dcõ‚0—ž”æÐÇ­ê£ÜØ“Ûß'\šb1άšëŸã‰x3á™Ú rÄÍÚ¦#z5V‹ÔåMׂ«âB{—&Ì€¬=CÌõy"(*~°­´qDN~c‡J±ž^r¡Z4ÝT¾fð\ iÜ% n-irö ¤ðmΈy»ü†MÂiËÔÛ]EèŠË|+A•Ðܬì@6ù-õ#FˆÝÀ½ÞÒó R:Ì©›aj‹"¼c±”MN(öÙB}^¥;úÐ*[#—艜¸{wk²z!⣄=+§Ð7cAŒèŠæ”—Ý:íÑ%ƃГVæ¨õÇ­þ ½ámì¥ûi!Ï¥ŠæK9¤ ©ñ^{7ºfÞžûqý4L·\×à×»×€²K…txV®ëlÀ§@ØÓÞšœa`€³ÖÑ9œ?~nËM÷rº«ˆTí˜\Ô>Ð+Æ™:"W)*uâˆcê”+»ãx#lK:„:ä¸ÏóU¹¡Ùù˜>ûØ`rOZ¡;óºmç@÷ÊB~µöH¡jÑÜsËuåÈÑ YüâƒèöçšJ¡í høùãÔ’‚&¤-Ó6rÒ¼ ‰­ar&k×#îËceûŸ#á0ý±þRô™nâ-5Ÿ‹z‰›…»zÒЉ×<¯«hÑHM¿¥âä„/ ':ÌÛØ¡cûíV„ ‘e3žOÝ%‹\ØVšÙŽ.À„ã…­ÊíðãvÉh‘jôRmJß“j%tº«|êÖÂnOÿà¶ý7n¿¸íÊ{"ævkÂÈКõýìu㱫ÒÌ<š¹Ò"ü[‰4‘B¬ J?·ò‚ùú»I~Švò¡öÅ=S˜Z÷¢å''ü–b[@AE> Ê¿îPÜþ÷äƒx÷ ܈V°udò:ëÔ:xª)pchš)|ÊDeaOìŸKß"v¶ê›Ùvû1ad2U’*'ŠèöêêzHµ0í/¿{$«6¾2YŒ¦vuÙYr§#«êᣥHÝ\¶÷.g:vP5¸Hb„K;?UžCÙ!욎_ÖP5Ç b¾%k©â)V4Jž•ëzè¾´›©µkr†ù›¨Íò<\¢›©‰Ú¦Vð«¿û·\ýج¿uÄ]½Š¬¿Œ¾‚4cѬ¿Î’Z(™¬¿Pþî5&¬¿î }°Œ ­¿ù*8¬¿¿€^¸sa¬¿¬Žé ¬¿u‘BYøúª¿ƒÜE˜¢\ª¿ë©ÕWWª¿ôÞ€«¿ëR#ô3õª¿¤q¨ß…­©¿ûu§;O<§¿mãOT6¬©¿¿€^¸sa¬¿êÐéy7°¿;nøÝtË®¿²×»?Þ«®¿ Y2Çò®¿š´©ºG6¯¿V¹Pù×òª¿$—ÿ~ûª¿ˆñšWuV«¿N 4Ÿs·«¿ÄAB”/h©¿ËLiý-¨¿ý0Bx´q¬¿Z ¦}«¿€cÏžËÔ¬¿A¼®_°®¿|𓙀§¿Ì^¶¶F¤¿UDÝ ¥¿8„*5{ ¥¿!å'Õ>§¿t ‡Þâ᥿ÒâŒaNЦ¿iêwak¦¿F–̱¼«¦¿±KXc§¿¡/½ý¹h¨¿<ùôØ–§¿hé ¶O¦¿ÿA€ ¥¿Þ‘±Úü¿¢¿ˆôÛ×s¦¿ãR•¶¸Æ§¿D¨R³Z©¿BëáËD©¿ÊÂ×׺Ԩ¿A~6rÝ”ª¿¯°à~À£¿Gÿ˵hª¿I‚pª¿˜Q,·´ª¿ßj¸¯¨¿—üSªD©¿p|í™%ª¿ 4ØÔ©¿d¬6ÿ¯:ª¿ÀÎM›qª¿Ð}9³]©¿´TÞŽpª¿N³@»CŠ©¿Jï_{f©¿ŠUƒ0·{©¿÷@¨¿,H3Mg§¿IºfòÍ6§¿$0ðÜ{¨¿®Ÿþ³æÇ§¿£“¥Öû¦¿²Õ唀˜¤¿æèñ{›¦¿Ház®G©¿73úÑpʬ¿Âûª\¨ü«¿Åv÷Ý—«¿ßPøl¬¿0o»¬¿rˆ¸9• ¨¿ˆ,ÒÄ;¨¿Ãð1%’¨¿(ó¾IÓ¨¿ˆØÒ£©¦¿Ç.Q½5°¥¿‘œLÜ*ˆ©¿g›Ó–¨¿¯xꑦ¿Št?§ §¿­ü2#¥¿„ÒBΣ¿ê‘·µ…§¿õKÄ[çߦ¿(`;±O¨¿Ð}9³]©¿¨SÝ£¿Z¼X"§Ÿ¿¢'eRC ¿¾Ý’°«¡¿¤ö{b¢¿Å­j¡¿”Kã^I¢¿ì±¾¡¿Šº}å¡¿ÚX‰yVÒ¢¿O#-•·#¤¿|E·^Óƒ¢¿h”.ýKR¡¿%”¾rÞŸ¿9*7QKs›¿²ó66;¢¿±n¼;2V£¿´;¤ Ѥ¿¨8¼Z­ÞávhX¤¿æ‘?xwIœQ¿:W”‚U¥¿[•DöA–¥¿L4HÁSÈ¥¿€›6ã4¤¿iàG5ì÷¤¿›«æ9"ߥ¿a©.àe†¥¿Vïp;4,¦¿¦ ÐÒ¥¿®×ô  ¥¿†ŽTâ:¦¿T8‚TŠ¥¿ŽW zR&¥¿Ö70¹Qd¥¿ÄìeÛik¤¿‰$zÅr£¿õŸ5?þÒ¢¿ºW•}W¤¿‹3†9A›¤¿Ù@ºØ´¢¿‡¦ìôƒº ¿Àìž<,Ô¢¿*ÖT…¥¿|·y㤨¿8ýÚúé§¿ 7U†q§¿zR&5´¨¿³\6:秨¿Õæ!S>¤¿iý-ø§¤¿/iŒÖQÕ¤¿Ù²|]†ÿ¤¿¯þ·’£¿†¯¯u©¢¿,)wŸã£¥¿¤ÿåZ´¥¿ŸåypwÖ¦¿_b,Ó/§¿ê%Æ2ý¡¿ÿ²{ò°P›¿czÂ(›¿Mh’XRîž¿´ü6Ä ¿75Ð|ÎÝž¿BÌ%UÛM ¿‹jQLž¿g}Ê1YÜŸ¿)&o€™ï ¿úE ú =¢¿à€J• ¿Þ Šcž¿Aœ‡˜N›¿«?Â0`É•¿bLú{)< ¿®¶bÙ=¡¿=ºq¢¿K®bñ›Â¢¿tµûËî¡¿ÓjHÜc飿 yçP†ª˜¿qXøQ £¿ADjÚÅ4£¿óWÈ\T£¿GÌìóå¡¿Zg|_\ª¢¿¤aQ§£¿¹8*7QK£¿Ržy9ì¾£¿¾L!u£¿?S¯[Æ¢¿3ùf›Ó£¿ç6á^™·¢¿O"¿£¿ð¾**ÿ¢¿q¯Ì[u¢¿¸æŽþ—k¡¿¢'eRC ¿ š²Ó¢¿yËÕM¢¿µ‡½PÀv ¿È&ù¿b¿då—Á‘ ¿ï9°!£¿oFÍWÉǦ¿[•DöA–¥¿ª€{ž?m¤¿OêËÒNÍ¥¿Ž®ÒÝu¦¿£ÉÅXÇ¡¿/ùŸüÝ;¢¿Ô¹¢”¬¢¿è£Œ¸4¢¿N`:­Û ¿. ø1æž¿³A&9 £¿?Ȳ`⢿aÅ©ÖÂ,¤¿mæÔBɤ¿Ä QºôŸ¿GJ±£q˜¿æ;ø‰è—¿Õ[[%Xœ¿|`Ç  ¿Bx´qÄZœ¿©÷TN{Jž¿žACÿ›¿>"¦D¿bK¦z2Ÿ¿ý-ø§T¡¿VðÛã5Ÿ¿¾/.Ui‹›¿ƒÚoíDI˜¿5Ó½NêË’¿x%És}ž¿ñFæ‘? ¿ÈzjõÕU¡¿òµg–¨¡¿ÈÑYùe ¿$—ÿ~û¢¿»DõÖÀ–¿ñd73ú¡¿ µ‰“û¢¿¸\ýØ$?¢¿RóUò±» ¿úÏš¡¿vþí²_w¢¿+5{ ¢¿ ûrf»¢¿³{ò°Pk¢¿9€~ß¿y¡¿å(@̘¢¿}¢|¡¿þ¸ýòÉŠ¡¿jݵßÚ¡¿ƒmÄ“ÝÌ ¿O±jævŸ¿Òá!ŒŸÆ¿ç©¹n ¿˜3Ûú`¡¿Ž¿·éÏž¿Ð³Yõ¹Úš¿MK¬ŒFž¿dyW=`¢¿†åÏ·K¥¿È¨p©¤¿ÏÏÙB£¿øO7Pि\='½o|¥¿hvÝ[‘˜ ¿þÕã¾Õ:¡¿ ¥+ØF<¡¿mXSYv¡¿ûÍÄt!VŸ¿†Ç~K‘œ¿?ªa¿'Ö¡¿Swe ®¡¿'Ø›6£¿l•`q8󣿅ÏÖÁÁž¿Þ Z+Ú—¿j‰•ÑÈç•¿+J Áªš¿„*5{  ¿R€(˜1›¿ÙwEð¿•œ¿<ÁþëÜ´™¿,¾-Xª›¿¬Ê¾+‚ÿ¿× /½ý¹ ¿ØºÔýL¿5µl­/š¿Ð™´©ºG–¿ºóÄs¶€¿B[Î¥¸ªœ¿4Lm©ƒ¼ž¿ù£¨3÷ ¿ñd73úÑ ¿I„+ PŸ¿q¯Ì[u¢¿÷WûV딿"ÝAìL¡¿6ŽX‹O¡¿´­fñ}¡¿Øñ_  ¿8Ùî ¿Ù|\*Æ¡¿ÛkAï¡¿t 34ž¢¿æ²Ñ9?Å¡¿F#ŸW<õ ¿ÀÎM›q¢¿-ê“Üa¡¿(›r…w¡¿¡º¹øÛž ¿ˆFw;S ¿ßRÎ{/ž¿¶*‰ìƒœ¿ãÝ‘±ÚüŸ¿™*•Ô  ¿äø¡Òˆ™¿úÏš™¿•žé%Æ2¿’±Úü¿ê ¿’’¦¿Ù=yX¨5¥¿á±ŸÅR¤¿Ýξò =¥¿¡JÍh¦¿“o¶¹1=¡¿°ÿ:7mÆ¡¿aà¹÷pÉ¡¿w|ÓôÙ¡¿l|&ûçi ¿Oqž¿¸çùÓFu¢¿%És}¢¿€'-\Va£¿"¨½ ¤¿9ïÿã„¡¿d’‘³°§¿xòé±-ž¿ïU+~©Ÿ¿—ÒþX£¿SäG ¿ßýñ^µ2¡¿%Ïõ}8H ¿Á¨SÝ ¿Æ2ýñÖ¡¿í¸áwÓ-£¿*ß3¡¡¿ 毹2 ¿-´sšÚ¿H‰]ÛÛ-™¿£ x|{× ¿¶;P§<¢¿ùNÌz1”£¿³Z!«£¿|(Ñ’ÇÓ¢¿ô¥·?¥¿Œ÷ãöË'›¿oÖà}U.¤¿‘ao¤¿Áæ<š¤¿œ3¢´7ø¢¿¸ [–¯£¿*kg{¤¿Ÿp]1#¤¿žâ<œÀ¤¿±Ÿ`¤¿Â5wô¿\£¿¸éÏ~¤ˆ¤¿ ¾iú쀣¿{NzßøÚ£¿¡½úx軣¿ì…¶ƒ£¿(º.üà|¢¿«“3w¼¡¿Øî û¢¿Ô×ó5Ëe£¿-å}Í¡¿,g~5 ¿kšwœ¢#¡¿Â†§WÊ2¤¿QJVÕ˧¿Qù×òÊõ¦¿¢©ÛÙW¦¿¢&ú|”§¿ýfbº«§¿âÊÙ;£­¢¿333333£¿§>¼s(£¿ÎˆÒÞà £¿‹ˆbò˜¡¿ª, »(z ¿Æ4Ó½Nꣿ_|Ñ/¤£¿{‚Äv÷¥¿Ï ¡‚‹¥¿Jð†4*p¢¿ÁÅŠLà¿çR\Uö]¡¿ªÕWWj¡¿NA~6rݤ¿Útp³x¡¿rÞÿÇ £¿aþ ™+ƒ¢¿g(îx“ߢ¿c ¹§«£¿v‹ÀXßÀ¤¿Õ”dŽ®¢¿C¨R³¢¿”™€_#¡¿ÑA—pè-ž¿õÙ×3¢¿jjÙZ_$¤¿¦^·Œ¥¿ëTùž‘¥¿îZB>èÙ¤¿ØžY ¦¦¿«Íÿ«ž¿C8Ù¦¿ ]Þ¦¿R¶HÚ>¦¿'l?ãä¿Év¾Ÿ/¥¿°«ÉSVÓ¥¿û&7Ь¥¿D¥3û<¦¿ ¦–­õ¥¿˜iûWVš¤¿æCV¸¥¿·Aí·v¢¤¿×L¾ÙæÆ¤¿†!YÀ¦¿ã¦šÏ¹£¿må%ÿ“¿£¿Ò¥I*£¿^J]2Ž‘¤¿0œk˜¡ñ¤¿€ôMšE£¿Ú‘ê;¿(¡¿!èhUK:¢¿Ý”òZ Ý¥¿Q†ª˜J?©¿OqN¨¿Ÿ>øù§¿Xÿç0_^¨¿ ¦šYK©¿l®šçˆ¤¿pìÙs™š¤¿ìjò”Õ¤¿R)v4õ£¿LÃð1%¢¿ƒ3øûÅl¡¿Ù[Êùb勵EœN²Õ夿Tÿ ’!Ǧ¿z©Ø˜×§¿ÙìHõ_¤¿ýi£:È¢¿ñ·=Ab»£¿aŠriü¢¿8-xÑW¦¿¸Ku/£¿î\éE¥¿ö$°9Ϥ¿ -ëþ±¥¿eÅpuÄ¥¿ÿè›4 Ц¿ ¦}s¥¿!u;ûʃ¤¿$*T7£¿>ÏŸ6ªÓ¡¿8en¾Ý£¿?ªa¿'¦¿·D.8ƒ¿§¿ 'LÍʦ¿ó«9@0G§¿;´TÞŽ¨¿imÛk¡¿ƒOsò"¨¿'0Öm¨¿6>“ýó4¨¿®)ÙYô¦¿¦·? §¿æXÞU˜§¿odùƒ§¿‡ˆ›Sɨ¿Ö ˜£Ç§¿~įXÃE¦¿sJ_9§¿h‘í|?5¦¿Ä@×¾€¦¿ãúw}欧¿ÚpXøQ¥¿QÛ†Q<¦¿{ø2Q„Ô¥¿L5³–Ò¦¿æ®ò¦¿v©ú™z¥¿÷‘[“n£¿4„c–= ¤¿ (ÔÓGà§¿„-vû¬2«¿÷ç¢!ãQª¿­„î’8+ª¿X¾Û¼qª¿Étèô¼«¿Ãžvøk²¦¿ŽË¸©¦¿ßp¹5馿EÖJí¥¿{¡€í`¤¿IƒÛÚÂó¢¿Ë2g§¿ô噗æ¿]þCúí먿 Qºô/©¿1 ò¦¿¯D ú‘¤¿¬„¹ÝË¥¿‹‹£rµ¤¿|¸ä¸S:¨¿Ø›6㤿ÜõÒN§¿œ‡˜N릿F^Öħ¿)uÉ8F²§¿Èyÿ'L¨¿•ð„^§¿w…>XƆ¦¿d:tzÞ¥¿NðMÓg¤¿F—7‡k¥¿„*5{ ¨¿É­I·%r©¿ùK‹ú$w¨¿6?þÒ¢>©¿_@/ܹ0ª¿eU„›Œ*£¿¼vié¿ã7…• *ª¿£!ãQ*á©¿¼± 0(Ó¨¿r¦ ÛOƨ¿*äJ= B©¿ï9°©¿G²t±©¿žz¤Ámm©¿õôøÃϧ¿&Î5ÌШ¿"Þ:ÿvÙ§¿0™ò!¨¿ "RÓ.¦©¿³í´5"§¿‹6ǹM¸§¿‚9züÞ¦§¿¼Yƒ÷U¹¨¿kÔC4ºƒ¨¿Êû8š#+§¿øŠn½¦¥¿=›UŸ«¥¿é)rˆ¸©¿‚Uõò;­¿zlË€³”¬¿# Â¤R¬¿Ó¾¹¿z¬¿üÖMõ¬¿¥hå^`V¨¿BA)Z¹¨¿M/1–é—¨¿­lò–«§¿ˆÖŠ6ǹ¥¿¤á”¹ùF¤¿’”ô0´:©¿´Éá“N$¨¿#›ª¿âÊÙ;£­ª¿‰îY×h9¨¿}?qý¦¿ô‡fž\S¨¿7íµ ÷¦¿á þ~1[ª¿@½5_%§¿–Tÿ ’©¿7À[ A©¿ý-ø§T©¿î<0€ð©¿ raŠª¿¡0(Óhr©¿sÙ蜟⨿³–Òþ¨¿Ï¾ò =E¦¿ÌDR·³§¿{Øœƒgª¿ùÙÈuSÊ«¿­C9Ñ®ª¿?É6‘™«¿1±ù¸6T¬¿&Šº}¥¿5·BX%¬¿ÄìeÛik¬¿gd»S¬¿CB’Y½«¿½qR˜÷8«¿î•y«®«¿Œ½_´Ç«¿Mø¥~ÞT¬¿Û¿²Ò¤¬¿rm¨çoª¿ùNÌz1”«¿r‹ßVª¿ÿwD…ª¿â|~!¬¿—üSªD©¿/ùŸüÝ;ª¿ è…;Fª¿•ô0´:9«¿}гYõ¹ª¿ö–r¾Ø{©¿ ’>­¢§¿Màô.Þ§¿r4GV~¬¿k¶ò’ÿɯ¿CƒfÚ®¿€µjׄ´®¿Iô2Šå®¿z4Õ“ùG¯¿ŒdP3¤ª¿&¬±^ª¿ÝéÎÏÙª¿ë©ÕWWª¿Ñ;pÏó§¿Çb›T4Ö¦¿þEИIÔ«¿Å’r÷9>ª¿¤§È!âæ¬¿Úå[Ö­¿_Ñ­×ô ¨¿l^ÕY-°§¿(ó¾IÓ¨¿Œi¦{Ô§¿î%Ñ:ªª¿ä-W?6ɧ¿ãßg\8ª¿H§®|–ç©¿:–wÕæ©¿)í ¾0™ª¿>³$@M-«¿“V-ª¿¢–æV«©¿EeÚʢ¨¿‘·\ýØ$§¿Š!9™¸U¨¿¨SÝ«¿—6–~¬¿EÕ¯t><«¿›äGüŠ5¬¿Ë,B±4­¿õ…óþ?¦¿b¾¼û謿Ž ­¿9™¸U­¿C —8ò@¬¿Œ½_´Ç«¿ŠÍǵ¡b¬¿¶Û.4×i¬¿ÂLÛ¿²Ò¬¿“ýó4`¬¿Vn2ª «¿vü¬¿qXøQ «¿²¡›ýr«¿×ÁÁÞĬ¿yy:W”ª¿3Pÿ>«¿Ò¥I*«¿ì.PR`¬¿¥0ïq¦ «¿¤TÂzý©¿^žÎ¥„¨¿[#‚qp騿”0Óö¯¬¬¿T7Û°¿B˜Û½Ü'¯¿ªðgx³¯¿:“6U¯¿Óiݵ¯¿ ø5’ᪿGW#»ª¿–Zï7Úq«¿ raŠª¿…–uÿX¨¿»òYžw§¿!ê>©M¬¿Àϸp $«¿D¤¦]L3­¿ËòuþÓ­¿ÃÔ–:Èë©¿#ºg]£å¨¿LàÖÝ<Õ©¿mrø¤©¿¤aQ§«¿½äòw憎F}’;l"«¿ÖR@Úÿ«¿d"¥Ù<«¿uWvÁàš«¿Ÿu–=¬¿ê²˜Ø|\«¿fõ·Cê¿fh<Ä©¿T;ÃÔ–:¨¿ò]J]2Ž©¿°Œ Ý쬿wÜð»é–­¿Õ³ ”÷q¬¿ð£ö{b­¿—ª´Å5®¿â”¹ùFt§¿l—6–®¿ûÌYŸrL®¿@‰Ï`ÿ­¿ßÜ_=î[­¿êy7­¿‡¯yUg­¿nߣþz…­¿EõÖÀV ®¿'µ¿³­¿õÛ×sF¬¿!YÀn­¿Ð—Þþ\4¬¿}—R—Œc¬¿¯xê‘·­¿Ç¡~¶f«¿– # Â¬¿– # Â¬¿B–­¿åïÞQcB¬¿/o×j«¿¤6qr¿C©¿» )?©ö©¿#„GG¬­¿ç4 ´;¤°¿¾¾Ö¥F°¿†Yhç4 °¿—ÿ~û:°¿Ã¤Rìh°¿—6–~¬¿Âûª\¨ü«¿­iÞqŠŽ¬¿$ð‡Ÿÿ¬¿¿›nÙ!þ©¿›®'º.ü¨¿¼#cµù­¿—«›äG¬¿‘ñ(•ð„®¿ùÜ ö_箿:]›«¿HÞ9”¡ª¿2“¨|š«¿¸"1A ߪ¿LÆ1’=B­¿ò^µ2á—ª¿ 34ž⬿¯w¼W­¬¿Áüýb¶¬¿"Æk^ÕY­¿C=·Ð­¿UkaÚ9­¿ª€{ž?m¬¿ÂhV¶y«¿›ÿW9Ò©¿Ô×ó5Ëe«¿€d:tzÞ­¿s×òA¯¿ªÒ×øL®¿*Wx—‹ø®¿ ³³è °¿j¿µ%!©¿.9î”Ö¯¿Ëø÷°¿¯ÒÝu6䯿PãÞü†‰®¿¹Æg²ž®¿Qù×òÊõ®¿œ‡˜N뮿øo^œøj¯¿=,Ԛ毿nj ùœ»­¿¿eN—ÅÄ®¿‰±L¿D¼­¿¥½Á®¿ 7U†q¯¿9}=_³¬¿ãm¥×fc­¿ªæsîv­¿‚Uõò;M®¿U1•~ÂÙ­¿À>:u峬¿*«éz¢ëª¿x úÒÛŸ«¿™Êø÷¯¿á´àE_±¿X’<×÷á°¿a3ÀÙ²°¿]RµÝß°¿QO?ü°¿3MØ~2®¿P«”žé­¿iêwak®¿á%8õä­¿“ÅýG¦«¿ºõš”ª¿ E¹‡„¯¿ùf›Ó®¿ŸÉþy0°¿þ,–"ùJ°¿R)v4õ«¿ˆ×õ v«¿Q1Îß„B¬¿¶½Ý’°«¿›«æ9"ß­¿Pà|zl«¿‹©ôÎn­¿ü¦°RAE­¿Úå[Ö­¿6Y£¢Ñ­¿–u®¿iþ˜Ö¦±­¿¸! _B­¿Î‰=´¬¿Y4 Žª¿\…zú¬¿‘ñ(•ð„®¿Màô.Þ¯¿¡K8ô¯¿n‰\p¯¿;‡ú]°¿(ôú“øÜ©¿ùÀŽÿA°¿ãl:¸Y°¿tîv½4E°¿ÜµÛ.4¯¿ÆÂ9}=¯¿ø§T‰²¯¿!x|{× ¯¿{-è½1°¿qÊÜ|#º¯¿áëk]j„®¿Ð%z‹‡¯¿'f½ʉ®¿{„ò>Ž®¿%<¡×ŸÄ¯¿WÑšyr­¿ŸwcAaP®¿þ*Àw›7®¿ì1‘Òl¯¿-ÒÄ;À“®¿Ì:“6­¿¨T‰²·”«¿ýØ$?âW¬¿}%»¶¯¿F—7‡k±¿ÓNÍåC±¿:豿»¹øÛž ±¿q‘{ººc±¿µ?QÙ°®¿Ä>#K®¿@gҦꮿ÷<Ú¨®¿`’Ês¬¿úDž$]«¿O?üü¯¿þHVñ®¿Ì EºŸS°¿ßºñîȰ¿Gªïü¢­¿paÝxwd¬¿Þ„€| ­¿ÅE¹‡¬¿Y |E·®¿‰šèóQF¬¿ÀÙ²|]®¿ù¾¸T¥-®¿®€B=}®¿†!rúz¾®¿=·Ð•T¯¿( ß÷o®¿¨PÝ\ü­¿Ë¡E¶óý¬¿>³$@M-«¿Y|^ñ¬¿=·Ð•T¯¿Þ¬Áûª\°¿=}þðó¯¿p±¢Ó0°¿Û‰’HÛ°¿Ð€z3j¾ª¿~©Ÿ7©°¿£té_’ʰ¿NŸt"Á°¿ãÝ‘±Úü¯¿K’çú>°¿ š]÷V°¿ôPÛ†Q°¿Š§wñ~°¿Ð›ŠT[°¿sFZ*o¯¿ñJ’çú>°¿ýÛe¿ît¯¿ø§T‰²¯¿”¥Öûv°¿´CV·z®¿è÷ý›¯¿u殿lê<*þ¯¿k+ö—Ý“¯¿Œ¢>+®¿W•}Wÿ«¿LnYk(­¿ìŠáíA°¿DÂ÷þí±¿[éµÙX‰±¿ñ*k›âq±¿_ÎlW胱¿¬„¹Ý˱¿»}V™)­¯¿øo^œøj¯¿,¹ŠÅ¯¿š´©ºG6¯¿Ç,{ج¿Õ³ ”÷q¬¿cÙ=yX°¿?¨Œ¯¿­Lø¥~Þ°¿të5=((±¿kdWZFê­¿ÕyTüß­¿²t±i­¿]5Ïù.­¿`=î[­¯¿s-Z€¶Õ¬¿q:É®¿Yà+ºõš®¿UOæ}“®¿ÁþëÜ´¯¿V¶yËÕ¯¿HüŠ5\䮿0c Ö8›®¿ô4`ôi­¿*É:]¥«¿|HøÞß ­¿¢DKO˯¿8ƒ¿_Ì–°¿%Ïõ}8H°¿ª€{ž?m°¿{Cr2±¿é¹-@«¿±‰Ì\àò°¿Æ¬q6±¿P§<º±¿R hE°¿Þ¬Áûª\°¿SvúA]¤°¿‚69|Ò‰°¿Õ–:ÈëÁ°¿4J—þ%©°¿,g~5°¿aTR' ‰°¿„~¦^·°¿K‘|%°¿¦~ÞT¤Â°¿W@¡ž>¯¿0GßÛô¯¿ñ»é–⯿_š"Àé]°¿ßmUÙ¯¿ûWV𔂮¿ù‚0º¬¿’®™|³Í­¿X”†…°¿BCÿ+²¿Þèc> б¿ß¿yq⫱¿½á>rkÒ±¿ñÿ²¿ürf»B°¿^fØ(믿€›6ã4°¿L8 ¥+°¿Ïƒ»³vÛ­¿ÏÚmšë¬¿ÁÅŠLð¿J·%rÁ°¿Ï„&‰%±¿‰&PÄ"†±¿ ²eùº ¯¿Ý'G¢`®¿Ÿ`<ƒ†®¿¹-@Ûj®¿Þu6䟰¿ÚæÆô„%®¿X‘ÑIد¿3ù¼â©¯¿æXÞU˜¯¿amŒð°¿ î<0€°¿AJ˜iû¯¿ïU+~©¯¿š–X|®¿Å_ѭ׬¿Mu€Ô®¿è„ÐA—p°¿!«[='±¿€^»´á°¿xñ~Ü~ù°¿K­÷í¸±¿jMóŽS¬¿§wñ~Ü~±¿Ù$?âW¬±¿ƒ¾ôö碱¿¢E¶óýÔ°¿2w-!ô°¿àسç25±¿V¸å#)±¿¬Xü¦°R±¿[²*ÂMF±¿¦&ÁÒ¨°¿*Œ-9(±¿™ðKý¼©°¿Ûe6Ȱ¿DP5z5@±¿lÌëˆC6°¿ß‡ƒ„(_°¿ølìM°¿ä‚3øûŰ¿‡-y°¿`[?ýgͯ¿ÖµÂô­¿8ÀÌwð¯¿CqÇ›ü±¿Y2Çò®²¿=¶eÀYJ²¿ñH¼<+²¿8öì¹LM²¿-z§îy²¿ŠXİØ°¿Îp>?Œ°¿@ÜիȰ¿ü8š#+¿°¿Z)r‰#¯¿9}=_³\®¿CÉäÔÎ0±¿ü8š#+¿°¿jÙZ_$´±¿Õz¿Ñ޲¿ Š·˜¯¿ö~£7ü®¿Ü M¯¿~âú}ÿ®¿Ì EºŸS°¿ªek}‘Ю¿ŸÉþy0°¿•ñï3.°¿=Õ!7à °¿“r÷9>Z°¿E½àÓœ¼°¿¾Mö#E°¿á+Ù±°¿²ºÕsÒû®¿®bñ›ÂJ­¿\Âõ(\¯¿O=Òà¶¶°¿"Æk^ÕY±¿³ïŠà+±¿Þp¹5±¿›sôø±¿8fÙ“À欿‰]ÛÛ-ɱ¿p$Ð`S籿Ú¦¶Ô±¿¶¡bœ¿ ±¿¸¯çŒ(±¿ö–r¾Ø{±¿Þ­,ÑYf±¿'½o|í™±¿ Ê4š\Œ±¿©ÐDØð°¿õ"rl±¿Y|^ñ°¿ R)v4±¿žÒÁú?‡±¿Mƒ¢y‹°¿äõ`R|°¿;‰ÿ"h°¿B>èÙ¬ú°¿•c²¸ÿȰ¿ ·_>Y1°¿!<Ú8b-®¿:“6U¯¿>”h±¿R||BvÞ²¿‚8'0²¿CUL¥Ÿp²¿Y4 ޲¿f¡Ó,в¿|*§=%ç°¿sËcÍȰ¿ÖT…]±¿„Ó‚}±¿ñcÌ]Kȯ¿âeS®ð®¿:[@h=|±¿²dŽå]õ°¿g Ü¶ï±¿ñôJY†8²¿Ì΢w*°¿¼°5[yɯ¿.®ñ™ìŸ¯¿»}V™)­¯¿A€ ;¨°¿sFZ*o¯¿ÒUº»Î†°¿I›ª{°¿jjÙZ_°¿à¾œ3¢°¿ÁãÛ»±¿.ÎR²œ°¿!ɬÞáv°¿¶Ov3£¯¿7R¶HÚ­¿scz°¿}]†ÿt±¿>w‚ý×¹±¿„fÚþ•±¿6"—ޱ¿¥Kÿ’T²¿åÓc[œ­¿B—pè-²¿rl=C8²¿cÑtv28²¿ìÕ[[±¿ÛûTˆ±¿‘fر¿ˆ‚S°Æ±¿²¹jž#ò±¿×¥Fèg걿 Qºô/I±¿ ~þ{ðÚ±¿ÀnÝÍS±¿¢ ê[æt±¿Ã(ßÞ±¿V ì1‘Ò°¿Ø›6ã°¿vãÝ‘±Ú°¿µ©ºG6W±¿UgµÀ±¿~r f°¿J ,€)¯¿ö^|Ñ/°¿NÒü1­±¿óWya³¿I+¾¡ðÙ²¿÷ʼUס²¿[Í:ãûⲿˆº@j³¿àسç25±¿Èïmú³±¿¢³Ì"[±¿Û£7ÜGn±¿gd»S°¿†r¢]…”¯¿.;Ä?l鱿á´àE_±¿¯]ÚpX²¿JÏôc™²¿ÿ>ã°¿Ž®ÒÝu6°¿ _B‡°¿{ó&°¿Gˆ,Ò°¿XÎüj°¿B]¡·°¿ ò³‘린¿ž)t^c—°¿˜Ü(²Ö°¿-î?2:±¿Xæ­ºÕ°¿£•{Y¡°¿ ýL½n°¿ÏIï_{®¿5–°6ÆN°¿±ÀWtë5±¿DÂ÷þí±¿%’èe˱¿ZEhæÉ±¿õhª'ó²¿Kä‚3øû­¿#ö  Y²¿–u²¿nˆñšWu²¿6<½R–±¿;mÆÁ±¿çp­ö°²¿»¶·[’²¿¬U»&¤5²¿’‘³°§²¿š™™™™™±¿òwï¨1!²¿ØI}YÚ©±¿s‚69|Ò±¿q>?Œ²¿{Cr2±¿DP5z5@±¿ Qºô/±¿ûZ—¡Ÿ±¿œæ=Î4±¿®HLP÷°¿^c@öz¯¿¯D ú‘°¿Ïƒ»³vÛ±¿ö@+0du³¿XÇñC¥³¿Ñæ8· ÷²¿ Tÿ ’!³¿Ø—qS³¿9Òy±¿tCSvúA±¿w0bŸŠ±¿n¾ݳ®±¿À_Ì–¬Š°¿¡ø1æ®%°¿[(™œÚ²¿³~31]ˆ±¿,Ÿåypw²¿L‰$zŲ¿ö&†ä°¿’zOå´§°¿´ް¿õfÔ|•|°¿¿ÑŽ~7±¿×i¤¥òv°¿2V›ÿW±¿µùÕ‘#±¿¶¡bœ¿ ±¿ùk¸È=±¿bÜ ¢µ¢±¿ÖXÂÚ;±¿*àžçO±¿7ünºe‡°¿²õ á˜e¯¿ÖqüPiİ¿QÁᩱ¿'‡O:‘`²¿8J^c@²¿´Ë·>¬7²¿—wJ³¿7íµ ÷®¿Ð³Yõ¹Ú²¿ÓÁú?‡ù²¿Wèƒel貿LkÓØ^ ²¿™Ø|\*²¿2æ®%䃲¿§ÌÍ7¢{²¿-¤ý°²¿¤ng_y²¿E¡eÝ?²¿Qj/¢í˜²¿¯³!ÿÌ ²¿l"3¸<²¿pêÉ;‡²¿PŽDÁŒ±¿áBÁ”±¿b0…Ì•±¿ ¦–­õ±¿Å °rh‘±¿Ý—3Ûú°¿H¾D°¿Žx²›ý°¿Ž°¨ˆÓI²¿Ï1 {½û³¿2çû’³¿$&¨á[X³¿¥ž¡¼³¿ Ifõ·³¿%êŸæä±¿÷­Ö‰Ë±¿3SZK²¿”÷q4GV²¿ž?mT§±¿'‰%åîs°¿ušž²¿pÐ^}<ô±¿ñf ÞW岿sôø½M³¿ŸrL÷±¿íµ ÷ư¿ßºñîȰ¿u¬Rz¦—°¿¦D½Œb±¿Ç-æç†¦°¿p² Ü:±¿M¡ó»D±¿áçSÇ*±¿h†¬n±¿„d¸±¿àc°âTk±¿¡,|}­K±¿Û…æ:´°¿í*¤ü¤Ú¯¿+0du«ç°¿9c˜´É±¿w…>XƆ²¿ô5Ëe£s²¿\ætYLl²¿TææÑ=³¿9~¨4bf¯¿ƒ§Z ³¿aÄ>#³¿7l[”Ù ³¿ñ ÙuoE²¿…>XƆn²¿CŒ×¼ª³²¿ï¬Ýv¡¹²¿&¤à)䲿€ ˆWβ¿d°âTka²¿. ø1沿&¬±^²¿±¤Ü}޲¿È ~b¼²¿{ø2Q„Ô±¿fh<ı¿øTN{Jα¿&¨á[X7²¿1[²*±¿dWZFê=±¿ï Ñ!p°¿²G¨RE±¿þµ¼r½m²¿ØaLú{)´¿ˆ„ïý Ú³¿‚ëßõ™³¿Êmûõ׳¿{-è½1´¿h^»ï²¿ò@d‘&Þ±¿¯³!ÿÌ ²¿p–’å$”²¿EhæÉ5±¿ˆ-=šêɰ¿ m9—ⲿQ/ø4'/²¿vŠUƒ0³¿7§’ Š³¿sóèžu±¿‹ÆÚßÙ±¿Èïmú³±¿{Oå´§ä°¿Y¾.ú±¿‚ÿ‚±¿À燣±¿wÐ}9³±¿é Œ¼¬‰±¿“ `ÊÀ±¿“å$”¾²¿Ü:åѱ¿DÝ µ±¿} ^±¿5–°6ÆN°¿zŠ"nN±¿ººc±M*²¿§¥h岿Êf/Û²¿Âö“1>̲¿êé#ð‡³¿9DÜœJ°¿4¡l\³¿HPüs³¿|îû¯s³¿»|ëÃz£²¿ù0{ÙvÚ²¿Ó hÀ"³¿¢&ú|”³¿^fØ(ë7³¿Üšt["³¿‹áꈻ²¿"rl=³¿ßlscz²¿Ô˜sIÕ²¿iQŸä³¿×ÜÑÿr-²¿Ä_“5ê!²¿š_Í‚9²¿1·{¹O޲¿@h=|™(²¿Pmp"úµ±¿âx>êͰ¿st´ª±¿‚oš>;ಿ Q¾ …´¿ÇAœ‡´¿Q¡º¹ø³¿¦ ÐÒ´¿%Ïõ}8H´¿÷?ÀZµk²¿~įXÃE²¿‡l ]l²¿/kb¯è²¿÷uàœ¥±¿­Û ö[;±¿¾ …Œ.³¿߉Y/†²¿~8gDi³¿K[\ã3Ù³¿°ÿ:7mƱ¿Õ\n0Ôa±¿½þ$>w‚±¿ßýñ^µ2±¿.ÆÀ:޲¿Z/†r¢]±¿Ÿä›È̱¿¬r¡ò¯å±¿Ýa™¹À±¿íØÄëú±¿êŸæäE²¿¾4»î±¿R›8¹ß±¿'†ädâV±¿«>W[±¿°¿.9(a¦±¿Sé'œÝZ²¿•ð„^³¿5¶×‚Þ³¿Œ£rµ4³¿dèØA%®³¿]Œu?°¿Nµf¡³¿~TÃ~O¬³¿pïô¥·³¿ö&†ädⲿºÙ(³¿sFZ*o³¿K?ªa³¿1 Xr‹³¿Q»_øn³¿,¹ŠÅo ³¿»¶·[’³¿÷±‚߆³¿MŸp]1³¿2Çž=³¿xã§qo²¿ÎZ_²¿KËH½§r²¿]¤P¾¾²¿B&9 {²¿¿ïß¼8ñ±¿6;R}籿ȔAÕ豿ƒú–9]³¿J}YÚ©¹´¿/÷ÉQ€(´¿«x#óÈ´¿.È–åë2´¿çýœ0a´¿b…[>’’²¿»}åAz²¿¿¹¿zÜ·²¿9ïÿã„ ³¿¤¤‡¡Õ±¿NA~6r±¿:¯±KT³¿¡GŒž[貿oïô¥³¿½ÅÃ{´¿eqÿ‘éб¿h”.ýKR±¿–\Åâ7…±¿·ÔA^&±¿®Ø_vO²¿™žwc±¿oõœô¾ñ±¿Ñ=ë-²¿Æ2ýñÖ±¿Éc벿ػ?Þ«V²¿1 ò²¿Çkñ)²¿Q½5°U‚±¿ÛÝt_ΰ¿Œ³—m§±¿Kw×Ù²¿¤4›Ça0³¿„-vû¬2³¿¥½Á&³¿Í#0ðܳ¿=dʇ j°¿{ö\¦&Á³¿¼°5[yɳ¿«• ¿Ôϳ¿ä0˜¿B沿K‘|%³¿Äëú»a³¿\Âõ(\³¿-{Øœƒ³¿äòÒo³¿rÞÿÇ ³¿ìlÈ?3ˆ³¿Tâ:Ƴ¿÷±‚߆³¿­‰¾¢[³¿TpxADj²¿ÞªëPMI²¿ßÛôg?²¿3Žç3 ²¿•*Qö–r²¿‡†Å¨kí±¿->Àx±¿–=Ô¶±¿S³Z!³¿ÔÔ²µ¾´¿Q1Îß„B´¿;þ 2´¿/ûu§;O´¿Q¼ÊÚ¦x´¿$ –\Ų¿pìÙs™²¿ðß¼8ñÕ²¿ÅçN°ÿ:³¿ósCSvú±¿¼#cµù±¿bÚ7÷W³¿å·èd©õ²¿`"Ä•³³¿œ¿ …8´¿[{C²¿ÌîÉÃB­±¿q« ºö±¿¾1Çž±¿6wô¿\‹²¿Óhr1Ö±¿0,¾-X²¿j÷«ßm²¿ÚÅ4Ó½N²¿î!á{ƒ²¿¼¯Ê…Ê¿²¿¬8ÕZ˜…²¿­—㈲¿ÙëÝﱿá³up°7±¿™,î?2²¿Ééëùšå²¿RF\¥³¿a2U0*©³¿ºöô³¿Bx´qÄZ´¿‰`\:æ°¿ÝÍSr3´¿tîv½4E´¿]Œu?´¿æuÄ!H³¿HnMº-‘³¿}XoÔ Ó³¿ñ[z4Õ³¿˜÷8Ó„í³¿rPÂLÛ³¿?qý¾³¿Ï1 {½û³¿Ð%z‹‡³¿Ú©¹Ü`¨³¿ó>ŽæÈʳ¿ZòxZ~ಿ¥]Pß²¿ÕËï4™ñ²¿s×òA³¿þðó߃ײ¿n0Ôa…[²¿¢·xxϱ¿Ô›QóU²¿Ž<Y¤‰³¿G 6µ¿'À°üù¶´¿W\•›´¿Ÿã£Åô¿ŸçOÕé´¿,D‡À‘@³¿ß£þz…³¿+½6+1³¿?T1³Ï³¿Ì¸©æs²¿.çR\Uö±¿Ð¸p $ ´¿Äëú»a³¿š]÷V$&´¿ŒºÖÞ§ª´¿d¬6ÿ¯:²¿ÎÅßö‰±¿¤¨3÷𱿠]Þ®±¿„¹ÝË}²¿~(F–̱¿0,¾-X²¿½§rÚSr²¿'/2¿F²¿Ö¸ÇÒ‡²¿€µjׄ´²¿ñ+Öp‘{²¿œÅ‹…!r²¿DÂ÷þí±¿RµÝß4±¿©lXSY²¿Éå?¤ß²¿¿¶~úÏš³¿»ÑÇ|@ ³¿ EºŸS³¿î@òèF´¿e#Ù#Ô°¿Z*oG8-´¿Pª}:3´¿?ãÂ,´¿2°Žã‡J³¿M.ÆÀ:޳¿&o€™ï೿„ÒBγ¿Ab»{€î³¿R)v4õ³¿V„aÀ’³¿ïb€D´¿¹ÇÒ‡.¨³¿ëpt•¿vŠUƒ0·³¿éH.ÿ!ý²¿`‘_?IJ¿ú =bôܲ¿'Ø›6³¿hÐÐ?ÁŲ¿ƒ0·{¹O²¿ZôPÛ†±¿¸;k·]h²¿.VÔ`†³¿!;oc³#µ¿ãûâR•¶´¿Ò4(š°´¿æ±fd»´¿©ÐDØð´¿ÇÒÁú?³¿Nïâý¸ý²¿Š‘%s,³¿ƒlY¾.ó¿ â<œÀt²¿78ýÚú±¿•™Òú[´¿qçÂH/j³¿9œùÕ ´¿Î’Z(™´¿7+1ÏJ²¿9ïÿㄱ¿xÒÂe²¿^.â;1뱿0c Ö8›²¿P«”žé±¿ønóÆIa²¿°ŒØ'€²¿·Œõ L²¿åÐ"Ûù~²¿*A*Ų¿à¼8ñÕŽ²¿áëk]j„²¿ƒMGÅÿ±¿ª}:3P±¿[[x^*6²¿å·èd©õ²¿:õÔꫳ¿ØŸÄçN°³¿iTàd¸³¿CSvúA]´¿ö&†ä°¿ ܺ›§:´¿2kœMG´¿ 4ØÔyT´¿Sê’qŒd³¿¸ [–¯³¿‡Ü 7à󳿣#¹ü‡ô³¿B•š=Ð ´¿vü´¿øúZ—¡³¿ï<ñœ- ´¿_(`;±³¿àœ¥½Á³¿sØ}ÇðØ³¿ót®(%³¿¤¥òv„Ó²¿R( __벿d­¡Ô^D³¿7á^™·ê²¿¸çùÓFu²¿ Q…?Û±¿Ãgëà`o²¿hÍ¿´¨³¿‡$š@µ¿ãOT6¬©´¿zlË€³”´¿_)ËǺ´¿ÆÝ Z+Ú´¿§–­õEB³¿Ü M³¿ë5=((E³¿d”g^»³¿Kw×Ù²¿§é³®+²¿¯±KTo ´¿®¼äòw³¿ŸÉþy0´¿A,›9$µ´¿à…­ÙÊK²¿5Cª(^e±¿®,ÑYf²¿$š@‹²¿Wj1x˜²¿DÂ÷þí±¿‘~û:p²¿¾ÚQœ£Ž²¿­4)Ý^²¿Üóüi£²¿3¤ŠâUÖ²¿Ô x'Ÿ²¿øùïÁk—²¿ÛikD0²¿&«"ÜdT±¿p[[x^*²¿zýI|ÍÓÚ4¶³¿-Ó¾¹³¿Tüߪ³¿ô߃×.m´¿ýJçó±¿gd»S´¿;‡ú]´¿ –ê^f´¿RÑèb³¿c ¹§«³¿›­¼äò³¿÷³B‘øù³¿7ÿ¯:r¤³¿Ã.Šø´¿ïU+~©³¿žwgí¶³¿i¬ýíѳ¿ ®¹£ÿ岿þðó߃ײ¿Å5| 벿$G:#/³¿@ÚÿkÕ²¿6‘™ \²¿THÞ9”±¿ˆœ¾ž¯Y²¿Š¯v稳¿;Sè¼Æ.µ¿¨4bfŸÇ´¿ JÑʽ´¿Ñ]gEÔ´¿5s»—û´¿( __ëR³¿÷±‚߆³¿’ê;¿(A³¿ö'ñ¹쳿²ºÕs²¿7Û$²¿÷_˜L´¿‘šv1Ít³¿$œ¼è+´¿Q0c Ö´¿9aÂhV²¿MIÖáè*±¿HS=™ô±¿%uš²¿ôáY‚Œ€²¿›ÿW9Ò±¿ûÌYŸrL²¿„»³vÛ…²¿[{C²¿)éahur²¿É9±‡ö±²¿jOÉ9±‡²¿TÈ•z„²¿*œÞÅû±¿m¨çoB±¿q>?Œ²¿¥,CëⲿìW\•³¿d´¿íñB:<´¿`tys¸V³¿qr¿CQ ³¿½ãÉ峿3Q„Ôí쳿ø‰è÷ý³¿Q¡º¹ø³¿>u¬Rz¦³¿š]÷V$&´¿•¶¸Æg²³¿+j0 ó¿LÞ3ßÁ³¿ó =E³¿”ص½Ý²¿-è½1³¿O­¾º*P³¿;nøÝt˲¿r‹ßV²¿A)Z¹˜±¿»}åAz²¿¬9@0G³¿/ø4'/2µ¿,€)´´¿å*¿)¬´¿äÖ¤Û¹´¿ö|ÍrÙè´¿LpêÉ;³¿F^Öij¿]߇ƒ„(³¿P¨§À³¿Á8¸tÌy²¿ ±ú# ²¿ÆàaÚ7÷³¿Õ²µ¾Hh³¿BA)Z¹´¿¼z´¿‡¾»•%²¿- &þ(ê°¿ì¥)œÞ±¿ªGÜÖ²¿e‹¤Ýèc²¿2Ïg@½±¿à- ø1²¿§t°þÏa²¿úíëÀ9#²¿÷Žb²¿’Z(™œ²¿ƒÜE˜¢\²¿÷Žb²¿“màÔ±¿¼@I0±¿Ñ‘\þCú±¿›T4Öþβ¿»òYžw³¿tÐ%z‹³¿~âú}³¿!>°ã¿@´¿Ù®Ð˰¿iTàd´¿êD2´¿·e¥I)´¿YúÐõ-³¿1`ÉU,~³¿ жšuƳ¿'c`dz¿­KÐÏÔ³¿´:9Cqdz¿b‚¾…u³¿ Ôbð0í³¿YÝê9é}³¿t|´8c˜³¿~TÃ~O¬³¿~Wÿ[ɲ¿ÌC¦|ª²¿Ó¾¹¿²¿×Ùf³¿—z6«²¿"¦D²¿§uÔ~k³¿~©Ÿ7µ¿èˆ|—R—´¿%•C‹´¿hÎú”c²´¿0½ý¹hÈ´¿S³Z!³¿py¬䲿Q²¿“å$”¾²¿/ùŸüÝ;²¿„¹ÝË}²¿‡nùHJ²¿õ¸oµN\²¿zqÈÒ±¿LnYk(±¿|+Ôð±¿1–é—ˆ·²¿Ã9}=_³¿CV·zNz³¿ö@+0du³¿1Ò‹Úý*´¿³´Ss¹Á°¿G¬Å§´¿¸ õô´¿ŸÉþy´¿à*O 쳿1´:9Cq³¿‚åȳ³¿Z¼X"§³¿N]ù,ϳ¿?T1³³¿˜5±ÀWt³¿¾jeÂ/õ³¿Ÿ«­Ø_v³¿»ÑÇ|@ ³¿o¼;2V›³¿‘|%»²¿…BB•²¿þe÷äa¡²¿×¦±½ô²¿6wô¿\‹²¿E¡eÝ?²¿ö#EdX±¿Z€¶Õ¬3²¿†ç¥bc^³¿ê!ÝAì´¿ñÿ‚´¿d²¸ÿÈt´¿‘Gp#e‹´¿J´´¿u=Ñu᳿î#·&ݲ¿§wñ~ܲ¿µŠþÐÌ“³¿òÏ â;²¿}ZEh汿gÑ;pϳ¿úšå²Ñ9³¿ïY×h9г¿ô‹ôz´¿]S ³³è±¿=)“Ú°¿¯Ì[uª±¿IŸVѲ¿ŸF6²¿ ïr߉±¿×QÕQ÷±¿„, &þ(²¿˜¡ñD籿î>ÇG‹3²¿"H›V²¿#k ¥ö"²¿ÊRëýF;²¿¤q¨ß…­±¿t È^ïþ°¿k :!tб¿aª™µ²¿ŽYö$°9³¿ ßû´W³¿Ÿ ±Ý=@³¿¢ÏGq´¿šD½àÓœ°¿^, ‘Ó׳¿Ï…‘^Ô ¯$y®ï³¿rk²¿pê”G7²¿W"PýƒH²¿ôÃáÑÆ±¿ -ëþ±±¿¿CQ O䱿}x– # ²¿ƒ…“4L³¿Ì †:¬p³¿:“6U³¿UÙY´¿'À°üù¶°¿Ï1 {½û³¿¿ñµg–´¿—t”ƒÙ´¿~âú}ÿ²¿-wf‚á\³¿ÓL÷:©³¿&8õ䳿©‡ht±³¿E¼uþí²³¿÷”œ{h³¿.å|±÷⳿aûÉf³¿*q㊋³¿ôÞ€³¿¥MÕ=²¹²¿ŸVÑšy²¿}$%= ­²¿J ,€)³¿‰'»™Ñ²¿ƒ¥º€—²¿£>+N±¿IeŠ9:²¿–{Y¡H³¿šÏ¹ÛõÒ´¿y²›ýh´¿ÚÇ ~b´¿JFΞv´¿Ð~¤ˆ «´¿h¯>úRI€&²¿ù€@gÒ²¿odùƒ³¿¯³!ÿÌ ²¿Ü:åѱ¿²„µ1v³¿A‹v³¿¿º*P‹Á³¿œ¿ …8´¿˜¥šË ²¿n¦B</¯¿fh<ı¿œQ}>²¿hé ¶O²¿(DÀ!T©±¿Ñ‘\þCú±¿ZÔ'¹Ã&²¿cšé^'õ±¿>^H‡‡0²¿\:æ±N•ﱿֵÂô±¿^‘š²¿ñ}q©J³¿ó¯å•ëm³¿$&¨á[X³¿N ^ô´¿ À%W±°¿4ØÔyTü³¿Û$¶»´¿"‹4ñ´¿ˆº@j³¿ü¤6qr³¿u["œÁ³¿‡¥Õ°³¿QJVÕ˳¿Zº‚mij¿W'g(îx³¿S°ÆÙt´¿m®šçˆ|³¿“üˆ_±†³¿±ù¸6TŒ³¿ÖµÂô½²¿]Tœˆ²¿ Hû`­²¿í-å|±÷²¿ºõš”²¿ÈDJ³y²¿ —8ò@d±¿+Àw›7N²¿Â‰è×ÖO³¿ô¥·? µ¿Ä˜ô÷Rx´¿ g·–Ép´¿7ünºe‡´¿…DÚÆŸ¨´¿I/j÷«³¿Pj’̲¿èO=ÒಿÛ5x_•³¿ñôJY†8²¿àö‰í;ÃÔ–:ȳ¿a\:æ<³¿Êmûõ׳¿™€_#I´¿…zúü±¿iêwak®¿=˜Ÿ±¿­QÑ貿dyW=`²¿&Šº}±¿t ‡ÞⱿV¼‘y䲿É<òϱ¿®Gáz²¿dXÅ™G²¿dÍÈ w²¿Ì)1 ²¿¨ªÐ@,›±¿…{eު밿U…bÙ̱¿} yçP†²¿„çÞÃ%³¿ IJ™CR³¿+½6+1³¿ØÖOÿYó³¿À_Ì–¬Š°¿DÀ!T©Ù³¿/À>:u峿R}ç%賿8h¯>ú²¿ËÚ¦x\T³¿Ó3½ÄX¦³¿B]¡³¿u¯“ú²´³¿Ôð-¬³¿fgÑ;p³¿Ü»}éí³¿Óƒ‚R´r³¿µŠþÐÌ“³¿ 퀵j³¿Å‰&PIJ¿Â¿3‰²¿og_yž²¿Qù×òÊõ²¿#N'Ùêr²¿g×½‰ ²¿2ZGUD±¿5 Šæ,²¿oÕu¨¦$³¿TýJçó´¿"ʼn&P´¿êu‘BY´¿aP¦Ñäb´¿ç4 ´;¤´¿øU¹Pùײ¿¿ 1^󪲿*§=%çIJ¿t$—ÿ~³¿ôR±1¯#²¿|ÓôÙ×±¿õHƒÛÚ³¿ƒú–9]³¿ жšuƳ¿@¢CàH´¿‹¦³“Á±¿Ü:åÑ­¿¥½Á&S±¿7R¶HÚ±¿?üü÷౿̳’V|C±¿åòw僧¿+Ù±ˆ×±¿î$"ü‹ ±¿©h¬ýí±¿ßú°Þ¨²¿)ϼvß±¿V¸å#)鱿÷>U…b±¿mâä~‡¢°¿‰ÒÞà “±¿~įXÃE²¿7á^™·ê²¿Ð#„G³¿Æßö‰í²¿J(}!ä¼³¿œ'¾ÚQ°¿b†ÆAœ³¿mÆÁ¥³¿Vס𒬳¿È²`⢲¿ ²Hﳿ IJ™CR³¿n…°KX³¿iÅ7>[³¿æ!S>U³¿ŒKUÚ⳿hur†â޳¿D¢Ð²î³¿kœMG7³¿!‘¶ñ'*³¿«ö˜Hi²¿°æÁ=²¿f3‡¤J²¿?tA}Ëœ²¿c%æYI+²¿¥øø„ì¼±¿åa¡Ö4ï°¿t ‡Þâ᱿=|™(B겿W[±¿ìž´¿´qÄZ| ´¿Ð`SçQñ³¿k™ Çó´¿kIG9˜M´¿]Mž²š²¿sœÛ„{e²¿àªÔ첿v?T1³¿;%¯Î±¿ÏL0œk˜±¿Ìa÷Ãc³¿Va3ÀÙ²¿_EF$a³¿W•}Wÿ³¿Ïôc™~±¿²h:;­¿>"¦D±¿L©KÆ1’±¿þEИ±¿v½S÷°¿ãm¥×fc±¿-ÑYfб¿R û=±N±¿°t>>!³¿é™^b,Ó³¿ŽW zR&±¿ãOT6¬©¬¿î{Ô_¯°°¿çsîv½4±¿ ¸Ê;±¿Õ{L¤°¿‚ÿ‚±¿2*A*±¿ôMšEó°¿¿ÑŽ~7±¿mXSYv±¿þÕã¾Õ:±¿¶€Ðzø2±¿- ´¾°¿\Uö]ü¯¿®×gÎú°¿ "RÓ.¦±¿oض(³A²¿æ$”¾r²¿¤7ÜGnM²¿l˜¡ñD³¿÷”œ{h¯¿µ;າ¿ïOZ¸¬²¿Âö“1>̲¿ ȳ˷²¿šBç5v‰²¿¯°à~À³¿—Œc${„²¿(™œÚ¦²¿ ƒ2²¿Í!©…’ɱ¿Òª–t”ƒ±¿~RíÓñ˜±¿Q¤û9ù±¿ÆOãÞü†±¿31]ˆÕ±¿*Æù›P°¿¶,_—á?±¿¯ ?8Ÿ:²¿!¯“â㳿 퀵j³¿Ü¡a1êZ³¿„}³¿ÿA$CŽ­³¿…zúü±¿ðÞQcḆ¿Ëž6ç౿ œO«”²¿VF#ŸW<±¿±1¯#Ù°¿zrMÌβ¿&üR?o*²¿%“S;ÃÔ²¿XË™`8³¿æ±fd»°¿>´ü6¬¿›äGüŠ5°¿Q}>ʰ¿ßºñîȰ¿¼ëlÈ?3°¿g›Ó–°¿Gsdå—Á°¿ro~ÃDƒ°¿––‘zOå°¿†®D ú±¿B_zûsѰ¿l’ñ+Ö°¿Õ¯t>&RšÍã°¿ 3¦`±¿©0¶ä°¿$™Õ;ܱ¿+Qö–r¾°¿†8ÖÅm4°¿ÍwðЯ¿*Æ3h诿[µkBZc°¿ƒ£äÕ9°¿\Æú&¯¿'½o|í™­¿aûÉf¯¿q à-°¿&†§W²¿L4HÁSȱ¿ú`º±¿Ù¯;ÝyⱿÉ <÷²¿8øÂdª`°¿ c AJ°¿â‘xy:W°¿¾¿A{õñ°¿B{õñÐw¯¿CƒfÚ®¿.qäÈ"±¿±RAEÕ¯°¿»šzÃ}äÖ°¿Pþî5&°¿±Ý=@÷å°¿=˜Ÿ±¿~įXÃE®¿+ß3¡¬¿ý¾ó⬿ K< lÊ­¿Ä@×¾€®¿ ¸çùÓF­¿g+/ùŸü­¿—ª´Å5®¿%êŸæä­¿õ¸oµN\®¿×Ùf¯¿8¢{Ö5Z®¿^‚SH®¿CqÇ›ü­¿ßâá=–«¿?{ó®¿lË€³”,¯¿;R}ç%°¿@Â0`ÉU°¿¡ø1æ®%°¿w ùg±¿‹mRÑX«¿"3¸<Ö°¿„%Zò°¿Ùî@ò°¿–AµÁ‰è¯¿5B?S¯[°¿¯w¼W­°¿u¬Rz¦—°¿Ù'€bdɰ¿Û…æ:´°¿'0Öm°¿»µL†ãù°¿ g·–Ép°¿`áC‰–°¿€(˜1k°¿£<órØ}¯¿ÆÂ9}=¯¿3‡¤J&¯¿[ï7ÚqᅩÖQÕQ¯¿ãã²ó6®¿è»[Y¢³¬¿În-“áx®¿êÉ;‡2°¿™Gþ`౿Ÿâ8ðj±¿`r£ÈZC±¿“¦AÑ<€±¿Y¾.ú±¿0GßÛô¯¿aü4îͯ¿¤ª ¢î°¿4òyÅS°¿1 íœf®¿B´V´9έ¿›þìGŠÈ°¿{Ý"0Ö7°¿St$—ÿ°¿ çfh<±¿ŠÇEµˆ(®¿ÑʽÀ¬P¬¿RGÇÕÈ®¬¿¡„™¶e­¿F³²}È[®¿c`Ç­¿±¾É­¿.çR\Uö­¿û&7Ь­¿Z€¶Õ¬3®¿¥,Cë⮿dËòu®¿÷XúÐõ­¿š'×È쬿JA·—4F«¿æ‘?xî­¿ÚV³Îø®¿Úf°¿… £YÙ>°¿WA tí °¿Y|^ñ°¿ž™`8×0«¿`™D½°¿ep”¼:ǰ¿ 34žâ°¿2rö´Ã¯¿Ÿqá@°¿¸•^›•°¿ÿ>ã°¿ÿB=·°¿¡º¹øÛž°¿ –Í’Z°¿T÷<Ú°¿ š]÷V°¿Ìbbóqm°¿paÝxwd°¿K?ªa¯¿î#·&Ý®¿ù„ì¼Í®¿*¬ÿs˜¯¿h’XRî>¯¿¢ÕÉŠ;®¿paÝxwd¬¿¯>úîV®¿{1”í*°¿ØÔyTüß±¿ífF?N±¿QÚ|a2±¿8ù-:Yj±¿ÔdÆÛJ¯±¿%”¾rÞ¯¿1?74e§¯¿èº꯿D†U¼‘y°¿Ô‚}i®¿¸£®­¿´ã†ßM·°¿ ܺ›§:°¿K°8œùÕ°¿¡,|}­K±¿²¹jž#ò­¿¶+ôÁ26¬¿ m6 B¬¿€cÏžËÔ¬¿ÙÎ÷Sã­¿Ñ­×ô  ¬¿`vOj­¿\='½o|­¿Q29µ3L­¿j’Ìê­¿În-“áx®¿Ð+žz¤Á­¿Ù=yX¨­¿ çfh¬¿¶Go¸Üª¿™º+»`p­¿îyþ´Q®¿×÷á !ʯ¿-é(³ °¿œýrÛ¾¯¿€·@‚âǰ¿Pj’̪¿§Î£âÿް¿mâä~‡¢°¿r„Ѭ°¿â”¹ùFt¯¿ºÝË}r°¿¼"øßJv°¿;‡ú]°¿o¹ú±I~°¿Õ³ ”÷q°¿b k_@/°¿ò ú'¸°¿Ò¨ÀÉ6°¿´Éá“N°¿š †s 3°¿ Ü¶ïQ¯¿a5–°6Æ®¿£“¥Öû®¿áìÖ2ޝ¿[yÉÿä﮿ÙëÝï­¿‰ê­­¬¿œ6ã4D®¿3¥õ·௿„EEœN²±¿Ååx¢'±¿\Va3À±¿W!å'Õ>±¿§#€›Å‹±¿‚®}½p¯¿ÿíÕÇC¯¿Ë¹W•}¯¿‹¤Ýèc>°¿}‘Жs)®¿¿˜-Y­¿¥óáY‚Œ°¿µ§!ªð¯¿O‘CÄÍ©°¿» j¿µ±¿cÔµö>U­¿!¯“âã«¿ ñ+Öp‘«¿p–’å$¬¿p² Ü:­¿K:ÊÁl¬¿ÔFu:õ¬¿÷WûV묿ªc•Ò3½¬¿Ð˜IÔ >­¿ð6oœæ­¿UkaÚ9­¿÷XúЭ¿«í&ø¦é«¿Ïj=&Rª¿0ôˆÑs ­¿cò˜ù®¿úšå²Ñ9¯¿“¨|š“¯¿¸Y¼X"¯¿Æ‚”°¿E,bØaLª¿Š!9™¸U°¿ŒƒKÇœg°¿Òýœ‚ül°¿Ív…>XÆ®¿%±¤Ü}ޝ¿(Í9x&°¿,ºõš°¿Ýyâ9[@°¿Ÿp]1#°¿h%­ø†Â¯¿ç©¹n°¿"«[='½¯¿#ÁÆõ¯¿;äf¸Ÿ¯¿::ZÕ’®¿ Ö8›Ž®¿…(_ÐB®¿L5³–Ò®¿`ãúw}®¿»ðƒó©c­¿¨T‰²·”«¿M,ðÝz­¿$~Å.r¯¿XXp?౿âW¬á"÷°¿§/ú Ò°¿H›V ±¿‹¥H¾H±¿ ²Hﯿ¾÷7h¯®¿4/‡Ýw ¯¿ 6uÿ¯¿&â­óo—­¿¨Ã ·|$­¿£uT5A°¿¢ÎÜC¯¿Hk :!t°¿š'×Èì°¿ ß÷o^¬¿¬ª—ßi2«¿Ë¼Uסšª¿úcZ›Æöª¿p À?¥J¬¿”i4¹«¿ì.PR`¬¿pΈÒÞà«¿‡¢@ŸÈ«¿¡ÛK£u¬¿iàG5ì÷¬¿óùõC¬¿\…zú¬¿Ã}äÖ¤Ûª¿ønóÆI©¿@j'÷«¿“HÛø­¿™cyW=`®¿ \7¥¼®¿èOÕé@®¿§;OW[©¿†Ê¿–W®¯¿rý»>sÖ¯¿¯ÒÝu6䯿ºƒØ™Bç­¿÷™®¿Ž<Y¤‰¯¿­‰¾¢[¯¿ ’>­¢¯¿Ë¹W•}¯¿I×L¾Ù殿ˆc]ÜF°¿7íµ ÷®¿„};‰¯¿¿ðJ’çú®¿Ñ“2©¡­¿Ë¡E¶óý¬¿4-±2ù¬¿Cç5v‰ê­¿ž~P)”­¿ çfh¬¿j.7ê°ª¿*kg{¬¿áëk]j„®¿]þCúíë°¿QhY÷…°¿‚«<°S°¿—㈞°¿°VíšÖ°¿p[[x^*®¿KY†8ÖÅ­¿ú™zÝ"0®¿\Æú&¯¿c¶dU„›¬¿O@aÃÓ«¿ÍÓÚ4¶¯¿Lª¶›à›®¿‡ˆ›Sɰ¿šìŸ§ƒ°¿µŠþÐÌ“«¿§ ?¹nª¿‰]ÛÛ-É©¿Püsת¿xµÜ™ †«¿CV·zNª¿×Ý<Õ!7«¿ä»”ºd«¿p%;6ñª¿ºžèºðƒ«¿e‰Î2‹P¬¿Ò¬lò–«¿TÆÝ Z«¿‚jÛ0 ª¿ñ˜õb¨¿é'œÝZ&«¿2äØz†p¬¿×Â,´sš­¿Õ"¢˜¼®¿å`6†­¿B{õñÐw¯¿p@KW°¨¿-è½1¯¿ ²eùº ¯¿¦]Pß2¯¿þrÛ¾G­¿kdWZFê­¿ï¬Ýv¡¹®¿¾ÚQœ£Ž®¿¸Î¿]ö뮿¶hÚV³®¿h‘í|?5®¿¹Œ›h>¯¿ÒYùe0®¿/0+é~®¿×ˆ`\:®¿np–’嬿Q1Îß„B¬¿]4d«¿ïÇí—OV¬¿6ÊúÍĬ¿Œ 1“¨¬¿¢}¬à·!®¿” ¿Ð#F§¿‘{ººc±­¿sÖ§“Å­¿±†‹ÜÓÕ­¿amŒð¬¿Xæ­ºÕ¬¿´Yõ¹ÚŠ­¿Wya§X­¿sÖ§“Å­¿N_Ï×,—­¿÷XúЭ¿ø3¼Yƒ÷­¿ÿy0Hú¬¿ÔÑq5²+­¿FÏ-t%­¿RÑXû;Û«¿6\-«¿H¨REñª¿óuþÓ ¬¿»)嵺«¿kÖß—ª¿À’«Xü¦¨¿2tì ª¿ß,Õ¼¬¿ª¹nÀ¯¿äÜ&Ü+ó®¿€*nÜb~®¿dÎ3ö%¯¿Ò‹Úý*À¯¿æÊ ÚàD¬¿¦z2ÿè«¿Ðzø2Q¬¿tCSvúA­¿> Й´©ª¿tµûËî©¿ê—ˆ·Î¿­¿Š“ûŠ­¿6<½R–!®¿Ot ‡®¿ðŠà+Ù©¿Ús™šo¨¿xìg±ɧ¿ðÐïû§¿$î±ô¡ ª¿˜½l;m¨¿fÁÄE©¿šž^©¿‚:vP©¿›WuV ì©¿‰ÓI¶ºœª¿…]=ð©¿é¸Ù•–©¿Ê¤†6¨¿°qý»>s¦¿(›r…w©¿Zg|_\ªª¿JÔ >ÍÉ«¿+Ôð-¬¿‹Š8d««¿0bŸŠ‘­¿Ç/¼’书¿–!Žuq­¿þrÛ¾G­¿ß©€{ž?­¿á iTàd«¿eþÑ7i¬¿æ!S>­¿ð…ÉTÁ¨¬¿­/Úr.­¿ÁãÛ»­¿²Jé™^b¬¿~sõ¸o­¿‚Zº‚m¬¿ÆÝ Z+Ú¬¿©¥¹Âj¬¿‘cëÂ1«¿„gB“Ä’ª¿ûxè»[Yª¿Õ²µ¾Hh«¿eà€–®`«¿Ý—3Ûª¿B•š=¨¿èKo.ª¿å—Á‘(¬¿)êÌ=$|¯¿è‚ú–9]®¿ÞÇÑYù­¿$Õw~Q‚®¿h\W̯¿©‡ht±«¿0-ê“Üa«¿Í#0ðÜ«¿ìjò”Õ¬¿À&kÔC4ª¿c${„š©¿.É»š<­¿LÅÆ¼Ž8¬¿‡ú]Øš­¿•Ò3½ÄX®¿¯–;3Á¨¿¾÷7h§¿Ež$]3ù¦¿@½5_%§¿t—ÄY5©¿ý¾óâħ¿`¬o`r£¨¿BÌ%UÛM¨¿^ÒƒN¨¿ØƒIññ ©¿-å}Í©¿]þCúí먿LüQÔ™{¨¿¢~¶f+§¿qåìÑV¥¿†ðùa„¨¿C㉠Îé¿ãUÖ6Å㪿{0)>>!«¿oB@¾„ª¿§z2ÿ蛬¿‘Ó×ó5Ë¥¿É6‘™ ¬¿å—Á‘(¬¿ð¢¯ ÍX¬¿¸çùÓFuª¿TææÑ=«¿Ýšt["¬¿ÅôûþÍ«¿ ·_>Y1¬¿æ?¤ß¾¬¿Pà|zl«¿©Ø˜×‡¬¿(c|˜½l«¿ÐѪ–t”«¿· £ x|«¿¼ "5íbª¿ÄÎ:¯©¿šž^©¿á—úyS‘ª¿{Crª¿êX¥ôL/©¿:Yj½ßh§¿®ƒƒ½‰!©¿š`8×0C«¿ûÌYŸrL®¿ÃÕw­¿€»ì×ŸY ¦–­¿é*Ý]gC®¿¥MÕ=²¹ª¿¼ "5íbª¿þœ‚ül䪿 Ifõ·«¿ŠÊ†5•E©¿½Œb¹¥Õ¨¿jMóŽS¬¿O­¾º*P«¿[ìöYe¦¬¿f‚á\à ­¿Ž²~31]¨¿‚,`·¦¿aª™µ¦¿ì¦”×J覿jÜ›ß0Ѩ¿Ï¿]öëN§¿O›sðL¨¿Û$¶»¨¿Éq§t°þ§¿3ÃFY¿™¨¿rÄZ| €©¿˜Št?§¨¿Ù蜟â8¨¿;nøÝt˦¿`;±O¥¿]ˆÕa¨¿ðO©eo©¿sHj¡drª¿ýi£:Ȫ¿pê”G7ª¿ܵÛ.¬¿lÑ´­f¥¿(îx“ߢ«¿6WÍsD¾«¿å Åoò«¿t 34žª¿©ÞØ*Áª¿fk}‘Ж«¿œnÙ!þa«¿™óŒ}ÉÆ«¿ô¤‹¦«¿ut\쪿ˆìø/¬¿õ÷RxÐ쪿]›k«¿ýôŸ5?þª¿|ƒöêã©¿3á—úyS©¿©|š“©¿g×½‰ ª¿$î±ô¡ ª¿íÓñ˜Ê¨¿èÚÐ §¿íµ ÷ƨ¿ÚX‰yVÒª¿ä¾Õ:q9®¿Ë,B±4­¿IJzZ¬¿äˆž”I­¿˜M€aùó­¿èKo.ª¿­QÑ調‘í|?5^ª¿¨qo~ÃD«¿ò?ù»wÔ¨¿:ZÕ’Žr¨¿ßmÞ8)Ì«¿Æ‹…!rúª¿jÂö“1>¬¿Ÿ“Þ7¾ö¬¿±à~À¨¿ú™zÝ"0¦¿—ª´Å5¦¿ý½4»¦¿Ãc?‹¥¨¿Ež$]3ù¦¿ÀA{õñЧ¿…—àÔ’§¿H¾D„§¿1Ò‹Úý*¨¿(~Œ¹k ©¿:ÏØ—l<¨¿ð0í›û«§¿¢©ÛÙW¦¿@¥J”½¥¤¿‹Þ©€{ž§¿åa¡Ö4憎ùº ÿ骿+Àw›7Nª¿„ñÓ¸7¿©¿µmÁ«¿Ø×ºÔý¤¿ˆ¾»•%:«¿÷è ÷‘[«¿(c|˜½l«¿ú`º©¿šêÉü£oª¿„-vû¬2«¿t¶€Ðzøª¿÷‘[“n«¿ÿZ^¹Þ6«¿ÿwD…ª¿‚ëßõ™«¿Üƒ/¡ª¿%çÄÚǪ¿¥MÕ=²¹ª¿™fº×I}©¿jg˜ÚR©¿¯–;3Á¨¿™ñ¶Òk³©¿dæ—Çš©¿2kœMG¨¿µ5"—¦¿tîv½4E¨¿G’ \…ª¿û®þ·’­¿W³Îø¾¸¬¿Çò®zÀ<¬¿ Õ°ß묿Ϡ¡‚‹­¿…$³z‡Û©¿ì±¾©¿ÇeÜÔ@ó©¿Ã}äÖ¤Ûª¿R·³¯<¨¿ÓL÷:©§¿1`ÉU,~«¿0„œ÷ÿqª¿ß¿yqâ«¿}"O’®™¬¿‰yVÒŠo¨¿36t³?P¦¿^L3Ý뤦¿bhur†â¦¿:“6U÷¨¿¹Œ›h>§¿R·³¯<¨¿dw’ ¨¿FzQ»_¨¿Õ’Žr0›¨¿`ÊÀ-]©¿Úþ••&¥¨¿Æk^ÕY-¨¿Ã)só覿—Tm7Á7¥¿ËLiý-¨¿IZÖýc©¿•Ö߀ª¿n¿|²b¸ª¿´Ë·>¬7ª¿ÿ¬U»&¬¿ž´pY…¥¿x úÒÛŸ«¿DÀ!T©Ù«¿¯&OYM׫¿­„î’8+ª¿- PSËÖª¿åѰ¨ˆ«¿Ã9}=_«¿!WêYÊ«¿ðQ½Â‚«¿‚oš>;િä1•ñï«¿¶Go¸Üª¿ß§ªÐ@,«¿Æ‚”0«¿ú`º©¿N(DÀ!T©¿?üü÷¨¿Å:U¾g$ª¿¶ö>U…ª¿üd訿ÀË eý¦¿{K9_콨¿Nïâý¸ýª¿´wF[•D®¿gµÀ)­¿ä‚3øûŬ¿_›•˜g­¿®Gáz®¿A›>éDª¿78ýÚú©¿Â¿3‰ª¿Ž={.S«¿0½ý¹hȨ¿Î5ÌÐx"¨¿òšWuV ¬¿¦cÎ3ö%«¿^ Pj¬¿ŸÛ2à,­¿ÿÍ‹_í¨¿å|±÷⋦¿£±öw¶G§¿º¿zÜ·Z§¿eŽå]õ€©¿4MØ~2Ƨ¿0½ý¹hȨ¿!!Ê´¨¿p@KW°¨¿ø6ýÙ©¿§Z ³ÐΩ¿©J[\ã3©¿š#+¿ ƨ¿odùƒ§¿£uT5AÔ¥¿ùõCl¨¿ÃÔ–:Èë©¿Nïâý¸ýª¿Ø¸þ]Ÿ9«¿ÖÇCßÝʪ¿~ý,œ¬¿R+Lßk¦¿é(³ 0¬¿tšÚR¬¿1%’èe¬¿Ð(]ú—¤ª¿lwÐ}9«¿`:­Û ö«¿&¶Øí«¿. ´¾L¬¿OË\å ¬¿ÔÖüøK«¿ g·–Ép¬¿Aœ‡˜N«¿2;‹Þ©€«¿ó:â ¤«¿"Ã*ÞÈ<ª¿;q9^è©¿’Ï+žz¤©¿†uãÝ‘±ª¿x ý,–ª¿6rÝ”òZ©¿Ãï¦[vˆ§¿@¿ïß¼8©¿:]›«¿2>Ì^¶®¿ê?k~ü¥­¿Zd;ßO­¿H‰]ÛÛ­¿ž'ž³„®¿ZòxZ~િݵßÚ‰ª¿²fd»«¿_ÔîW¾«¿€šZ¶Ö©¿¯yUgµ¨¿%®c\qq¬¿©¤N@a«¿:A›>鬿]ݱØ&­¿>”h©¿_ š]÷¦¿aü4îͧ¿ÉæªyŽÈ§¿sÚSrNì©¿žî<ñœ-¨¿³z‡Û¡a©¿©J[\ã3©¿2®¸8*7©¿’Z(™œÚ©¿×gÎú”cª¿zÅrK«©¿ÛÄÉýE©¿é|x– #¨¿nˆñšWu¦¿mUÙ©¿8N ógª¿pënžê«¿ëâ6À«¿F6ŽX«¿c|˜½l;­¿ÃFY¿™˜¦¿«>W[±¿¬¿T­…Yh笿¬q6ܬ¿–Ïò<¸;«¿dsÕY1\­¿íÑV%‘­¿¦H¾H‰­¿õ  ­Ü«¿ºŸSŸ¬¿Úå[Ö­¿#¾³^ ­¿˜ßi2ãm­¿^‘šv1­¿hÊN?¨‹¬¿A)Z¹˜­¿Ûúé?k~¬¿©0¶ä ¬¿'ŸÛ2ିúDž$]«¿šž^)«¿$š@«¿ßøÚ3K¬¿sÖ§¬¿EGrù鯿Éå?¤ß®¿Á8¸tÌy®¿¼’ä¹¾¯¿^Mž²š®¯¿©¾ó‹¬¿µŠþÐÌ“«¿êÐéy7¬¿[D“7À¬¿Î7¢{Ö5ª¿ósCSvú©¿J&§v†©­¿a‹Ý>«Ì¬¿Èì,z§®¿'f½ʉ®¿×gÎú”cª¿Ö§“Åý§¿(Ö©ò=#©¿´ç25 Þ¨¿r3܀ϫ¿ ¥+ØF<©¿jûWVš”ª¿ˆ jôj€ª¿ eýfbª¿Õw~Q‚þª¿:vP‰«¿Ù°¦²(쪿ÁäF‘µ†ª¿mXSYv©¿Ö§“Åý§¿ÞË}r ª¿çß.ûu§«¿s-Z€¶Õ¬¿ÛÝt_ά¿þc!:ެ¿=¶eÀYJ®¿ÎR²œ„Ò§¿ £YÙ>ä­¿U¯²¶)®¿7Û$®¿Õ³ ”÷q¬¿'÷;ú¬¿àg\8’­¿+øDk­¿ÔïÂÖlå­¿µŒÔ{*§­¿‰`\:欿¤¨3÷ð­¿e#Ù#Ô¬¿Ø×ºÔý¬¿Í:ãûâR­¿í~à»Í«¿GÆjóÿª«¿€J•({«¿ý0Bx´q¬¿¦(—Æ/¬¿ÒyY«¿~oÓŸýH©¿%çÄÚǪ¿©öéxÌ@­¿6>“ýó4°¿Um7Á7M¯¿µ­¿®×ô  ­¿“HÛø­¿]ûzáέ¿ž±/Ùx°­¿Î’Z(™¬¿Ð€z3j¾ª¿÷9>Zœ1¬¿ãQ*á ½®¿!°rh‘í°¿³%«"Üd°¿J–“PúB°¿ª, »(z°¿¢™'×Ȱ¿ o –ꮿ•Zº‚­¿1 ò®¿Ùy›©®¿ W@ÜÕ«¿ ÐÒl«¿UPQõ+¯¿^‘š®¿á' ß÷¯¿È%Ž<Y°¿ÄìeÛik¬¿°ÅnŸUfª¿Ò¬lò–«¿EJ³y«¿gµÀ)­¿«ZÒQf«¿ö|ÍrÙ謿3úÑpÊܬ¿Û…æ:´¬¿Ž¯=³$@­¿ K< lÊ­¿—qS­¿'ŸÛ2ିŒ½_´Ç«¿Ó.¦™îuª¿Ðïû7/N¬¿©;‡ú­¿À#*T7¯¿u殿ÞÈ<ò¯¿²òË`ŒH°¿ö´Ã_“5ª¿þ|[°T°¿¬«µ<°¿ÎQÚ°¿’¯Rb×®¿QLÞ3¯¿¼°5[yɯ¿(š°È¯¯¿œà›¦Ï°¿0GßÛô¯¿çmlv¤ú®¿„~¦^·°¿È•z„ò®¿÷”œ{h¯¿`[?ýgͯ¿U1•~ÂÙ­¿)#.Ò­¿1E¹4~á­¿,*ât’­®¿æWs€`Ž®¿ñ~Ü~ùd­¿’!ÇÖ3„«¿nÁR]Àˬ¿y<-?p•¯¿3á—úyS±¿¸tÌyƾ°¿>v()°°¿Ÿ;Áþëܰ¿Ç›ü,±¿«˜J?á쮿÷Žb®¿. ø1殿H¾D„¯¿Fì@1²¬¿µP29µ3¬¿Ž®ÒÝu6°¿áaÚ7÷W¯¿@¡ž>°¿ir1Öq°¿Žç3 Þ¬¿Öa°äª¿Û4¶×‚Þ«¿Ì †:¬p«¿_ÎlW胭¿»)嵺«¿Í’5µl­¿çþêqßj­¿ù½Mö#­¿X7Þ«­¿Á­»yªC®¿¬‡¾»•­¿J›ª{ds­¿o.þ¶'H¬¿3Pÿ>㪿#Ûù~j¼¬¿‰Ï`ÿu®¿UPQõ+¯¿|—wJ¯¿n1?74e¯¿Z ‰{,}°¿eP3¤Šª¿…µ1vÂK°¿Ús™šo°¿@¼®_°¿b£¬ßLL¯¿?T1³¯¿Ä$\È#°¿,ºõš°¿B•š=°¿ÿ¬U»&°¿Qgî!á{¯¿@j'÷;°¿· b k¯¿¹˜Št¯¿çfh<°¿d°âTka®¿AG«ZÒQ®¿îîº/g®¿á ½þ$>¯¿¸®˜Þ®¿G ^×/Ø­¿ÅŒðö ¬¿Ù•–‘zO­¿ÊPS鯿ZôPÛ†±¿æ!S>±¿Ž •bGã°¿HÝξò ±¿>”h±¿'„º„C¯¿¥,Cë⮿КiQ¯¿Œi¦{Ô¯¿^‘šv1­¿Ô»x?n¿¬¿»›§:äf°¿Ï…‘^Ô¾Ü'G¢°¿<ŸõfÔ°¿­ü2#­¿wÚŒƒ«¿ PO?¬¿§\á].â«¿‰±L¿D¼­¿=°S¬¬¿qÈÒŦ­¿4KÔÔ²­¿7QKs+„­¿©lXSY®¿Ô‚}i®¿ £YÙ>ä­¿”Ù “Œœ­¿‹3†9A›¬¿€œ0a4+«¿Þ„€| ­¿$`tys¸®¿ÍXäׯ¿“5µl­¯¿P)”…¯¯¿…DÚÆŸ¨°¿gÓÀͪ¿äŸÄv°¿—㈞°¿RC€ ˆ°¿Ÿ«­Ø_v¯¿*Æ3h诿sµ4·B°¿7n1?7°¿ÖT…]°¿^ÒƒN°¿»}V™)­¯¿$'· b°¿d¯w¼¯¿vmo·$°¿³š®'º.°¿ØžY ¦®¿°Tð2î¿øQ û=±®¿(·í{Ô_¯¿´ ”÷q4¯¿G«ZÒQ®¿${„š!U¬¿àg\8’­¿¹‰Zš[!°¿F•aÜ ¢±¿,»`pͱ¿:tzÞ±¿œæ=Î4±¿é`ýŸÃ|±¿È[®~l’¯¿R_–vj.¯¿LS8½‹¯¿Èš‘Aî"°¿`vOj­¿ë¬Øc"­¿·Aí·v¢°¿eRC€ °¿ÕBÉäÔΰ¿ŽÌ#0ð°¿‹ÆÚßÙ­¿ëpt•¿p À?¥J¬¿å—Á‘(¬¿°«ÉSVÓ­¿J·%rÁ¬¿+Nµf¡­¿st´ª­¿O²žZ}­¿j’Ìê­¿Ot ‡®¿?ÆÜ­¿(ðN>=¶­¿¼z¬¿°;Ýyâ9«¿¢$$Ò6þ¬¿c¸:â®®¿´æÇ_ZÔ¯¿Ä"†Ƥ¯¿þ™A|`ǯ¿Óˆ™}£°¿2uWvÁિ‰yVÒŠo°¿'¼§>°¿×3ÂÛƒ°¿aNÐ&‡¯¿ÆàaÚ7÷¯¿×Š6ǹM°¿ú›Pˆ€C°¿T1³Ïc°¿¾Û¼qR°¿%<¡×ŸÄ¯¿ÒQf`°¿^€}têʯ¿ßmUÙ¯¿?Qžy9°¿­k´è¡®¿øQ û=±®¿å|±÷â‹®¿•Óž’sb¯¿JbI¹û¯¿’’†®¿ ßû´¯¿¾ƒŸ8€~¯¿å%ÿ“¿{¯¿Îp>?Œ°¿uF^ÖĪ¿©ù*ùØ]°¿ƒŠª_é|°¿!Âøi°¿{ž?mT¯¿QJVÕ˯¿)ßÞ5°¿c{-è½1°¿ÏdT°¿lÌëˆC6°¿“¨|š“¯¿^ÒƒN°¿¤oÒ4(š¯¿õôøÃϯ¿,g~5°¿$Õw~Q‚®¿¸“ˆð/‚®¿‘ñ(•ð„®¿s×òA¯¿Šå–VC⮿üÄôûþ­¿£uT5A¬¿ÿç0_^€­¿WA tí °¿­jIG9˜±¿óÊõ¶™ ±¿…{eު밿ë¬Øc"±¿<…\©g±¿Ë2g¯¿þHVñ®¿÷”œ{h¯¿‡P¥f°¿h@½5_­¿Ç•F̬¿TÝ‹Š°¿T7Û°¿rNì¡}¬°¿²dŽå]õ°¿Ã×׺Ô­¿š±h:;¬¿¨rÚSrN¬¿ª x™a£¬¿ Ö8›Ž®¿Ø¹i3NC¬¿æCV¸­¿¦ ÐÒ­¿ÖMò#~­¿—6Êú­¿X­Lø¥~®¿Ý”òZ Ý­¿å+”ص­¿3oÕu¨¦¬¿R %“S;«¿>]ݱØ&­¿¿eN—ÅÄ®¿aŽ¿·é¯¿¨ÅàaÚ¯¿zÇ):’˯¿áñí]ƒ¾°¿¿ 1^óª¿ìÁ¤øø„°¿/ÞÛ/Ÿ°¿9^èI™°¿¶¹1=a‰¯¿¯±KTo °¿`Xþ|[°¿l$ ÂP°¿Þß4}v°¿‚WË™`°¿*Æ3h诿çUÕ{°¿•fó8 毿Øñ_ °¿y®ïÃAB°¿µÀ)Í®¿KÔÔ²µ®¿ºùFtϺ®¿fgÑ;p¯¿‘·\ýØ$¯¿”lu9% ®¿˜Þþ\4d¬¿¾‰!9™¸­¿°8œùÕ°¿é Œ¼¬‰±¿]‰@õ"±¿ïå>9 ±¿%xC8±¿øq4GV~±¿¿œ3¢´¯¿½á´àE¯¿èf Ü¶¯¿Ž®ÒÝu6°¿B\9{g´­¿ù½Mö#­¿ãOT6¬©°¿•™Òú[°¿ð5Çeܰ¿2V›ÿW±¿{O崧䬿êÐéy7¬¿ÈбƒJ¬¿ò`‹Ý>«¬¿Ð^}<ôÝ­¿±¿ìž<,¬¿Rðr¥ž­¿(ðN>=¶­¿;ŠsÔÑq­¿²ˆ×õ ®¿j÷«ßm®¿|+Ôð­¿Ç.Q½5°­¿pìÙs™š¬¿X«vMH«¿µÆ B­¿›!U¯²®¿76;R}篿×÷á !ʯ¿oïô¥¯¿³\6:ç§°¿‰^F±ÜÒª¿‡oaÝxw°¿Jy­„î’°¿E ¦aøˆ°¿C®Ô³ ”¯¿„Ÿ8€~߯¿pµN\ŽW°¿GN¶;°¿ÖqüPi°¿Ô€AÒ§U°¿R}ç%诿Z ‰{,}°¿Å­‚èÚ¯¿‡3¿š°¿žî<ñœ-°¿ý½4»®¿ Ï.ßú°®¿Ùy›©®¿GßÛôg¯¿MóŽSt$¯¿ùf›Ó®¿H¾D¬¿EºŸSŸ­¿÷ZÐ{c°¿CX%¬±¿®×gÎú°¿Âøiܛ߰¿OÌz1”±¿¢_[?ýg±¿=·Ð•T¯¿RÔ™{Hø®¿÷<Ú¨N¯¿øá !ʰ¿¼ËE|'f­¿èy’tͬ¿W|Cá³u°¿áíAȯ¿zÄè¹…®°¿9}=_³°¿ÔÑq5²+­¿ù÷„¬¿œú@òΡ¬¿ÌÔ$xC­¿D¥3û<®¿}Ê1Yܬ¿¹¨Åä­¿3SZK®¿`YiR º­¿ìˆC6.®¿Q¾¾Ö¥®¿@‰Ï`ÿ­¿Ø(ë7Ó­¿Oé`ýŸÃ¬¿µÿÖª]«¿Z×h9ÐC­¿ À;ùôØ®¿\Uö]ü¯¿ÁtZ·Aí¯¿AJ˜iû¯¿J}YÚ©¹°¿;r¤30òª¿]¡·x°¿[@h=|™°¿„½‰!9™°¿œ0a4+Û¯¿ÎQÚ°¿±3…Îk°¿ëâ6À[°¿2<ö³Xа¿EÚÆŸ¨l°¿j¾J>v°¿²Õ唀˜°¿ºÝË}r°¿eVïp;4°¿n§­Á8°¿u=Ñu᯿¯\o›©¯¿'ù¿b ¯¿ƒlY¾.￯\o›©¯¿tì ×1®¿uÍä›mn¬¿ž MKÊ­¿:Ë,B±°¿júì€ëб¿–?ß,±¿1#¼=±¿©öéxÌ@±¿¢·xxϱ¿ÌDR·³¯¿"—Ž9¯¿“¨|š“¯¿Ë+×Ûf*°¿Ð^}<ôÝ­¿™b‚ŽV­¿ç4 ´;¤°¿©¾ó‹°¿éšÉ7Ûܰ¿vÂKp걿 ¸Ê;­¿Š§wñ~¬¿—6–~¬¿êy7­¿)®*û®®¿xÓ-;Ä?¬¿íÑV%‘­¿ß4}vÀ­¿¸’x­¿ŽÍŽTßù­¿°¿ä£Åܰ¿)ë7Ó…°¿ù¢=^H‡¯¿±à~À°¿Mø¥~ÞT°¿Çò®zÀ<°¿LnYk°¿Ã¹†O°¿•fó8 毿ir1Öq°¿{úüá篿8iͰ¿5cÑtv2°¿èI™ÔЮ¿£¢ÑÄ®¿»a̮ۢ¿Ÿ«­Ø_v¯¿J ,€)¯¿[(™œÚ®¿ŠÍǵ¡b¬¿¯xê‘·­¿žB®Ô³ °¿ŒgÐÐ?Á±¿?Œm±¿¯Z™ðKý°¿ YÝê9±¿íÑV%‘±¿d¯w¼¯¿q:ÉV¯¿>u¬Rz¦¯¿;þ 2°¿%êŸæä­¿ÀnÝÍS­¿+¥gz‰±°¿í`Ä>°¿'ŸÛ2à°¿¨äœØCû°¿··[’v­¿ÆÝ Z+Ú¬¿(F–̱¬¿­‡/EH­¿òz0)>®¿ÌAÐѪ–¬¿oõœô¾ñ­¿ÒÄ;À“®¿YÂÚ;á­¿=ƒù+d®¿ã©GÜÖ®¿c}“E®¿”XS®¿Xqªµ0 ­¿Ý^Ò­«¿öBÛÁˆ­¿dÎ3ö%¯¿UÙY°¿Ã.Šø°¿vþÓ °¿§/ú Ò°¿ÆnŸUfJ«¿Wya§°¿¡×1®¸°¿RóUò±»°¿8ýÚú鯿b k_@/°¿Ûúé?k~°¿_F±ÜÒj°¿7¨ýÖN”°¿ î<0€°¿0K;5—°¿Ãc?‹¥°¿Bí·v¢$°¿ÊTÁ¨¤N°¿Ñ"Ûù~j°¿y ²H¯¿©»² ¯¿×Ùf¯¿“5µl­¯¿ü¤6qr¯¿ˆLùT®¿ÅªA˜Û½¬¿:è®¿ì½ø¢=^°¿HO‘CÄͱ¿Ö‹¡œhW±¿˜Û½Ü'G±¿/¨o™Óe±¿‘'I×L¾±¿‘´}̰¿ õôøÃ¯¿"Žuq °¿ùõCl°¿/¥.ÇH®¿4óäš™­¿a7l[”Ù°¿W$&¨á[°¿ŽW zR&±¿äˆž”I±¿ó:â­¿ñaö²í¬¿‘Òl‡Á¬¿J›ª{ds­¿c%æYI+®¿&9`W“§¬¿ðiN^d®¿ú™zÝ"0®¿G ^×/Ø­¿rl=C8®¿ºùFtϺ®¿¿Òùð,A®¿tì ×1®¿)Ý^Ò­¿ßÁÿV²«¿™Iô2Š­¿ÿ®Ïœõ)¯¿â|~!°¿BA)Z¹°¿AfgÑ;°¿#ºg]£å°¿sIÕv|«¿J´°¿]¦&ÁÒ°¿aßN"¿°¿@ÛjÖ߯¿Ô($™Õ;°¿?ÆÜµ„°¿M×]~°¿œN²Õå”°¿þc!:ް¿º/g¶+°¿¢A žB®°¿)\Âõ(°¿›äGüŠ5°¿;5— u°¿ªH…±… ¯¿:3Pÿ®¿·&ݖȯ¿_³\6:篿6 B\9{¯¿’$W@¡®¿iUK:ÊÁ¬¿%ÍÓÚ4®¿·¶ð¼Tl°¿¤¨3÷ð±¿+*ÿZ^±¿`r£ÈZC±¿€)´t±¿£7ünº±¿:w»^š"°¿—Ž9ÏØ¯¿K’çú>°¿QhY÷…°¿‹RB°ª^®¿Ïø¾¸T¥­¿órØ}Çð°¿Š!9™¸U°¿Q._x%±¿/¨o™Óe±¿%êŸæä­¿¥½Á&S­¿—Ép<Ÿ­¿÷ÍýÕã¾­¿\ŽW zR®¿Ãº﬿ oÖà}U®¿Kw×Ù®¿%ÍÓÚ4®¿ó=#Á®¿EöA–¯¿ œO«”®¿¾ÚQœ£Ž®¿J›ª{ds­¿I„F°qý«¿Ïƒ»³vÛ­¿©PÝ\üm¯¿£’:M°¿)´¬ûÇB°¿^gCþ™A°¿¤ÿåZ´±¿ã¦šÏ¹«¿ý¾óâ°¿77¦',ñ°¿éòæp­ö°¿s ßû°¿~r f°¿¾ˆ¶cê®°¿ƒi>"¦°¿× /½ý¹°¿î{Ô_¯°°¿‚WË™`°¿nmáy©Ø°¿» ”X°¿É°Š72°¿½f¾ƒŸ°¿CV·zNz¯¿å%ÿ“¿{¯¿ïÊ.\s¯¿Lm©ƒ¼°¿h%­ø†Â¯¿ƒÃ "RÓ®¿$™Õ;Ü­¿Ôµö>U…®¿„ûPŒ°¿|+Ôð±¿{„±¿´UIdd±¿áBÁ”±¿ýläº)屿Î9x&4I°¿>\rÜ)°¿–Ð]gE°¿Ít¯“ú²°¿ý½4»®¿1 ò®¿Ïg@½±¿õc™~‰°¿ ¦šYK±¿“¦AÑ<€±¿%ÍÓÚ4®¿W\•›¨­¿Ò¦êÙ\­¿cò˜ù®¿Lª¶›à›®¿©öéxÌ@­¿'f½ʉ®¿:;%¯®¿‰Ï`ÿu®¿–è,³Å®¿]7¥¼VB¯¿ësµûË®¿ÐÔë±®¿Ç.Q½5°­¿UÂzýI¬¿Ä "RÓ.®¿­lò–«¯¿²Jé™^b°¿Ã¤Rìh°¿Tn¢–æV°¿'…y3±¿ä1•ñï«¿5´Ø€±¿ïå>9 ±¿G 6u±¿÷åÌv…>°¿ð¦[vˆ°¿6ÇeÜÔ°¿6ÊúÍİ¿ýkyåzÛ°¿ò?ù»wÔ°¿M×]~°¿XÅ™Gþ°¿À³=zÃ}°¿ Q¾ …°¿+Qö–r¾°¿Þ擼¯¿9¹ß¡(Я¿É>Ȳ`⯿Ÿu–=°¿ â;þ °¿³í´5"¯¿À\‹ m­¿ †7kð®¿ý»>sÖ§°¿ŠsÔÑq5²¿óäš™±¿(eRC€±¿^*6æuı¿l—6–²¿9ÏØ—l°¿ PO?°¿Ù˜×‡l°¿—ÅÄæãÚ°¿R_–vj.¯¿ÊÝçøhq®¿N|µ£8G±¿™Hi6ð¿#M¼[¯¿ÚV³Îø®¿ÚV³Îø®¿LŒeú%â­¿N+…@.q¬¿¯>úîV®¿ý¾óâį¿Ûúé?k~°¿@¡ž>°¿~£<ór°¿éÕ¥¡F±¿'ÙêrJ@¬¿ô #±¿{2ÿè›4±¿V¸å#)±¿²žZ}uU°¿ú*ùØ] °¿œÝZ&Ãñ°¿EÓÙÉà°¿æèñ{›þ°¿›™Eï°¿*Õ"¢°¿*Œ-9(±¿†s 34ž°¿mæÔBɰ¿ÛÝt_ΰ¿_³\6:篿çãÚP1ί¿šÒú[ð¯¿ô3õºE`°¿üÆ×žY°¿wÖn»Ð\¯¿ Q…?Û­¿OÎPÜñ&¯¿`™D½°¿7Û$²¿9 {Úᯱ¿JzZœ±¿Í!©…’ɱ¿îÎÚm²¿&5´Ø€°¿ 4ØÔyT°¿àºbFx{°¿Y|^ñ°¿o À±g¯¿Ô˜sIÕ®¿&«"ÜdT±¿A'„º°¿@öz÷Ç{±¿¦',ñ€²±¿Ot ‡®¿ ~þ{ðÚ­¿ß4}vÀ­¿<3Áp®a®¿W˜¾×¯¿ ]Þ®­¿yvùÖ‡õ®¿¯²¶)¯¿?ÿ=xíÒ®¿uÈÍp>¯¿‡¥Õ°¯¿¾ø¢=^H¯¿{Ü·Z'.¯¿ÞïU+®¿q¹5鶬¿âKº ®¿™Ö¦±½°¿„aÀ’«°¿È¨p©°¿Î’Z(™°¿NÔÒÜ a±¿V(Òýœ‚¬¿’”ô0´:±¿ÜK£uT±¿=a‰”M±¿o¹ú±I~°¿•AÕèÕ°¿¥2ű¿GV~Œ±¿ãâ¨ÜD-±¿o.2±¿üä(@̰¿ñKý¼©H±¿¦~ÞT¤Â°¿½Œb¹¥Õ°¿æèñ{›þ°¿ ©ÛÙW°¿€™ïà'°¿vÅŒðö °¿„elèf°¿Ýyâ9[@°¿HÜÖž¯¿2 {½ûã­¿äòÒo¯¿ÿunڌӰ¿ ˜£Çïm²¿%’èe˱¿¢–æV«±¿Ç ¿›nÙ±¿Á-]Á6²¿Óˆ™}£°¿@¡ž>°¿Ëe¡°¿Kçó±¿Ñ°u­½¯¿ÀË eý®¿ ¦}s±¿ ߺñî°¿4Ÿs·ë¥±¿ïÚÄɱ¿êè¸Ù•®¿°«ÉSVÓ­¿ÔdÆÛJ¯­¿@̘‚5®¿Î4aûɯ¿f 2þ}Æ­¿žbÕ Ìí®¿„çÞÃ%¯¿ÄÑUº»Î®¿B#ظþ]¯¿LÞ3ßÁ¯¿åš™E¯¿]7¥¼VB¯¿V¼‘y䮿¯D ú‘¬¿Æ¡~¶®¿÷_˜L°¿²,˜ø£°¿4»î­°¿dT8‚°¿Cý.l±¿Ð w.Œ¬¿ñKý¼©H±¿™b‚ŽV±¿Ì_!seP±¿2g—o}°¿‰ÿ"h̰¿ø6ýÙ±¿rÝ”òZ ±¿5 S"±¿7e±¿Ûe6Ȱ¿*ÙYôN±¿#Ûù~j¼°¿Žlê°¿Ac&Q/ø°¿ãüM(°¿”¼Ǚ&°¿ßmÞ8)°¿À³=zÃ}°¿Ì^¶¶F°¿oïô¥¯¿¾4»î­¿°rh‘í|¯¿sÙ蜟ⰿSé'œÝZ²¿xî=\rܱ¿÷!o¹ú±±¿ëŠáí±¿3ÞVzm6²¿mâä~‡¢°¿^ò?ù»w°¿r¥ž¡°¿Ä^(`;±¿-Ó¾¹¯¿h¯>ú”¢•{±¿->Àx±¿’Ï+žz¤±¿èÀr„ 䱿I/j÷«¯¿kïSUh ®¿ø‹Ù’U®¿"ÇÖ3„c®¿.®ñ™ìŸ¯¿¸°n¼;2®¿4¡l\¯¿ê‘·µ…¯¿`ÈêVÏI¯¿õœô¾ñµ¯¿Hˆò-$°¿u["œÁ¯¿ÞŽpZ𢯿š–X|®¿«–t”ƒÙ¬¿_• •-¯¿kIG9˜M°¿Óg\W̰¿©gA(ïã°¿‘~û:pΰ¿+Nµf¡±¿¾³^ 嬿/Ý$±¿x $(~Œ±¿£>É6‘±¿5%Y‡£«°¿ÝCÂ÷þ±¿äg#×M±¿^‘šv1±¿8ù-:Yj±¿vùÖ‡õF±¿5´Ø€±¿îÌù†±¿Ù²|]†ÿ°¿â¯Éõ±¿Ñ@,›9$±¿™¹Àå±f°¿ÒQf`°¿“ÆhUM°¿‚ŽVµ¤£°¿Ç):’˰¿vmo·$°¿4iSul®¿‘Õ­ž“Þ¯¿: ûv±¿¤ö{b²¿Ävü²¿Qøl챿ÆGå&²¿ s‚69|²¿Ï‚PÞÇѰ¿• •-¯°¿ÆÝ Z+Ú°¿~oÓŸýH±¿ÿÐÌ“k °¿fgÑ;p¯¿îаu­±¿¿ž¯Y.±¿‰ jøÖ±¿z‹üú!²¿ümOØî®¿=HO‘CÄ­¿Qølì­¿g+/ùŸü­¿£<órØ}¯¿dÍÈ w®¿S’u8ºJ¯¿Ìa÷Ãc¯¿i:;%¯¿‚9züÞ¦¯¿Èš‘Aî"°¿u["œÁ¯¿ÏJZñ …¯¿¯–;3Áp®¿4¢´7øÂ¬¿ÁV ‡3¯¿î@òèF°¿#‡ˆ›Sɰ¿«–t”ƒÙ°¿"T©Ù­°¿âÊÙ;£±¿'ŸÛ2ି~sõ¸o±¿^-wf‚±¿uU ƒ‡±¿r„Ѭ°¿l–ËFçü°¿q9^èI±¿@k~ü¥E±¿ ëÆ»#c±¿WÍsD¾K±¿ï!8ö°¿³&øŠn±¿U¿Òùð°¿Ã×׺Ô±¿@aÃÓ+±¿'Ý–Èg°¿uuÇb›T°¿ú›Pˆ€C°¿[ìöYe¦°¿$}ZE°¿« ºö°¿ê]¼·_®¿”3w¼É¯¿K¦z2ÿ°¿Ïõ}8Hˆ²¿5 ÞF²¿œ†¨ÂŸá±¿%uš²¿×]~p²¿Gˆ,Ò°¿^»´á°°¿¿»•%:˰¿$ïÊP±¿Owžxΰ¿â<œÀtZ¯¿1•~ÂÙ­±¿¿ÑŽ~7±¿xB¯?‰Ï±¿ÀÎM›q²¿¾ø¢=^H¯¿&å`6®¿)H4"®¿Hû`­Ú­¿o»ì¯¿ƒˆÔ´‹i®¿hÍ¿´¨¯¿-Ó¾¹¯¿¤µûU€¯¿‘`ª™µ°¿‡kµ‡½P°¿1Îß„B°¿¨ÅàaÚ¯¿?§ ?¹®¿ü´W­¿LS8½‹¯¿³Ñ9?Åq°¿iàG5ì÷°¿Tàd¸±¿ù†Âgëà°¿S#ô3õº±¿‹ÆÚßÙ­¿6<½R–±¿ àbE ¦±¿J&§v†©±¿a‹Ý>«Ì°¿{ö\¦&±¿èy’t±¿üª\¨ük±¿b„ðh㈱¿Tœˆ~±¿ïå>9 ±¿"ýöuàœ±¿ªò=#±¿.É»š<±¿§@fgÑ;±¿~Q‚þB°¿ô‹ôz°¿Ù˜×‡l°¿ï®³!ÿ̰¿ƒ§Z ³°¿ˆg 2*°¿…î’8+¢®¿=·Ð•°¿.sž±/±¿²ƒJ\Ǹ²¿ÉËšXà+²¿Ì}r ²¿×4ï8EG²¿=™ôMš²¿œˆ~mý°¿Ç¸ââ¨Ü°¿Q£dVï°¿'Þž´p±¿…]=ð1°¿èf Ü¶¯¿B´V´9α¿¥½Á&S±¿'÷;²¿²½ôÞ²¿y=˜¯¿Hû`­Ú­¿LbõG®¿p +TT­¿è¾œÙ®Ð¯¿õ¸oµN\®¿1е/ ¯¿˜Ÿ¯¿~âú}¯¿ú¸6TŒó¯¿HŒž[èJ°¿¬tw ù¯¿˜l<Øb·¯¿êè¸Ù•®¿0[wó¬¿#£’°o¯¿¶Û.4×i°¿xEð¿•ì°¿bjKäõ°¿@„¸röΰ¿`­Ú5!­±¿å 0óü¬¿§wñ~Ü~±¿ïSUh –±¿§#€›Å‹±¿#‡ˆ›Sɰ¿ŸrL÷±¿Â(›r±¿8MŸp]±¿ˆKŽ;¥ƒ±¿9€~ß¿y±¿Bêvö•±¿x·²Dg™±¿lîè¹±¿LÂ…<‚±¿UkaÚ9±¿Ê‹LÀ¯‘°¿$Ó¡Óón°¿Jš?¦µi°¿aßN"¿°¿Ê7Ûܘž°¿’;l"3°¿óèžu®¿ï«r¡ò¯¿ñó߃×.±¿‘Жs)®²¿ÐÒl#²¿g+/ùŸü±¿Ï¾ò =E²¿¬äcw’²¿Z Ý!ű¿£ x|{×°¿©¿^aÁý°¿e6È$#g±¿ÇF ^×/°¿ –\Å⯿ÌFçüDZ¿lÍV^ò?±¿És}²¿n¥×fc%²¿K?ªa¯¿){K9_ì­¿JDøAc®¿Í:ãûâR­¿{-è½1°¿€‚‹5˜®¿—Ž9ÏØ¯¿Ÿ>øù¯¿2rö´Ã¯¿¹5é¶D.°¿‚Zº‚m°¿ý¡™'×°¿ßøÚ3K°¿%“S;ÃÔ®¿-!ôlV­¿ ßû´¯¿o¹ú±I~°¿ /Ý$±¿2ª ãn±¿Žx²›ý°¿ΤMÕ±¿‹Q×ÚûT­¿0.sž±¿]£å@µ±¿´8c˜´±¿åa¡Ö4ï°¿Z×h9ÐC±¿¸>¬7j…±¿ÍçÜíz±¿ûZ—¡Ÿ±¿<½R–!ޱ¿ú˜t&±¿>w‚ý×¹±¿É"M¼<±¿ª)É:]±¿à0Ñ O±¿O‘CÄÍ©°¿“ýó4`°¿t%Õ?ˆ°¿áÐ[<¼ç°¿ JÑʽ°¿æÊ ÚàD°¿›T4Öþή¿¯]ÚpX°¿Ház®G±¿”Þ7¾ö̲¿E€Ó»x?²¿dyW=`²¿n0Ôa…[²¿²×»?Þ«²¿G 6u±¿’]i©÷°¿?Œm±¿¬ãø¡Òˆ±¿‚ÿ­dÇF°¿‘Õ­ž“Þ¯¿Õv|Óô±¿À3‰z±¿¯³!ÿÌ ²¿#žìfF?²¿·ìÿ°¥¯¿uÇb›T4®¿75Ð|ÎÝ®¿6l±Ûg­¿/÷ÉQ€(°¿. ø1殿>°ã¿@°¿‰–<ž–°¿¼;2V›ÿ¯¿´up°71°¿ò\߇ƒ„°¿k¸¯@°¿á² ›.°¿R_–vj.¯¿]£å@µ­¿‚Äv÷ݯ¿`¬o`r£°¿J¸Gp#±¿œæ=Î4±¿f.py¬±¿2Ì Úäð±¿r£ÈZC©­¿œSÉPű¿À—ƒf×±¿…ÐA—p豿¶Mñ¸¨±¿ ƈD¡e±¿[AÓ+£±¿Ü‚¥º€—±¿¡B]±¿ "RÓ.¦±¿¶Øí³ÊL±¿cBÌ%UÛ±¿ö?ÀZ±¿åœØCûX±¿ú{)°¿_b,Ó/¯¿ºê°¿Pª}:3°¿>\rÜ)°¿·^Óƒ‚R°¿3¸<ÖŒ°¿kIG9˜M°¿¤á”¹ùF°¿x N} y¯¿ÞïU+®¿ÀÌwð°¿´ã†ßM·°¿Ó–x@±¿c('ÚUH±¿L£uT5±¿ ±ú# ²¿j1x˜öÍ­¿™õb('Ú±¿EÖJí±¿aü4Þv¡¹N#±¿eâVA t±¿p]1#¼±¿öF­0}¯±¿C=·Ð±¿l´è¡¶±¿+*ÿZ^±¿j‰•ÑÈ籿§ÔE e±¿Ó1çû’±¿fÁÄE±¿Ç¸ââ¨Ü°¿×L¾ÙæÆ°¿B_zûsѰ¿z«®C5%±¿;]¥»ë°¿.ÿ!ýöu°¿+TTýJ¯¿ÒQf`°¿[éµÙX‰±¿i6Ã`þ²¿5ð£ö{²¿\ætYLl²¿ºõš”²¿vÀuÅŒð²¿h”.ýKR±¿&§v†©-±¿ƒ/L¦ F±¿,-#õžÊ±¿µ‡½PÀv°¿Åä 0ó°¿l’ñ+²¿'½o|í™±¿W"PýƒH²¿:ZՒ޲¿qUÙwEð¯¿äIÒ5“o®¿—á?Ý@¯¿{ö\®¿ðN>=¶e°¿ÝД~P¯¿È%Ž<Y°¿y²›ýh°¿î@òèF°¿³Ñ9?Åq°¿šwœ¢#¹°¿;5— u°¿ î<0€°¿ÊŠ;Þ䯿Ê52;‹®¿ 0,¾-°¿Ï.ßú°Þ°¿Í’5µl±¿¢ ê[æt±¿¸æŽþ—k±¿d¬6ÿ¯:²¿Qƒi>"®¿Ã,´sš²¿LÃð1%²¿Íù†²¿=a‰”M±¿‚7¤Q“±¿_]¨Å౿È—PÁ᱿È@ž]¾õ±¿b™¹À屿Á;ùôØ–±¿ßú°Þ¨²¿° ÍX4±¿€-¯\o›±¿n¾ݳ®±¿û”c²¸ÿ°¿€»ì×î°¿!\…zú°¿ìÕ[[±¿ÞÊe±¿4J—þ%©°¿ÑXû;Û£¯¿½©H…±…°¿òµg–¨±¿jÁ‹¾‚4³¿c'¼§²¿K‘|%²¿ª¹Ü`¨Ã²¿û–9]³¿×j{¡€±¿šë4ÒR±¿9€~ß¿y±¿HS=™ô±¿[ìöYe¦°¿ôPÛ†Q°¿¸Üšt[²¿6Y£¢Ñ±¿]mÅþ²{²¿ªa¿'Ö©²¿‘`ª™µ°¿ˆôÛ×s®¿^Mž²š®¯¿“âã²ó®¿ö•é)r°¿®¼äòw¯¿ƒÞCp°¿û­( ‰°¿5B?S¯[°¿=C8fÙ“°¿ßºñîȰ¿pìÙs™š°¿þc!:ް¿³–Òþ°¿˜Ãî;†Ç®¿­†Ä=–>°¿Ç{ö°¿Œ0E¹4~±¿·•^›±¿þ oÖà}±¿^‚SH²¿"Ä•³w®¿ÍX4 ²¿—©Ið†4²¿iå^`V(²¿&Œf±¿a4+Û‡¼±¿U‰²·”ó±¿[$íF󱿮,ÑYf²¿'÷;²¿È F³²±¿—©Ið†4²¿ó)‚ª±¿g'ƒ£äÕ±¿C=·Ð±¿c}“±¿øŠn½¦±¿LÂ…<‚±¿®ºÕ”d±¿J¸Gp#±¿ð1Xqªµ°¿h%­ø†Â¯¿‹3†9A›°¿Kº ¾±¿èøhqÆ0³¿ÐÔë±²¿x¸£²¿àŸR%ʲ¿àÖÝ<Õ!³¿Ó1çû’±¿˜3Ûú`±¿÷Ãc?‹±¿uèô¼ ²¿XŽ<»°¿‘›á|~°¿?p•'v²¿ç“¼DZ¿…î’8+¢²¿M…x$^ž²¿ï_{fI°¿)$™Õ;Ü®¿Ÿqá@H°¿ õôøÃ¯¿ª·¶J°°¿Ÿ>øù¯¿˜Á‘(´°¿TUh –Ͱ¿Âmmáy©°¿B_zûsѰ¿Ì|?q±¿Êß½£Æ°¿§/ú Ò°¿,Eò•@J°¿:Yj½ßh¯¿RC€ ˆ°¿'…y3±¿ÅÿQ¡º±¿‰ jøÖ±¿L4HÁSȱ¿U£W”†²¿¸®˜Þ®¿åñ´üÀU²¿l#žìf²¿£;ˆ)t²¿´„Ö×±¿» )?©ö±¿U±¿[²*ÂMF±¿«‹Ã™_±¿€-¯\o›±¿m6 B\±¿Äy8é°¿ø¯=³$°¿¢ñDçá°¿78ýÚú±¿Ø›’“‰³¿«°¿,ØF<ÙͰ¿u“V±¿âuý‚ݰ¿Žlê°¿y²›ýh°¿V+~©Ÿ¯¿8ƒ¿_Ì–°¿äg#×M±¿ßÃ%ÇÒ±¿oõœô¾ñ±¿÷Ý—3Û±¿½«0™²¿q¬‹Ûh¯¿£7ün²¿ËǺ¸²¿tϺF˲¿]£å@µ±¿®€B=}²¿Çô„%P²¿ÒÈçO=²¿8¢{Ö5Z²¿9Ñ®BÊO²¿Åâ7…• ²¿ï¨1!æ’²¿Zõ¹ÚŠý±¿³˜Ø|\²¿¸àŸR%²¿imÛk±¿4¼Yƒ÷U±¿°p’æi±¿‰±L¿D¼±¿÷êã¡ïn±¿Æ5>“ýó°¿!’!ÇÖ3°¿ÏÚmšë°¿Ñ=ë-²¿>ê¯WXp³¿*Wx—‹ø²¿— uXᲿJíE´³¿REñ*k³¿xî=\rܱ¿[AÓ+£±¿2È]„)ʱ¿ÅÅQ¹‰Z²¿eS®ð.±¿Ý´§!ª°¿ˆƒ„(_в¿eTÆÝ ²¿]ûzᲿiQŸä³¿ÉXmþ_u°¿²ºÕsÒû®¿VÕ{L°¿ï_{fI°¿ë˜Ü°¿é(³ 0°¿aãúw}æ°¿L÷™±¿þš¬QѰ¿øŠn½¦±¿Ååx¢'±¿]1#¼=±¿ß¥Ô%㱿u¬Rz¦—°¿*Æ3h诿Ky ²°¿n‡†Å¨k±¿œÞÅûqû±¿ ‚Ç·w ²¿õÕUZ ²¿a5–°6Ʋ¿F´Swe¯¿¨ÿ¬ùñ—²¿Ìï4™ñ¶²¿Ùy›©²¿k¸È=]ݱ¿è÷ý›'²¿¾IÓ h²¿©Or‡Md²¿ŸVÑšy²¿Ô‚}i²¿¤30ò²&²¿ù,σ»³²¿Uh –Ͳ¿yX¨5Í;²¿hé ¶O²¿ B²€±¿§Ëbbóq±¿‰&PÄ"†±¿$cµùÕ±¿ØEу±¿â[X7Þ±¿"q¥]°¿Àyq⫱¿eM.²¿u¯“ú²´³¿Öª]Ò³¿*´t³¿ò$éšÉ7³¿E¸É¨2Œ³¿©;‡ú±¿šxxÒ±¿j1x˜ö±¿5Dþ o²¿8IóÇ´6±¿‡¥Õ°¿HüŠ5\䲿é*Ý]gC²¿ñœú@ò²¿Â1Ëž6³¿ƒÞCp°¿3‡¤J&¯¿ôPÛ†Q°¿‰”fó8°¿6ÇeÜÔ°¿‘¸ÇÒ‡.°¿¤§È!âæ°¿ØƒIññ ±¿O•ï‰Ð°¿8Ùî°¿Àyq⫱¿=~oÓŸý°¿mɪ7±¿då—Á‘°¿µ§!ªð¯¿[ìöYe¦°¿¡0(Óhr±¿!±Ý=@÷±¿Ì}r ²¿ta¤µû±¿àŸR%ʲ¿-{Øœƒ¯¿ÃFY¿™˜²¿Eg™E(¶²¿DàH Á¦²¿ßÃ%ÇÒ±¿Yùe0F$²¿Ê1YÜd²¿AŸÈ“¤k²¿j£:Èz²¿`ãúw}²¿õ-sº,&²¿!# Â¤²¿?{ó²¿ÏdT8²¿S‘ c A²¿Ó…Xý†±¿Öã¾Õ:q±¿'á_±¿75Ð|α¿–Tÿ ’±¿ `­Ú5!±¿±o'á_°¿‡á#bJ$±¿û Ë‚‰?²¿ä¢ZD“³¿Åã¢ZD³¿‡3¿š³¿ÀDˆ+³¿É¯bƒ…³¿òDçᲿsÖ§“ű¿g Ü¶ï±¿ND¿¶~²¿—Tm7Á7±¿x™a£¬ß°¿ÚV³Îø²¿÷“1>Ì^²¿sÛ¾Gýõ²¿6é¶D.8³¿]¥»ël°¿y=˜¯¿ô3õºE`°¿¨n.þ¶'°¿€^»´á°¿›8¹ß¡(°¿îZB>èÙ°¿¿ò =E±¿ÊI»Ñǰ¿ ¡c±¿‡5•Ea±¿â;þ ±¿ëˆ»z±¿DŠM °¿AºØ´R°¿³\6:ç§°¿WÎÞm±¿Ê`æ;ø±¿èŸàbE ²¿ÊN?¨‹²¿bX9´È²¿1´:9Cq¯¿HÅÿQ¡²¿­ÃÑUº»²¿‚,`·²¿­¡Ô^DÛ±¿v‡$²¿û$wØDf²¿l#žìf²¿/0+é~²¿ÚÉà(yu²¿é~NA~6²¿<Û¤¢±²¿wö•é)²¿ßÛôg?²¿¢©ÛÙW²¿§#€›Å‹±¿¨ŒŸq±¿‹ˆbò˜±¿xî=\rܱ¿™Iô2б¿ÉÊ/ƒ1"±¿!Âøi°¿{ö\¦&±¿„Ø™Bç5²¿¼§>¼³¿Sy=˜³¿úBÈyÿ³¿6é¶D.8³¿–² q¬‹³¿;ü5Y£²¿„Iññ Ù±¿Æ6©h¬ý±¿Ÿ®îXl“²¿°=³$@M±¿—â¶ô°¿_b,Ó/³¿¦í_YiR²¿Öª]Ò³¿^fØ(ë7³¿Ig`äeM°¿«˜J?á쮿2kœMG°¿Òã÷6ýÙ¯¿–’å$”¾°¿N ^ô°¿¸tÌyƾ°¿^…”ŸTû°¿ž\S ³³°¿—ÅÄæãÚ°¿B=}þ°¿ë˜Ü°¿ï9°!±¿&áBÁ°¿Éq§t°þ¯¿°bƒ…“°¿°=³$@M±¿Ò9?Åq౿Q¤û9ù±¿/†Èé뱿w;Sè¼²¿:“6U¯¿]Tœˆ²¿GqŽ::®²¿nî•y«²¿ZEhæÉ±¿¸<ÖŒ ²¿æÌv…>X²¿n0Ôa…[²¿û$wØDf²¿1³Ïc”g²¿³˜Ø|\²¿O;ü5Y£²¿ýKR™b²¿õäC²¿–z„ò>²¿šé^'õe±¿½Þýñ^±¿_x%És±¿‹¦³“Á±¿âꈻz±¿(~Œ¹k ±¿ô‡fž\S°¿~8Hˆò±¿¦bc^G²¿]¡·xx³¿ž•´â ³¿Ê£aQ³¿”†…$³¿m«Yg|³¿­QÑ貿;mÆÁ±¿oõœô¾ñ±¿1 íœf²¿:$µP29±¿ò—õIî°¿ ŠcãósCS²¿ÓÁú?‡ù²¿à»Í³¿£V˜¾×°¿BÒ§Uô‡®¿J·%rÁ°¿òÐw·²D¯¿Mƒ¢y‹°¿zo À±¯¿–³wF[•°¿ö$°9ϰ¿|ïoÐ^}°¿0o»°¿à|zl˰¿0eà€–®°¿Oé`ýŸÃ°¿ÉPÅ[°¿ 6ªÓ¬¯¿Ú|a2U°¿Q._x%±¿¤q¨ß…­±¿¥øø„ì¼±¿ÌšXà+º±¿fõ‚Os²¿¶óýÔx鮿ip[[x^²¿ÞZ&Ãñ|²¿íšÖt²¿<Äy8±¿Õ—¥šË±¿=+iÅ7²¿Oyt#,*²¿/Màô.²¿½3Úª$²¿Ç ¿›nÙ±¿vúA]¤P²¿Æ2ýñÖ±¿ Ã|yö±¿xµÜ™ ²¿À%ÿ”*±¿$ñòt®(±¿¡€í`Ä>±¿ÿç0_^€±¿ÜŸ‹†ŒG±¿a7l[”Ù°¿Ã.Šø°¿wþEа¿LŒeú%Ɀð¥ð Ùu³¿ý2WÕ²¿*ÿZ^¹Þ²¿äÜ&Ü+ó²¿= ByG³¿÷Xúб¿4GV~Œ±¿ÄÐêä ű¿˜0š•íC²¿Hþ`à¹÷°¿*Æù›Pˆ°¿LÝ•]0¸²¿É <÷²¿ìöYe¦´²¿uå³<ü‹ 1“¨¯¿Í°QÖo&®¿¥žÐ믿DÃbÔµö®¿0Ö70¹Q°¿—®`ñd¯¿-ÌB;§Y°¿5Ñ磌°¿Mø¥~ÞT°¿$}ZE°¿Jy­„î’°¿È}«uâr°¿|š“™°¿Ô|•|ì.°¿â‘xy:¯¿ýM(DÀ!°¿€»ì×î°¿yY |±¿nߣþz…±¿N³@»Cб¿#J{ƒ/L²¿aR||Bv®¿°Žã‡J#²¿Ò¥I²¿Ú’Un2²¿¥½Á&S±¿GéÒ¿$•±¿å ïrß±¿E¹‡„ﱿôNÜóü±¿æå°ûŽá±¿zÅrK«±¿IŸVѲ¿gHÅ«¬±¿úÕ‘#±¿ª›‹¿±¿ÔFu:õ°¿ºÚŠýe÷°¿œ5x_• ±¿ ¸çùÓF±¿U¿Òù°¿ÍÈ w¦°¿Ð}9³]¡¯¿î#·&Ý–°¿¾Ý’°«±¿nú³)"³¿sL÷™²¿u®(%«²¿yëüÛe¿²¿W²c#³¿fÁÄE±¿³z‡Û¡a±¿âꈻz±¿úA]¤P²¿ƒÁ5wô¿°¿tšÚR°¿¹Ù•–‘²¿Ü ¢µ¢Í±¿$Õw~Q‚²¿~«uâr¼²¿ÖþÎöè ¯¿‰~mýôŸ­¿§êÙ\5¯¿¬8ÕZ˜…®¿ö'ñ¹쯿-]Á6âÉ®¿'Nîw( °¿}?5^ºI°¿î]ƒ¾ôö¯¿1&ý½°¿­†Ä=–>°¿%ËI(}!°¿%Ïõ}8H°¿Äz£V˜¾¯¿^L3Ý뤮¿ð0í›û«¯¿H¾°¿^d~$±¿í)H4±¿ÒÇ|@ 3±¿ *ª~¥ó±¿ùÛž ±Ý­¿ŽÉâþ#Ó±¿”¼:ǀ챿?ÆÜ±¿¦aøˆ˜±¿ÿ°¥GS±¿‡ú]Øš±¿?eÄ ±¿öF­0}¯±¿ÐÓ€AÒ§±¿W%‘}e±¿V W@ܱ¿R¹‰Zš[±¿<Äy8±¿°È¯bƒ±¿Ê7Ûܘž°¿[@h=|™°¿«’È>Ȳ°¿“8+¢&ú°¿o½¦¥°¿Õ¯t>°¿®bñ›ÂJ±¿t ò³‘벿†:¬pËG²¿÷“1>Ì^²¿v5yÊj²¿Ù@ºØ´²¿ÜŸ‹†ŒG±¿>Y1\±¿6;R}籿¿dãÁ»±¿ñ)Æ3h°¿ÀÌwð°¿ãã²ó6²¿*Çdqÿ‘±¿ÅæãÚP1²¿öëNwžx²¿Ëhä󊧮¿î\éE­¿lèf Ü®¿½vß1<®¿äòÒo¯¿¥Kÿ’T®¿Ž”-’v£¯¿ôPÛ†Q°¿Ãï¦[vˆ¯¿å`6†å¯¿˜÷8Ó„í¯¿ÛàDôk믿ía/°°¿'Ü+óV]¯¿36t³?P®¿†7kð¾*¯¿^ Pj°¿¦}sõ°¿Þ„€| ±¿Nñ¸¨±¿:’ËH¿±¿«´Å5>“­¿£êW:ž±¿nj ùœ»±¿ºøÛž ±±¿èy’tͰ¿ëˆ»z±¿ýÙ‘a±¿³z‡Û¡a±¿ÍÊö!o±¿Rewƒh±¿@aÃÓ+±¿½Œb¹¥±¿ñGQgî!±¿Â/õó¦"±¿Ì³’V|C±¿Ús™šo°¿R<¾½k°¿~Q‚þB°¿x]¿°¿Ô€AÒ§U°¿8©0¶°¿ÌC¦|ª®¿­§V_]°¿Mjh°±¿ÃFY¿™˜²¿T5AÔ}²¿E¡eÝ?²¿ äÙå[²¿a¦í_Yi²¿#Ù#Ô ±¿šwœ¢#¹°¿•»ÏñÑâ°¿THÞ9”±¿ 0,¾-°¿-Ó¾¹¯¿”XS²¿ÖXÂÚ;±¿kdWZF걿{3j¾J>²¿5~á•$Ï­¿uXá–¤¬¿.rOWw,®¿@øP¢­¿’Ìên‡®¿ÜÖž—Š­¿1–é—ˆ·®¿¢ ±ˆa¯¿V‚Åá̯®¿— uXᮿz©Ø˜×¯¿ †7k𮿠7U†q¯¿PãÞü†‰®¿ší }°Œ­¿ÎZ_®¿ï«r¡ò¯¿çUÕ{°¿œú@òΡ°¿Ê7Ûܘž°¿ÿ°¥GS±¿J´¬¿VF#ŸW<±¿ÿ°¥GS±¿ÚpXøQ±¿ãÉ¡f°¿¼­ôÚl¬°¿A·—4Fë°¿Œðœú°¿Ù²|]†ÿ°¿¢$$Ò6þ°¿%‘}eÁ°¿_—á?Ý@±¿¯#Ù@º°¿Ë™`8×°¿¾g$B#ذ¿7§’°¿•™Òú[°¿‹üú!6°¿ëâ6À[°¿¥-®ñ™ì¯¿¾ø¢=^H¯¿Ãõ(\­¿áaÚ7÷W¯¿­iÞqŠŽ°¿añd73²¿Ó1çû’±¿Ïø¾¸T¥±¿ ©¢x•µ±¿'÷;²¿Ãð1%’°¿²žZ}uU°¿¤l‘´}°¿hW!å'±¿õôøÃϯ¿Ü M¯¿aU½üN“±¿[%Xΰ¿F¶óýÔx±¿F•aÜ ¢±¿Év¾Ÿ/­¿½£9²ò«¿<½R–!Ž­¿Ö¬3¾/.­¿Ü¹0Ò‹Ú­¿"3¸<Ö¬¿©Ÿ7©0®¿ðß¼8ñÕ®¿dËòu®¿ÏIï_{®¿¯–;3Áp®¿¿Òùð,A®¿˜Ãî;†Ç®¿“9–wÕ®¿Ôž’sb­¿àòX32È­¿ SŸ\¯¿Pª}:3°¿¥hå^`V°¿}—R—Œc°¿?üü÷°¿æ?¤ß¾¬¿fO›sð°¿iŒÖQÕ±¿lBZcÐ ±¿Cëâ6°¿pµN\ŽW°¿h"lxz¥°¿8Ûܘž°°¿Û…æ:´°¿áE_Aš±°¿=Y¤‰w°¿êͨù*ù°¿(¸XQƒi°¿|Cá³up°¿ž)t^c—°¿„ äÙå[¯¿sFZ*o¯¿ÍwðЯ¿F&à×H°¿w£ù€@¯¿f¡Ø š®¿|^ñÔ#­¿9`W“§¬®¿^gCþ™A°¿ä×±Á±¿g s‚6±¿µ©ºG6W±¿<2V›ÿW±¿£êW:ž±¿ølìM°¿Øñ_ °¿¹‰Zš[!°¿_\ªÒ×°¿W•}W¯¿_DÛ1uW®¿]~p>±¿€ØÒ£©ž°¿‹LÃð±¿³Îø¾¸T±¿&¦ ±ú#¬¿ÈA 3mÿª¿CæÊ Úି šyrM¬¿¯Z™ðKý¬¿®~l’ñ«¿lîè¹­¿†« îê­¿o.2­¿^h®ÓHK­¿‚¬§V_]­¿ðûY,­¿ pzïÇ­¿¿F’ \­¿Çò®zÀ<¬¿*ý„³[ˬ¿!”÷q4G®¿n1?74e¯¿ü‹ 1“¨¯¿S%ÊÞRί¿c^G²°¿³™CR %«¿‚ã2nj°¿t%Õ?ˆ°¿»G6WÍs°¿jmÛkA¯¿ õôøÃ¯¿òÍ67¦'°¿çrƒ¡+°¿OÏ»± 0°¿o‚oš>;°¿léÑTO毿{®Gáz°¿UÛMðMÓ¯¿4ØÔyTü¯¿Éq§t°þ¯¿è‚ú–9]®¿‰'»™Ñ®¿Ež$]3ù®¿apÍý/¯¿U¯²¶)®¿·•^›­¿©¾ó‹¬¿]p¿˜­¿®¼äòw¯¿Óú[ðO±¿Ð*3¥õ·°¿µÂô½†à°¿>‘'I×°¿j¿µ%±¿Rò겯¿¶óýÔx鮿GãàÒ1¯¿¶/ î\°¿ëýF;nø­¿$ñòt®(­¿¬Å§ϰ¿"Þ:ÿvÙ¯¿×mPû­°¿IÕv|Ó°¿U£W°¿»¶·[ª¿Xp?à°¿°Œ Ýì°¿‘Ešx°¿iÄÌ>Q®¿ã©GÜÖ®¿K?ªa¯¿ô66;R}¯¿¤µûU€¯¿ Š·˜¯¿Òm‰\p¯¿á' ß÷¯¿HüŠ5\䮿”ÃÕ¯¿Aðøö®A¯¿MùT^­¿*ÙYôN­¿æ‘?xî­¿x%És}®¿6“o¶¹1­¿v3£ §¬¿*øD«¿Œ¾‚4cѬ¿m­/Úr®¿`™D½°¿³š®'º.°¿»›§:äf°¿?;àºbF°¿ÿêqßj°¿sL÷™®¿ÁgÓÀ­¿þÒ¢>É®¿1?74e§¯¿[D“7À¬¿°Y.ó«¿Ý%qVDM°¿Í‚9zü®¿½ÅÃ{°¿Ý%qVDM°¿Ï¤MÕ=²©¿ìm3⑨¿²GWéô1èLª¿Ù%ª·¶ª¿§Ï¸®˜©¿ƒú–9]«¿`:­Û ö«¿õ‚Osò"«¿¹à þ~1«¿²GWé۾Gýõ «¿ñ·=Ab»«¿Ž •bG«¿ÏM›q¢ª¿pÏ󧪿#½¨Ý¯¬¿y¯Z™ðK­¿'½o|í™­¿å^`V(Ò­¿u殿g)YNB©¿‡‡0~÷®¿è÷ý›¯¿É=]ݱخ¿)Ý^Ò­¿×j{¡€­¿ÃØBƒ®¿l"3¸<®¿¤30ò²&®¿ð‰Ð6®¿J&§v†©­¿,*ât’­®¿m7Á7MŸ­¿Žêt ë©­¿´ÿÖª­¿š †s 3¬¿Yû;Û£7¬¿"3¸<Ö¬¿ï!8ö¬¿†©-u׫¿¬±^‚«¿| €ñ ª¿o¸Üšt«¿E×…œO­¿?Qžy9°¿$&¨á[X¯¿léÑTO毿*¬ÿs˜¯¿,ºõš°¿]p¿˜­¿²Õ唀˜¬¿k» ¾iú¬¿«]Òƒ®¿Ržy9쾫¿í-å|±÷ª¿©ƒ¼LН¿¾4»î­¿É w¦(¯¿³³è ¸¯¿‰îY×h9¨¿Üšt["§¿qxµÜ™©¿9í)9'ö¨¿u?§ ?©¿¬ÿs˜//¨¿ôiý¡©¿B±4-±ª¿ˆ*üÞ¬©¿Ÿ·±Ù©¿ý-ø§T©¿‡Ä=–>t©¿sHj¡drª¿ýKR™bª¿JzZœ©¿üÇBt©¿$›:ª¿+‡ÙÎ÷«¿0º¼9¬¿éc> Й¬¿îx“ߢ“­¿¢œhW!å§¿ÒV%‘}­¿ƒjƒѯ­¿3ÀÙ²|­¿%»¶·«¿Ëø÷¬¿Qèy’¬¿K°8œùÕ¬¿‡ú]Øš­¬¿û]Øš­¼¬¿ ܺ›§:¬¿”ö_˜L­¿|dsÕeÄ­¿Ô‚}i®¿¡JÍh®¿àªÔ쮿ßmÞ8)¬¿¢ †7«¿:ùÙÈu«¿»šVðÛ¨¿gCþ™A|¨¿@PnÛ÷¨§¿(~Œ¹k ©¿§ ?¹nª¿ {Úá¯Éª¿Ánض(«¿¬Žé ¬¿ýh8en¦¿¼çÀr„ ¬¿ÓUø3¬¿1&ý½¬¿d¬6ÿ¯:ª¿ÙÍŒ~4œª¿ÎˆÒÞà «¿]¢zk`«¿žACÿ«¿0ÕÌZ H«¿õ÷RxÐ쪿7Ь5”Ú«¿l”õ›‰éª¿¡­Ü ̪¿í-å|±÷ª¿6?þÒ¢>©¿E>‘'©¿ªó¨ø¿#ª¿Þs`9Bª¿-ÌB;§Y¨¿©ù*ùØ]¨¿¢&ú|”§¿«’È>Ȳ¨¿´Ë·>¬7ª¿S@Úÿk­¿b k_@/¬¿»}V™)­¿z9ì¾cx¬¿å 0óü¬¿µá°4𣪿ò%Tpx©¿X;ŠsÔÑ©¿<ö³XŠä«¿âX·Ñ¨¿?;àºbF¨¿Î§ŽUJϬ¿}“EÖª¿ys¸V{Ø«¿ìÁ¤øø„¬¿´;¤ Ѥ¿ m6 B¤¿Êp<Ÿõ¦¿T^-w¦¿y¯Z™ðK¥¿OÌz1”¥¿1ì0&ý¥¿†r¢]…”§¿¸°n¼;2¦¿$D©¿ dv½S©¿«&ˆº@ª¿p^œøjG©¿N€aùóm©¿„H†[Ϩ¿4ºƒØ™B§¿ÿ®Ïœõ)§¿:ÏØ—l<¨¿öó娿ôú“øÜ ¦¿©Ÿ7©0¦¿Ü›ß0Ñ ¥¿Ò:ªš ꦿPR`L¨¿¤8GW«¿·)Õ"ª¿èLÚTÝ#«¿¸Üšt[ª¿ÅX¦_"Þª¿"T©Ù­¨¿K?ªa§¿¢DKO˧¿dT8‚Tª¿DkE›ãܦ¿ʦ\á]¦¿´éàfñª¿O•ï‰Ð¨¿…Ì•AµÁ©¿»(zàc°ª¿Š‘%s,ºM¸Wæ­¢¿y¯Z™ðK¥¿s¸V{Ø ¥¿ ÅVдĢ¿/¢í˜º+£¿‚ŽVµ¤£¿ºøÛž ±¥¿£#¹ü‡ô£¿õ  ­Ü£¿}гYõ¹¢¿Õ•Ïò<¸£¿ vöE™¥¿–´â Ÿ¥¿ÅS4¸­¥¿à ½úx裿ËI(}!䤿tì ×1¦¿Ù—l<Øb§¿Ý³®Ñr §¿ïb€D¨¿pÏ󧢿£ZD“7¨¿ûvþE¨¿Xtë5=(¨¿(Óhr1¦¿¨Š©ôΦ¿¦·? §¿£ x|{§¿ã4Dþ §¿*ât’­.§¿C®Ô³ ”§¿)ë7Ó…¨¿(›r…§¿'Ü+óV]§¿ 'iþ˜Ö¦¿†åÏ·K¥¿4Õ“ùGߤ¿AŸÈ“¤k¦¿¥ƒõó¥¿*øD£¿ÄÎ:¯±£¿¡GŒž[袿1'h“Ã'¥¿Å °rh‘¥¿Âmmáy©¨¿ÚUHùIµ§¿i©¼á´¨¿ÊPSé§¿°¹2¨6¨¿{¾f¹lt¦¿ `­Ú5!¥¿ù g³ês¥¿¥óáY‚Œ¨¿Ì|?q¥¿ðRê’qŒ¤¿ØÓMÖ¨¿1 íœf¦¿¡K8ô§¿l|&ûçi¨¿€ð¡DKŸ¿¶×‚ÞC ¿Ìï4™ñ¶¢¿RíÓñ˜¢¿‘~û:pž¿SäG ¿¾-Xª  ¿ó‘”ô0´¢¿Ìbbóqm ¿¸ õô ¿VZ ¦¿GN¶; ¿©ÞØ*Á¢¿ãý¸ýòÉ¢¿ 9¶ž!£¿µßÚ‰’ ¿· Íui¡¿Þ‘±Úü¿¢¿Õ³ ”÷q¤¿¸éÏ~¤ˆ¤¿ìL¡ó»¤¿ˆØÒ£©ž¿ù½Mö#¥¿î }°Œ ¥¿JVÕËï¤¿Æø0{Ùv¢¿ÄC?{£¿Ý^Ò­£¿Kê46¤¿Rb×öv£¿ƒèÚУ¿rúz¾f¹¤¿†p̲'¥¿t%Õ?ˆ¤¿Û…æ:´¤¿»S”K£¿ðÝzM¢¿€-¯\o›¡¿ˆ×õ v£¿„š!U¯¢¿£Xni5$ž¿gEÔDŸŸ¿×övKrÀž¿!Ky ¢¿î|?5^º¡¿™º+»`p¥¿jÂö“1>¤¿ú{)?Œm¤¿ý,–"ù¢¿ÎŽTßùE¡¿¯Ì[uª¡¿ ©¢x•µ¥¿XÇñC¥¡¿äôõ|Ír¡¿„d¸¥¿4õ»°5£¿w÷Ý—3£¿çÑ=륿GT¨n.þ–¿ áͼ¯š¿È•z„òž¿ç§8¼Zž¿±5[yÉÿ”¿J)èö’Ƙ¿oÅ1—¿êz¢ëž¿à€J•˜¿}%»¶—¿lí}ª ”¿’%s,流¿õhª'󞿘¦pzŸ¿æ¾÷7 ¿ŠUƒ0·{™¿+Àw›7Nš¿h†¬nõœ¿ N} yç ¿áÐ[<¼ç ¿]¦&ÁÒ ¿1Ñ O!—¿ífF?N¡¿Ë€³”,'¡¿ÖÆØ /¡¿ŸŠ‘%sœ¿ ²H˜‰"¤ngŸ¿—ÿ~û: ¿Ê52;‹ž¿™¼f¾ƒŸ¿³ïŠà+¡¿Ô›QóUò¡¿¿ò =E¡¿Àˆ¿)@̘‚…¿&«"ÜdTy¿X8IóÇ´†¿å—Á‘(”¿?sÖ§“•¿FzQ»_˜¿I„F°qý‹¿Gä»”ºdŒ¿iŒÖQÕ‘¿øŠn½¦—¿À{G 1—¿³Dg™E(–¿—®`ñd‡¿®Gáz®—¿˜Në6¨ý–¿­jI—¿Ìí^î“£¿t|´8c˜“¿be4òyÅ“¿5¸­-“¿ À;ùôØ–¿Y·ÑÞ’¿›sðLh’¿ÖÆØ /‘¿_)ËǺˆ¿/o×j‹¿)=ÓKŒeš¿=·Ð•T¿ÝÑÿr-Z¿ÆàaÚ7÷—¿›®'º.ü¿EcíïlŽ¿õ-sº,–¿]Þ®ÕN?1{ÙvÚ¿˜üOþî…¿°«ÉSVƒ¿¡½úxè»{?<-?p•'P¿ö|ÍrÙèl?î=\rÜ)}¿ù¿#*T7W?iSul®j?€'-\Vaƒ?ë§ÿ¬ùñg?”ùGߤi€¿©¾ó‹„¿gF?N™‹¿d!:ŽJ¿ìNwžxÎ&¿óæp­ö°g¿}w+Kt†¿Ã(ßÞ…¿xÐ캷"¿ã©GÜÖV?ïÈXmþ_…¿*T7Ûƒ¿¤‹¦³ƒ¿ë0 Xre¿„%Zòxz¿ùK‹ú$wx¿¬9@0G¿è…;Fzq¿ 4Ô($y¿{…÷ˆ¿Ý—3ÛŠ¿îziЧ‡¿^×/Ø Û†¿2®¸8*7q¿§"Æ‚l¿6wô¿\‹6¿KÈ=›U¿£V˜¾×l¿¨9y‘ ø…?=›UŸ«­x?w×Ùfp?§ÌÍ7¢{v¿£’°o'a?~£<órx¿WÎÞmUr¿Yni5$!9™¸Up¿Êß½£Æ„h¿ˆWÎÞi¿6wô¿\‹f?ú~j¼t“X?å™—Ãî‹¿d?‹¥H¾b¿st´ª%m¿é ¸çùƒ¿º ¾eNg¿õ×+,¸??·_>Y1\}¿°ŒØ'€’?„H†[Ïp?ºöôÂm? ‹Q×Úût?å€]Mž²š?ëZaúŽ?äù ¨7£–?¼·_>Y?v28J^“?½ÿ&Œ–?sœÛ„{ež?sÖ—?H§®|–ç©?Í¿´¨O¢?/N|µ£8§?¸¯çŒ(?+ö—Ý“‡¥?ÜõÒN§?lЗÞþ\¬?yvùÖ‡õ¦?M½në›?ìJËH½—?rˆ¸9• ?%ÊÞRΣ? -ëþ±¥?¹à þ~1£?Ô·Ì鲘˜?„Iññ Ù™?\WÌoŸ?G¢`Ƥ?$*T7›?¬TPQõ+?Mƒ¢y‹œ?¶ÚÃ^(`£?¡º¹øÛž ?oÓŸýH¡?¾÷7hŸ?pìÙs™¢?*äJ= B¡?>u¬Rz¦—?;©/K;5—?˜Ÿ—?êu‘BY˜?ƒ4cÑtv¢?`ºò¡?ËI(}!ä¤?c_²ñ`‹?£®µ÷©¢?mýôŸ5?®?1A ߺ©?9&‹ûL§?pïô¥·Ÿ?ÛàDôkë§?XŒºÖÞ§¢?|F"4‚£?w¦(—ÆŸ?j¼t“¤?NÒü1¥?À=ÏŸ6ª£?ë:TS’u¨?­¹Ä‘§?@léÑTO–?*6æuÄ!£?ä×±Á¡?I†[Ïž?8Hˆò-¤?äqs*¨?šë4ÒR¡?Ý 7àóð?Ê‹LÀ¯‘¤?”XS¦?Ž •b§?ãut\´? 6u¯?bhur†â²?—o}XoÔª?ϤMÕ=²±?qTn¢–æ²?5˜†á#¶?‚<»|ëò?³ðõµ.5ª?r„Ѭl§?þÖN”„D¢?ÆíñB:°?À燣±?0¹Qd­¡°?Á:Ž*¨? dv½S©?CqÇ›ü­?V€ï6oœ°?“«Xü¦°ª?©‡ht±«?5AÔ}R«?v3£ §°?$Diâ­? ô‰Q²?Y0ñGQg²?$ÑË(–[²?t'Ø›²?‹ÿ;¢Bu·??§ ?¹¶?4Õ“ùG߸?*Æù›P´?—®`ñd·?³êsµû¿?â镲 q¼?ðÝzMº?¯yUgµ´?1¬Zd»?@¼®_¸?q¹5鶸?ðÁk—6¶?æ@µm¹?Ø·“ˆð/º?¥t{Ic¸? Š·˜»?•ð„^»? a5–°6²?„¸röÎh·?És}¶?Ù•–‘zOµ?,ØF<Ù͸?ï8EGrù»?ú·Ë~Ýé¶?µ?QÙ°¾?àØ³ç2µ?ר%ª·¶?ïU+~©·?‡Ü 7àóÁ?N`:­Û¼?‡R{mÇÀ?›’¬ÃÑUº?ú ¨7£æ¿?ÖÇCßÝÊÀ?ùe0F$ Ã?ïV–è,³À?å+”ص¹?d@öz÷Ç·?j¾J>v´?ÇeÜÔ@ó½? ]lZ)À? ®¹£ÿå¾?àØ³ç25¹?gœ†¨ÂŸ¹?m6Vbž•¼?ðLh’XR¾?^ƒ¾ôöçº?V„aÀ’»?Sê’qŒd»?éH.ÿ!ý¾?ƒi>"¦¼?NÔÒÜ a½?æ¾÷7¼?²×»?Þ«¾?ˆŸÿ¼v½?0¹Qd­¡¸?´ü6ĸ?7TŒó7¡¸?» j¿µ¹?IœQ}¾?'iþ˜Ö¦½?ßÅûqûå¿?^+¡»$κ?Ô~k'JB¾?ßú°Þ¨Ä?ª™µöÁ?b/°ŒÀ?w£ù€@»?ÒSäqÁ?¿×—q¿?éEí~à¿?“HÛø½?5 Šæ,À?úa„ðhãÀ?REñ*k¿?äL¶ŸŒÁ?N™›oDÁ?»&¤5¸?žðœú@¾?§Y Ý!ż?’?xî=¼?ÔE eáë¿?˜Ùç1ÊÁ?Žuq à½?𢯠ÍXÂ?)éahurº?’“‰[1¼?æ@µm½?ÀBæÊ ÚÄ?8débÁ??üü÷àÃ?ºÛõÒÀ?R hÃ? l#öÃ?p À?¥JÆ?:è½Ã?ÙCûXÁo¿?yÈ”A½?q‘{ººc¹?5³–ÒþÁ?sž MÃ?øÆ{Â?Œó7¡¿?̵hÚV¿?úDž$]Á?mÊÞå"Â?]Á6âÉnÀ?à|zlËÀ?_}<ôÝ­À?pÏó§Â?= ByGÁ?é8h°Á?y$^žÎÁ?ì@1²dÂ?÷w¶Go¸Á?¯@ô¤L¾?£çº¾?ÐECÆ£T¾? 5?þÒ¢¾?»CŠMÂ?Ò°¨ˆÓÁ?Ñ’ÇÓòÃ?æv/÷ÉQÀ?bž•´âÂ?Vž@Ø)VÇ?! _BÅ?ûPŒ,™Ã?ZcÐ ¡ƒÀ?¨SÝ‹Ä?l’ñ+ÖÂ?Ë÷ŒDhÃ?þ`à¹÷pÁ?&«"ÜdTÃ?vmo·$Ä?Ó¾¹¿Â?ªa¿'Ö©Ä?G8-xÑWÄ?‹jQL¾?iTàdÂ?d¯w¼WÁ?yvùÖ‡õÀ??ŒmÃ?py¬äÄ?©Åä Â?Š;Þä·Ä?9ïÿã„ ¿?š–X|À?ZFê=•ÓÀ?jÞqŠŽäÆ?ÌFçüÇÃ?r‰#DÆ? @£té_Â?4hèŸàbÅ?ÙêrJ@LÆ?]øÁùÔ±È? ·_>Y1Æ?þÒ¢>ÉÂ?èô¼ Á?ìÜ´§!¾?v5yÊjÄ?LûæþêqÅ?|(Ñ’ÇÓÄ?t ]‰@õÁ?ÞɧǶ Â?âͼ¯ÊÃ?•,'¡ô…Ä?”4LkÓÂ?w÷Ý—3Ã?ã4Dþ Ã?» ¾iúìÄ?õiý¡™Ã?,g~5Ä?½¦¥hÃ? oB@¾Ä?LÁgÓÄ?p?à„Á?Ç,{ØœÁ?a©.àe†Á?¯zÀ9 Ã?ñ/‚ÆL¢È?0Ÿ¬®Æ?ÑÌ“k dÈ?ÓÞà “©Ä?÷Ç{ÕÊ„Ç?mWèƒelÈ?+¥gz‰±Ê?=¶eÀYJÈ?Ûø• kÄ?Í<¹¦@fÃ?zïÇí—Á?ªµ0 íœÆ?ÏL0œk˜Ç?zQ»_øÆ?p]1#¼=Ä?>“ýó4`Ä?*¬TPQõÅ? ȳ˷Æ?ÝE˜¢\Å?rÌ_!sÅ?ú'¸XQÅ?0JÐ_èÇ?Z›ÆöZÐÅ?ÀÐ#FÏ-Æ? 4Ô($™Å?¸v¢$$ÒÆ?Fì@1Æ?vàœ¥½Ã?éCÔ·ÌÃ?d¯w¼Ã?P3¤ŠâÃ?V ì1‘ÒÆ?Ò©+ŸåyÆ?¦D½ŒbÇ?J ,€)Å? ¨7£æ«Æ?LS8½‹Ë?Ü¡a1êZÉ?_aÁý€È?rÝ”òZ Å?g~5æÈ?–wÕæ!Ç?Â5wô¿\Ç?aŽ¿·éÅ?‹Þ©€{žÇ?È%Ž<YÈ?,cC7ûÇ?û·\ýØÈ?«Íÿ«ŽÈ?J±£q¨Ã?bƒ…“4Æ?¡i‰•ÑÈÅ?î—OV WÅ?ïÆ‚Â LÇ?B–É?Ì`ŒHZÆ?ªÔìV`È?Žs›p¯ÌÃ?£Ì™däÄ?rüPiÄÌÄ?Û0 ‚Ç·É?ȳ˷>¬Ç?Ýa™¹ÀÉ?¶…ç¥bcÆ?†¬nõœôÈ?ȵ¡bœ¿É?ÜcéCÔË?Iõ_” É?«x#óÈÆ?*äJ= BÅ?Ç.Q½5°Ã?Yùe0F$È?׆ŠqþÈ?È}«uârÈ?“áx>êÅ?Û¿²Ò¤Æ?ä „™¶Ç?7qr¿CQÈ?RóUò±»Æ?¨SÝÇ?xEð¿•ìÆ?C­iÞqŠÈ?®òÂNÇ?¹DkE›Ç?Å9êè¸Ç?_–vj.7È?¨V_]¨Ç?q;4,F]Å?±KXcÅ?®ºÕ”dÅ?ÅÈ’9–Å?²ô¡ ê[È?Éq§t°þÇ?¨Š©ôÎÈ?OçŠRB°Æ?"œÁß/È?=·Ð•Ì?·˜ŸšÊ?¶„|гYÉ?'L5³–Æ?¥½ÁÊ?óZ Ý%qÈ?r„ѬÈ?úÏšiÇ?{Oå´§äÈ?ìÀ9#J{É?ønóÆIaÈ?ùº ÿéÊ? 8€~ß¿É?x*àžçOÅ?;q9^èÇ?•'vŠUÇ?Þ“‡…ZÓÆ? !çýœÈ?ô¨ø¿#*Ê?ð0í›û«Ç?¨ŒŸqáÈ?|,}è‚úÄ?†­ÙÊKþÅ?[™ðKý¼Å?Ÿ·±É?Çô„%PÈ?ÄvüÊ?¨ÅàaÚ7Ç?]lZ)rÉ?+ß3¡Ê?K¯ÍÆJÌË?¶ö>U…Ê?aŠriüÆ?Ü ö[;QÆ?+4ËfÅ?ª x™a£È?×òAÏfÉ?¨ŒŸqáÈ?ø¥~ÞT¤Æ?}iÆ¢éÆ?_aÁý€È?›™EïÈ?´«ò“jÇ?‡O:‘`ªÇ?£>É6‘Ç?éòæp­öÈ?aŒHZÖÇ?Ýîå>9 È?¶»辜Ç?ÌëˆC6È?°âTkaÈ?J m6 Æ?®GázÆ?µ¤£Ì&Æ?ÈÍp>?Æ?N¸Wæ­ºÈ?j£:ÈzÈ? YÝê9É?«\¨ükyÇ?S ³³èÈ?#KæXÌ?ù‚0ºÊ?«“3w¼É?ÃbÔµö>Ç?T;ÃÔ–:Ê?lµ‡½PÀÈ?ÓÁú?‡ùÈ?"ŠÉ`æÇ?ý¢ý…É?ZÊû8šÉ?ãûâR•¶È?¯³!ÿÌ Ê?Ψù*ùØÉ?Ý—3ÛÆ?|~!<È?˜ËôKÄÇ?Ä?léÑTÇ?‰ÿ"hÌÈ?×ÜÑÿr-Ê?¿ñµg–È?òšWuV È?ç%è/ôÄ?b»{€îËÅ?^-wf‚Å?VGŽtFÈ?®›R^+¡Ç?Ö¨‡htÉ?÷vKrÀ®Æ?³Ñ9?ÅqÈ??­¢?4óÈ?—g)YNÊ?ìú»aÛÈ?S°ÆÙtÆ?ä1•ñïÅ?‹p“QeÅ? ~þ{ðÚÇ?­4)Ý^È?q©J[\ãÇ?Œöx!Æ?~p>u¬RÆ?sf»B,Ç?¾K©KÆ1È?rúz¾f¹Æ?. ø1æÆ?N`:­ÛÆ?ÅŒðö È?[%XÎüÆ?Õ%ãÉÇ?²/Ùx°ÅÆ?)–[Z ‰Ç?úDž$Ç?ºöôÂÅ?‰Ð6®Å?“ÅýG¦Å?˜Âƒf×½Å?¬„¹ÝËÇ?§%VF#ŸÇ?:w»^š"È?ZFê=•ÓÆ?$_ ¤Ä®Ç?™ðKý¼©Ê?}@ 3iSÉ?Ý a5–°È?y;ÂiÁ‹Æ?R||BvÞÈ?ù¢=^H‡Ç??þÒ¢>ÉÇ?’Y½ÃíÐÆ?‰_±†‹ÜÇ?3ˆìø/È?¶»辜Ç?Òà¶¶ð¼È?+Kt–Y„È?ž( ‰´Å?@mT§YÇ?›™EïÆ?õ¸oµN\Æ?¾Ý’°«Ç?LÝ•]0¸È?©¿^aÁýÆ?«zù&3Æ?ÇeÜÔ@óÃ?gCþ™A|Ä?4 çfÄ?šÐ$±¤ÜÅ?Ê`æ;øÅ?Ú¬ú\mÅÆ?³•—üOþÄ?Ì EºŸSÆ? À%W±Æ?Àé]¼·Ç?oò[t²Æ?!Z+ÚçÄ?`ãúw}Ä?F"4‚ëÃ?-“áx>Æ?;TS’u8Æ?¿rÞÿÇÅ?b/°ŒÄ?î{Ô_¯°Ä?ê46<Å?ô1èLÆ?ä0˜¿BæÄ?ǼŽ8dÅ?šÏ¹ÛõÄ? ~b¼æÅ?:vP‰ëÅ?kœMG7Å?HüŠ5\äÄ?Ïôc™~Å?µ¥òz0Å?0™ò!Ä?züÞ¦?ûÃ?>ù*Ä?~!<Ú8Ä?¬Ä<+iÅÅ?$·&Ý–ÈÅ?Ô'ž³Æ?ÛÂóR±1Å?N 4Ÿs·Å?ƒn/iŒÖÇ? U܏ů?jûWVš”Æ?Ì|?qÅ?Ð`ÿunÆ? YÝê9Å?-'¡ô…Å?<À“.«Ä?N ˆI¸Å?%æYI+¾Å?ß4}vÀuÅ?ô‡fž\SÆ??9 3Æ?ž@Ø)V Ä?×¾€^¸sÅ?dWZFê=Å?l¯½7†Ä?jÛ0 ‚Å?.rOWw,Æ? ^ô¤Å?ÞªëPMIÄ?Ï, PSËÂ?}¯!8.ãÂ?ï;†Ç~Ã?«²ïŠàÃ?ªIð†4*Ä??n¿|²bÄ?oÕu¨¦$Ã?UÃ?iäóЧÃ?3mÿÊJ“Â?<Ýyâ9[Â?ä£ÅÜÂ?ÍÈ w¦Â?ªb*ý„Ã?]ûzáÎÃ?0óüÄÃ?]߇ƒ„(Ã?[éµÙX‰Ã?´“ÁQòêÄ?Ujö@+0Ä?f,šÎNÄ?ðne‰Î2Ã?PÕé@ÖÃ?8 ¥+ØÂ? `­Ú5!Ã?ffffffÂ?§<ºÃ?iäóЧÃ?/¢í˜º+Ã?"Þ:ÿvÙÃ?vŠUƒ0·Ã?'‰%åîsÂ?Lý¼©H…Ã?çÁ=~Ã?ãÉ¡fÂ?CsFZ*Ã?BÏfÕçjÃ?"3¸<ÖÂ?"8öì¹Â?“áx>êÁ?'iþ˜Ö¦Á?PT6¬©,Â?»,D‡ÀÁ?ÖµÂô½Â?Ü ö[;QÂ?¤oÒ4(šÁ?aÃÓ+eÂ?FZ*oGÂ?l’ñ+ÖÂ?õfÔ|•Â?ÿÊJ“RÐÁ?.VÔ`†Á?±Äʦ\Á?4òyÅSÂ?Hˆò-$Â?t(CUL¥Á?Lÿ’T¦˜Á?…^Ÿ;Á? ÛÝtÁ?¸•^›•Â?¾¤1ZGUÁ?¬¬mŠÇEÁ?y±0DN_Á?Íui©¼Á?’#‘—Á?;ŪA˜Á?|eÞªëPÁ?÷s ò³‘Á?]lZ)rÁ?$Ò6þDeÁ?B˜Û½Ü'Á?K %vmoÁ?o×j{Á?ÙÐÍþ@¹Á?Éw)uÉ8Â?h®ÓHKåÁ?ìW\•Á?Ç TÆ¿Á?À]öëNwÂ?V]ûÂ?ŒƒKÇœgÂ?­Mc{-èÁ?Ð^»´Á?qËGRÒÃÀ?wKrÀ®&Á?ñº~ÁnÀ?zˆFwÁ?C’Y½ÃíÀ?PáRÁ?’á (ÔÁ?iR º½¤Á?‚ªÑ«JÁ?Ì'+†«Â?ì2ü§(Â?L3Ý뤾À?|îû¯sÁ?Æ‚”0Á? åD» )Á?ü§(ðNÂ?±Š72üÁ?~sõ¸oÁ?…]=ð1Â?Õ{*§=%Á?Ks+„ÕXÂ?~sõ¸oÁ?st´ª%Á?ƒÛÚÂóRÁ?‘ð½¿A{Á?¥¤‡¡ÕÉÁ?/j÷«ßÁ?.Ò¥Á?ޝ=³$@Á?žEïTÀ=Á?ÅŽÆ¡~Â?Þt_ÎlÁ?J²GWéÀ?·³¯Á?órØ}ÇðÀ?ÇóPoFÁ?,D‡À‘@Á?G©„'ôÀ? K< lÊÁ?¬ª—ßi2Á?Þ Z+ÚÁ?oÓŸýHÁ?2Çž=Á?Ï2‹PlÁ?0du«ç¤Á?Ý µ‰“Á?¨4bfŸÇÀ?aÁý€À?ýØ$?âWÀ?îCÞrõc¿?«zù&3À?h°©ó¨ø¿?½f¾ƒŸÀ?Ã×׺ÔÁ?÷x!ÂÀ?msczÂÁ? lÎÁ3¡Á?ÕÊ„_êçÁ?ö^|Ñ/À?ê”G7ÂÀ?ÑëOâs'À?;‹Þ©€{À?ÿ'LÍÂ?Ɖ¯vçÂ?É!âæT2Â?˜„ yÃ?²Ôz¿ÑŽÁ?.æç†¦ìÂ?Éå?¤Á?ÓiݵÁ?ÿ••&¥ Á?(ðN>=¶Á?cÒßKáÁ?7mÆiˆ*Â?2åCP5Â?Ç»#cµùÁ?Ùµ½Ý’Â?öCl°p’Â?sÖ§“ÅÁ?Òs ]‰@Á?'¢_[?ýÁ?cÔµö>UÁ?‹¥H¾HÁ?eáëk]jÂ?‹¥H¾HÁ?¨mÃ(Á?àFÊIÁ?Ë‚‰?Š:Á?ÇWËÁ?qçÂH/jÁ?°‘$W@Á? YÝê9Á?w£ù€@Á?c˜´ÉáÁ?î"LQ.Á?N˜0š•íÁ?ÆàaÚ7÷Á?ýú!6X8Á?ZÖýc!:Â?r„ѬlÁ? ¥ö"ÚŽÁ?@ RÁ?€D(bÁ?X}w+Á?t ]‰@õÁ?»íBsFÂ?ý¾óâÀ?K>v()À?+MJA·—À?Ònô1À? q¬‹ÛhÀ?²gÏejÀ?¶¶FãÀ?ˆ)‘D/Á?¥]PßÀ?D“7ÀÌÁ?ØØ%ª·Â? çfhÂ?ãÆ-æç†À?=Ô¶aÁ? ýL½nÀ?GW#»À?78ýÚúÃ?½§rÚSrÄ?ß¿yqâ«Ã?Cÿ+jÄ?†!rúz¾Â?qêé#Ä?ëÃz£V˜Â?î^î“£Ã?‚ŽVµ¤£Â?©2Œ»A´Â?’$W@¡Â?¬TPQõ+Ã?ü6ÄxÃ? ë©ÕWWÃ?xcAaP¦Ã?p™Óe1±Ã?Hú´ŠþÐÂ?JìÚÞnIÂ?ýÚúé?Ã?ÿ”*Qö–Â?[µkBZcÂ?Žèžu–Ã?½S÷<Â?¨“ÅýGÂ?¸u7OuÂ?Rb×övKÂ?{ù&3ÞÂ?þ¹hÈx”Â?]¡·xÂ?S=™ôMÂ?OqN`Â?’ê;¿(AÃ?¨ŒŸqáÂ?x*àžçOÃ?&S£’:Ã?'ÛÀ¨SÂ?ë7Ó…XÃ?pìÙs™Â?z¥,CëÂ?¥Kÿ’Â?9 {ÚáÁ?Õ$xCÂ?Úã…txÃ?&4I,)wÃ?y’tÍäÁ?s¼Ñ“2Á?ÝAìL¡Á?šž^)Á?«®C5%YÁ?QMIÖáèÀ?r2q« Â?õÙ×3Â?á%8õäÁ?Ä`þ ™+Ã? Ü¶ïQÃ?øTN{JÎÃ?rý»>sÖÁ?-™cyW=Â?æ–VCâÁ?biàG5ìÁ?K̳’V|Å?¥hå^`VÆ?¶»辜Å?Ä "RÓ.Æ?̶ÓÖˆ`Ä?â!ŒŸÆ½Å?¼=ùÄ?Ó¾¹¿Ä?R º½¤1Ä?"ÁT3k)Ä?±ˆa‡1éÃ?TýJçóÄ?¹8*7QKÅ?¹6TŒó7Å?IM»˜fºÅ?ÓNÍåCÅ?,ñ€²)WÄ?ç9"ߥÔÃ?Ùî@òÄ?cÓJ!KÄ?½ãÉåÃ?o+½6+Å?©¾ó‹Ä?1ïq¦ ÛÃ?ë©ÕWWÄ?JÔ >ÍÉÃ?Ð`ÿunÄ?ø‹Ù’UÄ?n÷rŸÄ?\ÊùbïÅÃ?QLÞ3ßÃ?ümOØîÄ?\>’’†Ä?üÄôûÄ?­¤ßPøÄ?‘}eÁÄÃ?<õHƒÛÚÄ?B@¾„ Ä?Ûü¿êÈ‘Ä?ßú°Þ¨Ä?vÂKpêÃ?>ê¯WXpÃ?šìŸ§ƒÄ?Q‚þBÅ?st´ª%Ã?0 ÃGÄ”Â?Ñ’ÇÓòÃ?8‡kµ‡½Â?»DõÖÀÂ?ûY,EÂ?*ÖT…Ã?)–[Z ‰Ã?âÊÙ;Ã?<õHƒÛÚÄ?wþEÐÄ?†K®bÅ?÷è ÷‘[Ã?×lå%ÿ“Ã?~÷æ7LÂ?Hmâä~Ã?ÚV³ÎøÆ?£°‹¢>È?âàI —Ç?ÁtZ·AíÇ?ì†m‹2Æ?h”.ýKRÇ?3‰zÁ§Å?&Ñ:ªšÆ? ÆÁ¥cÎÅ?õœô¾ñµÅ?Z!«[=Å?ÆíñB:Æ?eýfbºÇ?k'JB"Ç?Í#0ðÜÇ?ð3.ÉÆ?¥-®ñ™ìÅ?UL¥ŸpvÅ?2tì Æ?Î5ÌÐx"Æ?3Mg'ƒÅ?ä.ÂåÒÆ?’®™|³ÍÅ?G=D£;ˆÅ?(ðN>=¶Å?]lZ)rÅ?DiâÆ?4õºE`¬Å?Ÿ·±Å?·_>Y1\Å?jÛ0 ‚Å?"þaK¦Æ?«zù&3Æ?vß1<ö³Æ?%²²,˜Æ?Ù—l<ØbÅ?]¿`7l[Æ?“âã²Å?޲~31]Æ?9 {Úá¯Å?L¤4›ÇaÄ?, ü¨†ýÄ?LÃð1%Æ?‹yqÈÆ?Ò4(š°Ä?†§WÊ2Ä?§z2ÿè›Ä?ÖŽâutÄ?tBè KÄ?®+f„·Ã?vÄ!HÅ?œˆ~mýÄ?h$B#ظÄ?HG¬Å§Æ?çÿUGŽtÆ?>"¦DÇ?2WÕÅ?;QiÅ?1[²*ÂÃ?ÓÁú?‡ùÄ?íôƒºH¡È?{Ý"0Ö7Ê?/áÐ[<¼É?©‰>eÄÉ?EóùõÇ?(~Œ¹k É?q:ÉVÇ?uÄ]½ŠÈ?—㈞”Ç?Ð}9³]Ç?¡fHÅ«Æ?íÕÇCßÇ?ðHPüÈ?Å1w-É?DiâÊ?üÿ8aÂhÈ?ënžê›Ç?«ÏÕVì/Ç?q<ŸõfÈ?‡Þâá=È?äiù«<Ç?÷ʼUסÈ?ý,œ¤Ç?¿+‚ÿ­dÇ?¯™|³ÍÇ?ä²ó66Ç?ÈzjõÕÇ?ðMÓg\Ç?d±M*kÇ?h±ÉWÇ?$‘—5Ç?Ô‚}iÈ?”‚UõòÇ?ç«äcwÈ?ë8~¨4bÈ?‹LÃðÇ?È?Z/†r¢]Ç?lÎÁ3¡IÈ?TÇ*¥gzÇ?>ÍÉ‹LÀÅ?ŸZ}uU Æ?QôÀÇ`ÅÇ?…@.qäÈ? è…;FÆ?Õ"¢˜¼Å?jÂö“1>Æ?Þ3ßÁOÆ?™dä,ìÅ?öµ.5B?Å?¶jׄ´ÆÆ?Úʢ°‹Æ?ip[[x^Æ?™·ê:TSÈ?àLLbõÇ?Iô2Šå–È?÷x!ÂÆ?RóUò±»Æ?+TTýJÅ?t&mªÆ?Dý.lÍVÊ?,óV]‡jÌ?JaÞãLÌ?â#bJ$ÑË?é ¸çùÉ?F^ÖÄË?á ½þ$>É?P”i4¹Ê?*qãŠÉ?)³ 0,É?Ðïû7/NÈ?ÔdÆÛJ¯É?JíE´Ë?MÖ¨‡htË?A~6rÝ”Ì?H¾DÊ?Ñy]¢zÉ?íbšé^'É?÷ŽbÊ?s ßûÊ?fÚþ••&É?-¤ý°Ê?¬;Û¤¢É?{mÇÔ]É?ý‡ôÛ×É?‘·\ýØ$É?”1>Ì^¶É?MŸp]1É?ìhêwaÉ?Øc"¥ÙÈ?‰•ÑÈçÉ?{ØœƒgÊ?—¡Ÿ©×É?I»ÑÇ|Ê?ò'*ÖTÊ?ÌÐx"ˆóÈ?³_wºóÄÉ?£h[Í:É?Ø»?Þ«VÊ?œnÙ!þaÉ?ë9é}ãkÇ?îBsFZÈ?Ön»Ð\§É?TrNì¡}Ê?Àv0bŸÈ?,GÈ@žÇ?¤Q0c È?Èyÿ'LÈ? Й´©ºÇ?/m8, üÆ?¯ÍÆJ̳È?Ê?i¨QH2«É?"Ã*ÞÈ<Ê?'øŠn½Ê?H‰]ÛÛÉ?UDÝ É?M¾ÙæÆôÊ?ŒƒKÇœgÊ?PÅÈ’9Ê?Q¾ …ŒÌ?@1²dŽåË?äM~‹N–Ì?øˆ˜IôÊ?ѯ­Ÿþ³Ê?~Œ‰BÉ?Ð(]ú—¤Ê?söÎh«’Î?;äf¸ŸÐ?\’v5yÐ?óT‡Ü 7Ð?¦&ÁÒ¨Î?«®C5%YÏ?Ü:åÑÍ?2åCP5zÏ?¦òz0)Î?â¶ôhªÍ?.Ui‹k|Ì?4„c–= Î?Çž=—©Ï?ñòt®(%Ð? |E·^ÓÐ?óèžuÎ?ºÙ(·íÍ?Õ"¢˜¼Í?½8ñÕŽâÎ?V ÂÜîåÎ?ǂ L£Í?àØ³ç25Ï?Ïh«’È>Î?÷ZÐ{cÎ?©lXSYÎ?ÐÓ€AÒ§Í?†!YÀÎ?Eb‚¾…Í?L4HÁSÈÍ?Ö¬3¾/.Í?¨ŒÍ?Üóüi£Î?DiâÎ?ß,Õ¼Î?c ¬ãø¡Î? ú'¸XÍ?QúBÈyÿÍ?Xãl:¸Í?Ìz1”íÎ?|)êÍÌ?ãßg\8Î?‰²·”óÅÎ?Ñ$±¤Ü}Ì?½åêÇ&ùË?`#I®€Ì?]‰@õ"Í?zTüßÌ?åòÒo_Ë?NzßøÚ3Í?™Þ„€Ì?( ß÷oÌ?ž)t^c—Î?;ÈzjõÍ?´°§þšÎ?CsFZ*Í?ò ú'¸Ì?…?Û5xË?B_zûsÑÌ?ƒŠª_é|Ð?Zóã/-êÑ?$·&Ý–ÈÑ?%¯Î1 {Ñ?ü‹ 1“¨Ð?7á^™·êÐ?k›âqQ-Ð?YLl>® Ñ?®€¸«WÐ?ƒOsò"Ð?ÙZ_$´åÎ?¨(ðN>Ð?`=î[­Ñ?R<¾½kÑ?“V-Ò?·bÙ=yÐ?í¸áwÓ-Ð?q©;Ð?ê@ÖS«¯Ð?+Qö–r¾Ð?Á¨¤N@Ð?°­Ÿþ³æÐ?hA(ïãhÐ?‚Uõò;MÐ?¾Û¼qRÐ?Ñ@,›9$Ð?’?xî=Ð?xòé±-Ð?ººc±M*Ð?=Fyæå°Ï?\Uö]üÏ?»{€îË™Ð?+Àw›7NÐ?éd©õ~£Ð?M„ O¯”Ð?ÙëÝïÏ?—Tm7Á7Ð?ˆ»zÐ?6\-ËÐ?Ì:“6Ð?O?üüÍ?‘z6«>Ï?ޱ^‚SÐ?¡fHÅ«Ð?š{HøÞßÎ?ž#ò]J]Î?$¸‘²EÒÎ?•+¼ËE|Ï?Iò\߇ƒÎ?:”¡*¦ÒÍ? 6ªÓ¬Ï?ææÑ=ëÎ?úcZ›ÆöÎ?[wóT‡Ð?à‚lY¾.Ð?¸>¬7j…Ð?_&ŠºÏ?8IóÇ´6Ï?%–”»ÏñÍ?÷êã¡ïnÏ?ÕÎ0µ¥Ò?xak¶ò’Ó?LûæþêqÓ?€E~ýÓ?ôˆÑs ]Ò?–~TÃ~Ò?ðˆ ÕÍÅÑ?r£ÈZC©Ò?òB:<„ñÑ?ü‹ 1“¨Ñ?ÍV^ò?ùÐ?辜ٮÐÑ?PÂLÛ¿²Ò?Ñ>VðÛÓ?RÑXû;ÛÓ?‡4*p² Ò?Ä%ÇÒÁÑ?¥÷¯=³Ñ?¦B</OÒ?­‰¾¢[Ò?gòÍ67¦Ñ?7ú˜tÒ?Çkñ)Ò?Šº}åÑ?-`·îæÑ?—‘zOå´Ñ?õôøÃÏÑ?+¢&ú|”Ñ?RóUò±»Ñ?Ý}ŽgÑ?£>É6‘Ñ?@½5_%Ò?=)“ÚÑ?é(³ 0Ò?ì1‘ÒlÒ?ô7¡‡Ñ?ê—ˆ·Î¿Ñ?»Ò2Rï©Ñ?èÚÐ wÒ?h%­ø†ÂÑ?o¹ú±I~Ð?‰–<ž–Ñ?£ x|{×Ñ?ù*8Ò?ƒL2röÐ?'ÁÒ¨ÀÐ?Ù²|]†ÿÐ?™ ÇóPÑ?ñ[z4ÕÐ?w.Œô¢vÐ?6l±ÛgÑ?Â¥cÎ3öÐ?µ“ýØ? L§uÔØ?”i4¹Ù? â;þ Ú?.È–åëØ?¹8*7QKØ? 5?þÒ¢Ù?±‰Ì\àòØ?°âÊØ?-Ðîb€Ú?“‹1°ŽãÙ?$Ò6þDeÚ?,*ât’­Ù? cîZB>Ù?geû·\Ø?ãŽ7ù-:Ù?„€| Ý?&Ãñ|Ôß?>ÏŸ6ªÓß?B]¡Þ?3âÐ(]Ý?gÓÀÍÝ?¡¾eN—ÅÜ?zm6VbžÞ?þrÛ¾GÝ?‰~mýôŸÜ?ÑvLÝ•]Û?>”hÉÜ?$›:Þ?¼t“Vß?. ´¾Là?n¤l‘´Ý? †7kðÜ?«w¸Ý?–çÁÝY»Ý?6<½R–!Þ?.;Ä?léÜ?tµûËîÝ?7S!‰—Ý?C«“3wÝ?Y†8ÖÅmÝ?,œ¤ùÜ?ûY,EÝ?Ç/¼’ä¹Ü?;ÿvÙ¯;Ý?IœQ}Ü?7R¶HÚÜ?ÓL÷:©Ý?P0žACÝ?¤rµ4·Ý?”„DÚÆŸÝ?qxµÜ™Ü?Ï, PSËÜ?Yk(µÑÜ?vp°71$Þ?:3PÿÜ?DÀ!T©ÙÚ?EÕ¯t><Ü?ÁÉ6pêÜ?U†q7ˆÝ? 34žâÛ?\sGÿ˵Û?®€B=}Ü?Àϸp $Ý?ž #½¨ÝÛ?}<ôÝ­,Û?¹oµN\ŽÜ?Ó¼ãÉÛ? $ ˜À­Û?G>¯xê‘Ý?Ÿ9ëSŽÉÜ?ý‚ݰmQÝ?´8c˜´Ü?Ù š–XÜ?öE™ 2Û?ž™`8×0Ü?| €ñ ß?Ñ:ªš á?hwH1@"á?¾IÓ hà?þî5&ÄÞ?I×L¾Ùæß?ú¸6TŒóÞ?¹4J—~à?þ oÖà}ß? JÑʽÞ?þEИIÝ?—Æ/¼’äÞ?×¥Fègjà?ÕÊ„_êçà?‰~mýôŸá?ÐÒl#ß?8ÀÌwðß?D§çÝXPß?§Z ³ÐÎß?=HO‘CDà?ëW:ž%ß?d‘&Þà? (ÔÓGàß?qËGRÒÃß? ÂP¨§ß?¡Ö4ï8Eß?9}=_³\ß?ßÄœLÜÞ?üTˆeß? vöE™Þ? ®¹£ÿÞ?ÀA{õñÐß?‹RB°ª^ß?4Õ“ùGßß?ù‚0ºß?¤¥òv„ÓÞ?Nì¡}¬àÞ?(*ÖTß?N&nÄ@à?“EÖJß?N]ù,ÏÜ?ñÖù·Ë~Þ?ܵÛ.4ß?¾Þýñ^µß?”i4¹Þ?;‡ú]ØÝ?p–’å$Þ?IZÖýcß?z¦—ËôÝ?1Ò‹Úý*Ý?ÿÍ‹_íÞ?‚Ç·w úÝ?†!YÀÞ?ú´ŠþÐÌß?TVÓõD×Þ?Y†8ÖÅmß?†Xý†ß?ãósCSÞ?"Ä•³wÝ?DÛ1uWvÞ?-´sšÚß?LÃð±á? ý\¬á?,µÞïà?ïâý¸ýòÞ?6sHj¡dà?/lÍV^òß?(Óhr1á?aq8ó«9à?ª ãn­ß?ÌÔ$xCÞ?¢DKOËß?=dʇ êà?Å­já?“¬ÃÑU:â?^óªÎjà?RíÓñ˜à?ëÄåx"à?Ö5ZôPà?d–= lÎà?» ”Xà?¡÷Æœà?·`©.àeà?kb¯èVà?;nøÝtKà?}ÍrÙèà?µ5"à?Š)x ¹ß?þÒ¢>Éà?üU€ï6oß?X;ŠsÔÑß?:¯±KTà?2>Ì^¶à?5B?S¯[à?O›sðLà?õôøÃÏß?&åîs|´ß?ïãhެüß?û‘ 9¶à?>±N•ïà?öEB[Î¥Ý?»¹øÛž ß?[•DöAà?Ðïû7/Nà?Ú–?ßÞ?oFÍWÉÇÞ?Š?Š:sß?†1zn!à?Ù¯;ÝyâÞ?wIœQÞ?-]Á6âÉß?,)wŸã£Þ?É=]ݱØÞ?IŸVQà?n‰\pß?pënžêà?HPüs×ß?4,F]kïÞ?Äê0 XÞ?JíE´Sß?¿ÔÏ›ŠTß?¨QH2«wá?˜Ü(²ÖPá?öµ.5B¿à?þî5&ÄÝ?KXc'à?'3ÞVzmß?rúz¾f¹à?ÂøiÜß?€ï6oœß?¸4J—Ý?¨(ðN>ß? ]Þœà?Qèyá?6ZôPÛá?2çû’ß?mV}®¶bß?\qqTn¢ß? üáç¿à?~Œ¹k yà?4w¼Éoß?S<.ªEDà?¿ [³•à?—ÊÛN à?Ó×øLöß?hvÝ[‘˜ß?ê^'õeiß? ]Þß?žÒÁú?‡ß?¤P¾¾ÖÞ?%Ì´ý++ß?¤‹M+à?\•›¨¥ß?RD†U¼à?m±Ÿà?³˜Ø|\ß?¿ò =Eß?š`8×0Cß?ÁÆõïúLà?š–X|ß?ÇeÜÔ@óÜ?óX32È]Þ?×kzPPŠß?&6׆ à?Öà}U.TÞ?”Kã^IÞ?ší }°ŒÞ?겘Ø|\ß?3&c`Þ?8é´nƒÝ?/÷ÉQ€(ß?OË\å Þ?kž#ò]JÞ?an÷rŸà?d=µúÞ?pënžêß?…`U½üNß?¡Ø š–XÞ?ëR#ô3õÝ?ožê›áÞ?ÖÈ®´ŒÔÝ? ]lZ)„à?®›R^+!à?î#·&Ýß? GJ±Û?ÙÎ÷Sã¥Þ?aþ ™+ƒÝ?bMeQØEß?75Ð|ÎÝÝ?7n1?7Ý?sÚSrNìÛ?;5— uÝ?÷9>Zœ1ß?jßÜ_=îß?óŽSt$—à?[yÉÿäïÝ?«\¨ükyÝ?Õ=²¹jžÝ?XSYvQÞ?ÊÂ×׺ÔÞ?Ý µ‰“Ý?¼zÞ?À&kÔC4Þ?AºØ´RÞ?³–ÒþÞ?U¤ÂØBÝ?ƒѯ­ŸÝ?,ŸåypwÝ?Ç/¼’ä¹Ý?óv„Ó‚Ý?¦Óº jÝ?ˆôÛ×sÞ?AeüûŒ Þ?¶ºœ“Þ?¬á"÷tuÞ?æv/÷ÉQÝ?6x_• •Ý?CV·zNzÝ?­®€¸Þ?ìK6l±Ý?QÝ\ümOÛ?jØï‰uÜ?×L¾ÙæÆÝ?Y1\qÞ?H¾DÜ?apÍý/Ü?´Yõ¹ÚŠÜ?µ¥òz0Ý?Ñtv28JÜ?#„GG¬Û?É&pënÝ?:Ì—`Ü?®ð.ñÜ?ÚUHùIµÞ?uU ƒ‡Ý?î^î“£Þ?]¿ðJ’Ý?»ÕsÒûÆÜ?IÛø• Ü?÷<Ú¨NÝ?mŽs›p¯Û?ømˆñšWÞ?KPáÝ?ØG§®|–Ý? `­Ú5!Ù?œÁß/fKÜ?-Z€¶Õ¬Ú?bJ$ÑË(Ü?órØ}ÇðÚ?Útp³xÚ?·bÙ=yÙ?¤P¾¾ÖÚ?¦B</OÜ?¢ÎÜCÂÜ?å*¿)¬Ý?Ef.py¬Û?U†q7ˆÖÚ? 'LÍÊÚ?yËÕMòÛ?Œi¦{ÔÛ?ú™zÝ"Û?ÊÅXÇñÛ?9 ¥/„œÛ?]¢zk`Û?Ö:q9^Û?s‚69|ÒÚ?åîs|´8Û?eª`TR'Û?lyåzÛLÛ?æ²Ñ9?ÅÚ?" œlÛ?i©÷TNÜ?Ó×øLöÛ? eýfbÜ?Õ°ßëTÜ?ó‘”ô0´Ú?²KTo lÛ?Yk(µÑÚ?Sz¦—ËÛ?‡Ü 7àóÚ?YÃEîéêØ?³ ×ÜÑÙ?¦ ±ú# Û?úBÈyÿÜ?‘|%»Ù?C®Ô³ ”Ù?&o€™ïàÙ?/ùŸüÝ;Ú?ȳ˷>¬Ù?QØEÑÙ?¿œ3¢´Ú?ò™ìŸ§Ú?mrø¤Ú?R˜÷8Ó„Ü?y²›ýhÛ?0óüÄÜ?]S ³³èÚ?Cª(^emÚ?­5”Ú‹hÙ?ÍXä×Ú?£Ó0|DÙ?`ãúw}Û?BZcÐ ¡Ù?¡¢êW:Û?%ZòxZ~Ö?”‡…ZÓ¼Ù?óŽSt$—×?³¶)ÕØ?ÑËØÐÍ×?¢ U1•~×?-]Á6âÉÖ?XøQ û×?¸\ýØ$?Ù?'0ÖmÙ?Œô¢v¿ Ú?333333Ù?2 {½ûã×?î'c|˜½×?]j„~¦^Ù?%²²,˜Ø?XU/¿ÓdØ?`;±OÙ?`™D½Ø?ª*4ËfØ?½f¾ƒŸØ?-Ó¾¹×?_yž"‡Ø?»¶·[’Ø?Ü‚¥º€—Ø?aºÙØ? 4ØÔyTØ?áÐ[<¼çÙ?Ÿ`<ƒ†Ù?KÊÝçøÙ?0ðÜ{¸äÙ?ù.¥.Ç×?ëTùž‘Ù?­nõœô¾×?(F–̱Ø?¤ü¤Ú§ã×?­„î’8+Ö?#»Ò2RïÖ?Ñéy7Ø?µŒÔ{*§Ù?b¼æUÕÖ?›uÆ÷Å¥Ö?…|гYõÖ?!å'Õ>×?{÷Ç{ÕÊÖ?–$Ïõ}8Ö?Ív…>XÆ×?» ”X×?/£Xni5×?–B —8òÙ?ŸÉþyÙ?ø§T‰²Ù?dÎ3ö%Ø?5~á•$Ï×?{g´UIdÖ?ÕyTüߨ?mÇÔ]Ù×?c˜´ÉáØ?ZÊû8šÖ?75Ð|ÎØ?‹ßV*Ô?œ'¾ÚQ×?Z›ÆöZÐÔ?NA~6rÝÕ?›¯’ÝÕ?Ó¼ãÉÔ?Ýyâ9[@Ô?šë4ÒRÕ?»œ“pÖ?Kw×ÙÖ?“‹1°ŽãÖ?¹ü‡ôÛ×Ö?Ä "RÓ.Õ?$—ÿ~ûÔ?ö~£7üÖ?ëÅPN´«Õ?¯&OYM×Õ?ÖT…]Ö?ÞË}r Ö?•·#œ¼Õ?Ä\RµÝÖ?¸Î¿]öëÔ?kÕ® Ö?ƿϸp Ö?r4GV~Ö?a2U0*©Õ?E›ãÜ&ÜÕ?Û5x_•×?°;Ýyâ9×?•µMñ¸¨×?u¬Rz¦—×?«x#óÈÕ?¥N@aÃÖ?ÞtËñÕ?ŒgÐÐ?ÁÕ?£’°o'Õ?ƧÏ Ó?Ház®GÔ?· ÍuiÕ?Ïg@½5×?Kê46Ô?Žx²›ýÓ?|¹OŽDÔ?ó¬¤ßPÔ?ìÜ´§!Ô?ŠZš[!¬Ó?‹üú!6Õ? ®¹£ÿÔ?Øî<ñœÔ?bÚ7÷W×?¢ÑÄÎÖ? ByGs×?–Zï7ÚqÕ?É&pënÕ?ÿ–üSªÓ?ûå¶}Õ?h’XRî>Õ?“6U÷ÈæÖ?lÑ´­fÔ?ŽÍŽTßùÖ?²öw¶GoÒ?SìhêwÕ?é)rˆ¸Ò?Õ•Ïò<¸Ó?±3…ÎkìÒ?a4+Û‡¼Ò?q:ÉV—SÒ?óùõCÓ?îBsFZÔ?ºW•}WÔ?§$ëpt•Ô?å¶}úÔ?>\rÜ)Ó?¸#œ¼èÒ?ÓJ!KÕ?ìlÈ?3ˆÓ?ýÁÀsïáÓ?9&‹ûLÔ?£çºÔ?:õÔê«Ó?ãÝ‘±ÚüÓ?1îÑZÑÒ?vÄ!HÔ?Ë‚‰?Š:Ô?ÿ¯:r¤3Ô?¦~ÞT¤ÂÓ?Ýì”ÛöÓ?p]1#¼Õ?<Þä·èdÕ?%è/ôˆÑÕ?E½àÓœ¼Õ?Üg•™ÒúÒ?g+/ùŸüÔ?ˆdȱõ Ó?ØòÊõ¶™Ó?MÀ¯‘$Ó?‹ßV*¨Ñ?¥hå^`VÒ?›ÖtBÓ?1`ÉU,~Õ?Ù”+¼ËEÒ?t¶€ÐzøÑ?NÒü1­MÒ?„¸röÎhÒ?2•ñï3Ò?SÎ{/¾Ñ?Ë+×Ûf*Ó?9œùÕ Ó?†1zn¡Ò?¢Busñ·Õ?€šZ¶ÖÕ?Grùé·Õ?™Iô2ŠÓ?[éµÙX‰Ó?T¨n.þ¶Ñ?aª™µÓ?9c˜´ÉÓ?paÝxwdÕ?‹Ã™_ÍÒ?MØ~2ƇÕ?ÈDJ³yÑ?Úã…txÔ?T4¸­-Ñ?†§WÊ2Ò?äRìhÑ?{Ý"0Ö7Ñ?!¬ÆÖÆÐ?¯xê‘·Ñ?­nõœô¾Ò?ÀA{õñÐÒ?®ÕöBÓ?+ö—Ý“‡Ó?x ý,–Ñ?aûÉfÑ?»Ò2Rï©Ó?¯½7†Ò?j£:ÈzÒ?²×»?ÞÒ?&9`W“§Ò?œQ}>Ò?¸4J—Ò?¿ÔÏ›ŠTÑ?7ÿ¯:r¤Ò?„H†[ÏÒ?Ž¿·éÏÒ?5B?S¯[Ò?)Bêvö•Ò?ÉU,~SXÔ?ÁãÛ»Ô?ÙCûXÁoÔ?FzQ»_Ô?¿3‰Ñ?A}Ëœ.‹Ó?Lÿ’T¦˜Ñ?ÒŦ•B Ò?ZôPÛ†Ñ?35 ÞFÐ?ýI|îûÐ?eqÿ‘éÐÑ?ƒ÷U¹PùÓ?pÍý/×Ð?—á?Ý@Ð?RóUò±»Ð?µûU€ïÐ?‚åȳÐ?˃ô9DÐ?ú´ŠþÐÌÑ?–“PúBÈÑ?Ò?ŠÊ†5•EÐ?Ct 4Ò? â8ðj¹Ò?ÿ±GÔ?Û…æ:´Ñ?4iSulÔ?ˆWÎÞÐ?Ž!8öìÒ?½nëÐ?ä.ÂÑ?}ëÃz£VÐ?§çÝXPÐ?™b‚ŽVÏ?fj¼!Ð?mŒðœÑ?Ržy9ì¾Ñ?“[ìöÑ?²ºÕsÒ?PŒ,™cyÐ?‹¦³“ÁQÐ?û®þ·’Ò?¸xxÏåÐ?Rºô/IeÑ?¦ ÐÒÑ?Ž={.S“Ñ?–x@Ù”+Ñ?%ZòxZ~Ñ?†9A›>Ð?ɰŠ72Ñ?—nƒÀÑ?5ÒRy;ÂÑ?î\éEÑ?”¢•{Ñ?±k{»%9Ó?~6rÝ”òÒ?Xþ|[°TÓ?–Ïò<¸;Ó?QÚ|aÐ?m«Yg|Ò?jøÖwÐ?WêYÊûÐ?T1³ÏcÐ?{¡€í`Î?]ýØ$?âÏ?œ¦Ï¸®Ð?éFXTÄéÒ?{L¤4›Ï?ï%ÀÎ?Uø3¼YÏ?øÝtËñÏ?µ©ºG6WÏ?%«êåwÎ?Ð+žz¤ÁÐ?en¾ݳÐ?ˆ-y<-Ð?Ç›ü,Ó?&9`W“§Ò?ÌoB@Ó?¡÷ÆÑ?E>‘'Ñ? !çýœÎ?,»`pÍÑ?l#ö  Ñ?'Ø›6Ó?Ky ²Ð?Z.óSÓ?D÷¬k´Î?31]ˆÕÑ?*á ½þ$Î?¬Žé Ð?UûtŽÊ?Õ’weÊ?µ4·BXÉ?ûVëÄåÎ?¿ïß¼8ñÍ?,¨þA$Ï?}è‚ú–9Ë?„ƒ½‰!9Ë?0ÈбÇ?Q._x%Ë?w,¶IEcË? š]÷VÎ? eýfbÊ?àV*¨¨Î?¢*¦ÒO8Ç?DjÚÅ4ÓË?¥.ÇHöÆ?ŒðœúÈ?¯C5%Y‡Ç?b.äÜÆ?"ÿÌ >°Å?uU ƒ‡Ç?uÞɧÉ?~įXÃEÊ?((E+÷Ë?"‰^F±ÜÊ?-{ØœƒÇ?0×¢h[Ç?ñI'L5Ë?éµÙX‰yÈ?&ûçiÀ É?ÀÐ#FÏ-Ê?üߪ›É?öÒNïÈ?š•íCÞrÉ?DP5z5@Ç?Äëú»aÉ?‰Ð6®É?s.ÅUeßÉ?aãúw}æÈ?EÙ[ÊùbÉ? ˜£ÇïmÌ?^*6æuÄË?ØH„+ Ì?iÆ¢éìdÌ?¦¶ÔA^Ç?C’Y½ÃíÊ?ÐѪ–t”Ç?Š® ?8ŸÈ?iÇ ¿›nÇ?®ÙÊKþ'Å?6ÊúÍÄÆ?R)v4õÇ?¦^·ŒõË?‡Áü2WÆ?ûzáÎ…Å?G¢`ÆÆ?®­,Ç?VfJëo Æ?þÕã¾Õ:Å?S\Uö]È?W`ÈêVÏÇ?Ü ‹Qׯ?࢓¥ÖûË?ømˆñšWË?6‘™ \Ì?íHõ_”È?`áC‰–È?¹à þ~1Å?íDIH¤mÈ?âr¼Ñ“È?Ü,^, ‘Ë?Ó…XýÈ?ÊoÑÉRëË?¡MŸt"Å?‰A`åÐ"É?=E7§Ä?Ý´§!ªÆ?]‹ m5Å?Ze¦´þ–Ä?µ4·BXÃ?G¬Å§Å?w£ù€@Ç?@Ý@wòÇ?‘|%»È?µüÀUž@È?˜¦pzÅ?»_ønóÄ?ûY,Eò•È?ó!¨½Æ?éc> ЙÆ?l dv½Ç?(,ñ€²)Ç?!u;ûʃÆ?·$ìjòÆ?Ôšæ§èÄ?7‹ CäÆ?ªžÌ?ú&Ç?Ô+eâXÇ?‚WË™`Æ?TW>ËóàÆ?D¾K©KÆÉ?*8¼ "É?™Kª¶›àÉ? &áBÁÉ?6\äž®îÄ?Oyt#,*È?ðlÞpÅ?Äè¹…®DÆ?79|Ò‰Å?7‰A`åÐÂ?1%’èeÄ?^¡–±¡Å?è÷ý›É?mÆiˆ*üÃ?c('ÚUHÃ?ÂKpêÉÃ?ÖÇCßÝÊÄ?(ðN>=¶Ã?xEð¿•ìÂ?|ðÚ¥ ‡Å?Ww,¶IEÅ?â翯]Ä?x( ô‰<É?$0ðÜ{È?ë;¿(AÉ?ׄ´Æ Æ?ç½ÞýÅ?K®bñ›ÂÂ?M JÑÊÅ?k :!tÐÅ?{ŸªB±È?B°ª^~§Å?zýI|îÉ?þžX§Ê÷Â?ß3¡lÆ?\8’LÂ?ÇHö5CÄ?Å_Ñ­×Â?Šuª|ÏHÂ?¨äœØCûÀ?RGÇÕÈ®Â?lµ‡½PÀÄ?äôõ|ÍrÅ?a¦í_YiÆ?Y32È]„Å?DàH Á¦Â?ö—Ý“‡…Â?â#bJ$ÑÅ?I›ªÃ?Ä?Í[uª)Å?ß7¾öÌ’Ä?&¶ØíÃ?| ^Ä?áëk]j„Â?#ô3õºEÄ?ò°PkšwÄ?ÊÂ×׺Ä?oñðžËÃ?4e§ÔEÄ?Ã+IžëûÆ?ÀYJ–“PÆ?Ú‘aÇ?“Œœ…=íÆ?¸u7OuÂ?,F]kïSÅ?²+-#õžÂ?0fKVE¸Ã?Á”-’Â?§>¼sÀ?Õxé&1Â?CV¸å#Ã?Ê1YÜdÆ?‡D¤¦Á?qý¾óÀ?‹2d’‘Á?PãÞü†‰Â?¢_[?ýgÁ?ÓÜ a5–À?u;ûʃôÂ?¡õðe¢Â?“¬ÃÑUºÁ?ÀJÆ?A)Z¹˜Å?\qqTn¢Æ?ÜŸ‹†ŒGÃ?“Ã'H0Ã?f…"ÝÏ)À?eû·\ýÂ?F²G¨RÃ?À”Æ?vÿXˆÃ?p –ê^Æ?õ€yÈ”Á?úbïÅíÃ?™.Äê0À?Ë¢°‹¢Â?ùõCl°À?ÀÀ?Rðr¥ž½?ý…1zÀ?$0ðÜ{Â?ûÉf/Ã?;ªš ê>Ä?+4ËfÃ?K!KyÀ?Äê0 XÀ?Z/†r¢]Ã?øq4GV~Á?é Œ¼¬Á?. ø1æÂ? 毹2Â?“ªí&ø¦Á?ÂQòêÂ?Ý{¸ä¸SÀ?±4ð£öÁ?o~ÃDƒÂ?`sž MÂ?,žz¤ÁmÁ?’°o'áÁ?6‘™ \Ä?B²€ ܺÃ?½ý¹hÈxÄ?~÷æ7LÄ?Ujö@+0À?Ÿ9ëSŽÉÂ?9+¢&ú|À?Øñ_ Á?CUL¥ŸpÀ? 4ØÔy¼?‹6ǹM¸¿?eû·\ýÀ?¸Ë~ÝéÎÃ?õKÄ[çß¾?òaö²í´½?Q¾ …Œ¾?Ûúé?k~À?׈`\:¾?qªµ0 í¼?hÌ$êŸÀ?S=™ôMÀ?÷ʼUס¾?ô66;R}Ã?”½¥œ/öÂ?ÊmûõÃ?ú·Ë~ÝéÀ?‰´?QÙÀ?l¸ [–»?::ZÕ’À?`‰”fóÀ?~q©J[\Ã?\‘˜ †oÁ?Nïâý¸Ã?\ŽW zR¾?íñB:<„Á?£’:M¼?Œi¦{Ô¿?I¼<+J½?*n„EE¼?‰&PÄ"†¹?‘—5±¼?"o¹ú±IÀ?·|$%= Á?bôÜBW"Â?1@¢ ±À?Âmmáy©¼?8øÂdª`¼?iàG5ì÷À?ó9w»^š¾?šÍã0˜¿¾?7§’ À?CpìÙ¿?py¬ä¾?ÅÚÇ ~¿??ÆÜµ„¼?›:Š¿?ïU+~©¿?”Àæ<À?j£:Èz¾?ÙëÝïU¿?EóùÁ?#KæXÞUÁ? ËŸo Â? î\éÁ?òÍ67¦'¼?l¯½7†À?_yž"‡¼?ä0˜¿Bæ¾?µßÚ‰’¼?ù÷„¸?._x%É»?§#€›Å‹½? áÑÆkÁ?Öª]Òº?ÌFçüǹ?Q¾¾Ö¥º?c}“½?~įXÃEº?vQôÀǸ?ØœƒgB“¼?j3NCTá»?³Dg™E(º?­NÎPÜñÀ?UOæ}“À?ÁŠS­…Á?·³¯eĹ?4»î­¸?Åâ7…• ¶?7¢"N'¹?Ky ²¼?Z€¶Õ¬3¾?r‰#DÀ?FzQ»_½?©kí}ª ¹?ƒi>"¦¸?´€Ñåͽ?"5íbšéº?ÖµÂô½º?)ã¼?ioð…ÉT¹?‹Q×ÚûTµ?@ˆdȱõ´?SËÖú"¡¹?j½ßhÇ ·?ßlscz¶?†p̲'¹?)ÍæqÌ·?U·?Ž…A™F»?hñÿ¹?ò|Ô›Q»?%ÊÞRλ?ªc•Ò3½´?Ê7Ûܘž¸?š ñH¼<µ?¡ÚàDôk·?+*ÿZ^µ?–“PúBȱ?ÎOqxµ´?™„ y7¶? ÇóPoº?>u¬Rz¦³?ÖÇCßÝʲ?gEÔDŸ³?Ý@wòéµ?ˆ#³?³aMeQر?Yá&£Ê´?¦(—Æ/´?åÐ"Ûù~²?.’v£ù¸?Ð ¡ƒ.á¸?ÐECÆ£Tº?d[œ¥dµ?/ø4'/2µ?è3 ÞŒš¯?¶×‚ÞC´?î$"ü‹ µ?E/£Xni¹?¹Œ›h>·?œÞÅûqû¹?€&†§³?Y¿™˜.Ķ?!YÀnݱ?ñº~Án´?ÇמY ²?áy©Ø˜×±?C«“3¯? ]‰@õ²?÷WûVë´?«Îj=&¶?ÉüI‚¸?AJìÚÞnµ?™õb('Ú±?‘D/£Xn±?è1Ê3/‡µ?­‰¾¢[³?_ š]÷²?k=&Ršµ?‹Áôoî³?Z)r‰#³?÷s ò³‘³?—;3Áp®±?+Û‡¼åê³?×÷á !ʳ?p–’å$´?ú_®E в?ÙvÚŒ³? ×ÜÑÿ¶?—¡Ÿ©×µ?¯°à~À·?]øÁùÔ±¶?ü¦°RAE±?pìÙs™š´?±§þš¬±?ÉW)±³?M»˜fº×±?kdWZFê­? ýHV±?tÓfœ†¨²?Œu?T¶?ølìM°?‡‡0~÷®?ÂÚ;á%°?M.ÆÀ:²?gš°ýdŒ¯?7n1?74­?žï§ÆK7±?±RAEÕ¯°?9}=_³\®?˾+‚ÿ­´?Ü~ùdÅ´?ެü2#¶?/ÛN[#‚±?´Ø€q±?ê%Æ2ý©?()°¦°?µ¿³=²?\âÈ‘µ?…$³z‡³?Î3ö%¶?“Úl@°?°N]ù,³?@1²dŽ­?Ëõ¶™ ñ°?âvhXŒº®? ë©ÕWW­?߈îY×h©?ó<¸;k·­?†‘^ÔîW±?¿‚4cÑt²?ÚR ´?¨9y‘ ø±?‡À‘@ƒM­?Võò;Mf¬?Ç ¿›nÙ±?—Ž9ÏØ¯?ÎŒ~4œ2¯?üo%;6²?GJ±£q°?rjg˜ÚR¯?ÁÿV²c#°?ÏÚmšë¬? š]÷V°?÷@°?c^G²°?ümOØî®?‡P¥f°? ÛOÆø0³?Ðb)’¯²?$G:#/³?iâàI ³?PV W@¬?6çà™Ð$±?…²ðõµ.­?’?xî=°?Z/†r¢]­?h%­ø†Â§?}Ê1Yܬ?š´©ºG6¯?UûtVð£?0Ø Ûe¦?»€—6Ê¢?õ0´:9C¡?õ¢v¿ ð¥?an÷rŸ¥?¤¥òv„Ó¢?ðQ½Â‚«?y@Ù”+¼«?™cyW=`®?·}úë¦?õ-sº,&¦? `­Ú5!?ñaö²í¤?’릔×J¨?¿eN—ÅÄ®?ÎR²œ„ª?áaÚ7÷W¯?ðRê’qŒ¤?v“þ^ª?õ÷RxÐì¢?Û¦¶ÔA¦?Þ:ÿvÙ¯£?{jõÕU¢?…y3MØž?&¤à)ä¢?›!U¯²¦?6!­1脨?B•š=Ð ¬?Xtë5=(¨?„·! _¢?+*ÿZ^¡?üǙ&l§?UÂzýI¤?333333£?%ZòxZ~¨?õfÔ|•|¤?ºk ù g£?ØaLú{)¤?>w‚ý×¹¡?@j'÷£?~7ݲC¤?È}«uâr¤?Öª]Ò¢?l=C8fÙ£?ï9°!©?œß0Ñ §?¶Mñ¸¨©?zûsÑñ¨?#‡ˆ›SÉ ?¤¨3÷ð¥?…$³z‡Û¡?]¿`7l[¤?üs×ò¡?‰$zÅr›?ÔòWy¡?-íÔ\n0¤?èú>$D©?üǙ&lŸ?0ÕÌZ H›?ã‹öx!ž?1{ÙvÚ¡?zVÒŠo(œ? ߺñî˜?nøÝtË¡?Tn¢–æV ?Tœˆ~m?­Mc{-è¥?U¯²¶)¦?E½àÓœ¼¨?ßj¸¯ ?E½àÓœ¼ ?I¢—Q,·”?_(`;±Ÿ?ýÁÀsïá¢?‹¥H¾H©?»šrkÒ?»Õ”dž?èLÚTÝ#›?ª»² ל?øÃÏ^£?žz¤Ámm¡?ëåwšÌx£?mÇÔ]Ù£?9ÖÅm4€—?3d’‘³ ?»ïû™?æ®òž?g ר%š?ønóÆI‘?]¿ðJ’—?œ¡¸ãMž?¤8GW£?£W”†•?‹72üÁ?—pè-Þ“?( µ¦y—?U[rP’?SAEÕ¯tŽ?(Õ>˜?rÁüýb–?ÝAìL¡“?ìm3â‘ ?hÎú”c² ?ôÄs¶€Ð¢?ôS^-—?¯—¦pz—?í»"øßJ†?û\Äwb–?1í›û«Ç?èy’tͤ?‰—§sE)¡?|*§=%ç¤?ª‚QI€–?ð¦[vˆ ?K’çú>”?–vj.7š?çV«±„•?ýÁÀsïá’?¹à þ~1‹?–íCÞrõ“?4HÁSÈ•š?6׆Šqž?(º.üà|¢?$¶»辜?gEÔDŸ’?óÊõ¶™ ‘?Doñðž›?Ä "RÓ.–?‘îçäg“?®dÇF ž?rÞÿÇ –?F&à×H”?(*ÖT•?ò@d‘&Þ‘?@Û5x_•?ÆÞ‹/Úã•?™(Bêvö•?wòé±-“?áñí]ƒ¾”?¨ÄuŒ+.ž?ê<*þ?‹RB°ª^ž?±†‹ÜÓÕ?V™)­¿%?ó8 毙?1AG«Z’?]j„~¦^—?-Îæm’? |(ђǃ?9'öÐ>V?†TQ¼ÊÚ–?æË °ž?ñ+Öp‘{Š?§!ªðgxƒ?³\6:秈?ÞqŠŽäò?0…Ì•A…?7ê°Â-?ì2ü§(?L÷™?XWj1xˆ?¨ƒ¤O«˜?|a2U0*™?‰&PÄ"†?c%æYI+Ž?Y |E·Ž?C’Y½Ãíp?„.áÐ[<Œ?ïq¦ ÛO–?>&RšÍã ?ñ}q©J›?‚:åѰ ?íïlÞp?¶»辜™?·zNzßøŠ?úîV–è,“?!v¦Ðy?äôõ|Ír‰?Õ[[%X|?IeŠ9:Š?óâÄW;Š“?ì«an—?U¯²¶)ž?]5Ïù.•?9œùÕ ˆ?dT8‚„?Ww,¶IE“?…y3MØŽ?+¾¡ðÙ:ˆ?ž^)Ë—?¦{Ô—¥?B`åÐ"Û‰? :!tÐ%Œ?PáR)†? Íui©Œ?AØ)V ÂŒ?+Nµf¡?½nëˆ?ÕÏ›ŠT‹?"1ì0–?ÎR²œ„’?8-xÑW–?•›¨¥¹•?’ñ+Öp?סš’¬Ã‘?C®Ô³ ”‡?穹n?ü‹ 1“¨‡?>?Œml?â©iƒ?ìOâs'Ø?dv½S—?D¿¶~úÏz?+û®þ·b?0[wót?=Ú¨N‚?R臭ö”l?êÐéy7D?ެü2#‚?éd©õ~£}?YLl>® u?Êõî?~RíÓñ˜‘?ÖMò#~•?‹T[r€?(š°È¯?+¢&ú|”a¿ ß÷o^|?ùõCl?5$î±ô¡›?¸ê:TS’•? ܺ›§š?ß‹/Úã…„? ™žw“?w×Ùf€?ÙÎ÷S㥋?åѰ¨ˆƒ?Ç,{Ø|?¿¶~úÏš_?Í‚9zü~?sJ@LÂ…Œ?¹ü‡ôÛב?T;ÃÔ–:˜?_Ÿ;ÁŽ?7+1ÏJz?ìi‡¿&kt?'jin…°Š?ï<ñœ- „?ض(³A&y?Òo_Α?X;ŠsÔÑ?5&Ä\Rµ}?¨©ek}‘€??¨‹ÊÂw?"ýöuàœ?³z‡Û¡a?†àسç‚?Uð2ÃFy?{g´UId?'ú|”?‹Â.Šøˆ?{ò%T?óŽSt$—?{g´UIdo?!æ’ªí&ˆ?lìÕ[{?¯xê‘·…?ݵ„|гy?zUgµÀS¿²ºÕsr? ‹Q×Úû„?ÔòWy‘?Ž«‘]iY?€Õ‘#a¿]éEí~¿€(˜1kl?ýÙ‘aU¿%Ί¨‰>o¿d’‘³°§m?’xy:W”b??«Ì”Ößò>nü‰Ê†5…?±0DN_χ?hêu‹ÀX?í`Ä>d?àœ¥½Ág?´®Ñr ‡z¿5¸­-Qn?œŠT[‚?Ó–x@Ùt?>u¬Rz¦g?léÑTOæ_¿;s ßûk?ë˜Ü(‚?Q¤û9ù‰?¡Ÿ©×-“?ÐGqh„?ßlsczÂb?¿ò =EQ?uŽÙëÝ? ¨lXSYt?ý»>sÖ§\?eȱõ áˆ?bMeQØEq?-y<-?pe?|µ£8Gm?­ûÇBt\? Ž’Wçp?¾IÓ hn?JÑʽÀ¬p?Vbž•´â[?Èyÿ'Lh?@öz÷Ç{…?O#-•·#|?¾‰!9™¸…?~sõ¸o…?a2U0*©C¿Öª]Òz?ƒú–9]c?(¸XQƒix?Mjh°a?Ot]øÁùt¿.2¥B?æÎL0œkx?‡jJ²G‡?gd»Sd¿rÝ”òZ }¿·_>Y1\m¿¼êó)O¿½VBwIœu¿&6×†Š¿ì¿ÎM›qJ¿“ŠÆÚßÙ^¿?ÆÜµ„|p¿Ä•³wF[u?Íù†z?’]i©÷„?r„Ѭl_¿]Þ®Õ^¿Kiÿ¬…¿û®þ·’m¿ãÁ»}Vy?ˆž”I m?­k´衆?ê—ˆ·Î¿?Ǻ¸ðV?~p>u¬‚?f½ʉvE¿#0Ö70¹q?Î4aûÉO?}!ä¼ÿS¿órØ}Çðx¿P¨§ÀO¿È#¸‘²Er?ÁÿV²c#€?–x@Ù”+Œ?LàÖÝ¿ ì1‘Òl^¿«ZÒQfS¿¼è+H3f¿³B‘îçD¿Mjh°Q¿ü©ñÒMb@¿Šè÷ý›g¿Ä_“5ê!Z¿•~P)t?n…°KX[?½VBwIœu? IJ™Cr?òB:<„ñs¿/n£¼b?a2U0*©c¿'jin…°Z?CÁ”-b¿ŠsÔÑq5‚¿¾IÓ hn¿A ]Þ\?ض(³A&y?ÐDØðôJy¿ŽvÜð»é†¿QiÄÌ>¿V›ÿW9r¿›Ì^†¿¾IÓ hŽ¿0…Ì•A…¿È^ïþx¯z¿ Òo_Îi¿£V˜¾×\?ªek}‘Ðv¿sµ4·Bˆ¿êìdp”¼Š¿t—ÄY5¿šÏ¹ÛõÒ„¿7mÆiˆ*Œ¿8N ógj¿Cá³up°‡¿PqxµÜ‰¿ègêu‹Àˆ¿ÁŠS­…‰¿ºÙ(‡¿=Õ!7à ˆ¿Çº¸ð†¿`®E Ð¶Š¿ŠOÈÎÛˆ¿ÂÞÄœL|¿Ot]øÁù„¿/ܹ0Ò‹z¿_ÔîW¾{¿Lo.2Ž¿°® Ôbð€¿Qù×òÊõ†¿WÎÞmU‚¿À¯]Úpˆ¿ëŠáí‘¿ 4ØÔ‰¿pÐ^}<ô}¿¬r¡ò¯åu¿¢ †7k¿Ù[Êùbï•¿­C9Ñ®’¿0Ùx°Ån¿&¶Øí“¿^×/Ø Û–¿Ÿ[èJª¿ÂP¨§¿›:Šÿ;’¿ÖþÎöè ‡¿'õei§æ‚¿-@ÛjÖy¿dÉ˻ꑿ­mŽs›¿o l•`q˜¿>xí҆Ò¿@Þ«V&ü‚¿!¯“âãS¿øí¸áws¿gÕçj+ög¿kóÿª#GŠ¿’Z(™œÚy¿ßŒš¯’¿´äñ´üÀ…¿¨68ýÚŠ¿–]0¸æŽŽ¿”i4¹“¿U¯²¶)Ž¿— uXᆿÂ26t³?€¿ö²í´5"h¿™œÚ¦¶„¿w×Ùf¿•¸ŽqÅÅ‘¿€ÖüøK‹Š¿Á7MŸp¿eÂ/õó¦’¿k¶ò’ÿÉ¿—⪲Ë;Ū‘¿£9²òË¿ÇG‹3†9‘¿Öÿ9Ì—¿.ÿ!ýöu¿0Ÿ¬®¿,¸ðÀ’¿‰´?QÙ¿¸ õôˆ¿+£‘Ï+Ž¿<Ü ‹Q‡¿U„›Œ*È¿MeQØEÑ“¿nùHJzŠ¿°¶-Êl¿0™ò!¨Š¿˜ø£¨3÷¿;Èzjõ•¿L£uT5‘¿›sðLh’ˆ¿û:pΈ‚¿§]L3Ý딿ú ÒŒEÓ™¿.8ƒ¿_Ì–¿}þðóß“¿z‹‡÷˜¿ +TTýš¿–Ð]gE”¿ ­‡/•¿¿eN—ÅÄ–¿Ìí^î“£¿˜ËôKÄ‹¿¾öÌ’5…¿×kzPPŠ–¿¾‰!9™¸•¿j¥È%Žœ¿¡Ö4ï8E—¿£7ünŠ¿ a°ä*v¿¡0(Óhr¿9™¸U}¿<À“.«¿•c²¸ÿÈ„¿œmnLOX’¿Ü¹0Ò‹Ú¿®+f„·‘¿2Tqã“¿~ÿæÅ‰¯–¿UÞŽpZð’¿ð6oœæ¿ðÐïû‡¿>?Œm|¿9Ó„í'cŒ¿=Ô¶a”¿íõî÷ª•¿{ö\¦&‘¿@KW°x’¿ˆe3‡¤–¿;oc³#Õ‡¿|G 1—”¿mÞp¹•¿ôQF\•¿ÏÚmšë”¿ â8ðj¹“¿\<¼çÀr”¿ |E·^Ó“¿¡JÍh–¿w¾Ÿ/Ý”¿2rö´Ã¿ù»wÔ˜“¿ëZaúŽ¿û“øÜ ö¿r7ˆÖŠ6—¿e6È$#g‘¿¨ýÖN”„”¿ÿëÜ´§‘¿Ÿ;ÁþëÜ”¿üª\¨ük™¿Ýξò =•¿Ãe6\¿Zhç4 ´‹¿¡/½ý¹h˜¿ÜÖž—Š¿ßávhXŒš¿„GG¬Å—¿^¡–±¡›¿:3Pÿž¿BA)Z¹˜¿˜m§­Á˜¿)=ÓKŒeš¿õ¡ ê[政üûŒ B’¿#KæXŽ¿Èбƒš¿ÚÿkÕ®™¿Û$¶» ¿h–¨©e›¿È]„)Ê¥‘¿ŠXİØ„¿<ÁþëÜ´‰¿ ר%ª‡¿D6.6­”¿šÐ$±¤Ü¿ã5¯ê¬–¿BwIœQ“¿—¬Šp“Q•¿)¯•Ð]—¿çá¦Óš¿U2Tqã–¿OÜóüi“¿á (ÔÓG¿§ÌÍ7¢{†¿·µ…祒¿¬W‘ÑI˜¿YLüQÔ™¿ˆ.¨o™Ó•¿¹©æsî–¿ôÄs¶€Ðš¿‹qþ&"¿ýgÍ¿´˜¿žµÛ.4×™¿®fñ}q™¿—üSªD™¿:ÏØ—l<˜¿zûsÑñ˜¿e5]Ot]˜¿Ò¥Iš¿}”€F™¿;á%8õ”¿,+MJA·—¿®Ö‰Ëñ ”¿ %“S;Ô¿™¸U]›¿ŽÍŽTßù•¿Ô·Ì鲘¿³Dg™E(–¿¤«tw ™¿¦H¾H‰¿ŠriüÂ+™¿ú*ùØ] ”¿d\qqTn’¿ò`‹Ý>«œ¿Eð¿•ìØ ¿^L3Ý뤞¿cð0í›û›¿R}ç%蟿ì¾cxìg¡¿| Vœj-œ¿§/ú Òœ¿m­/Úrž¿íI`sž™¿èI™ÔЖ¿RÑXû;Û“¿GÉ«s Èž¿–¨©ek¿‡Šqþ&¢¿n‰\pŸ¿‹Q×ÚûT•¿Ç,{ØŒ¿HG¬Å§¿CV·zNz¿;R}ç%˜¿A~6rÝ”’¿Ñ=ë-š¿™ž°ÄÊ–¿îÑZÑæ˜¿ûå¶}š¿±i¥È%ž¿°u©ú™š¿êËÒNÍå–¿ ¯$y®ï“¿Öà}U.TŽ¿÷“1>Ì^–¿F³²}È›¿ûËîÉÃB¿Q§“lu™¿ÚÅ4Ó½Nš¿¡…Œ.ož¿èLÚTÝ“¿á¶¶ð¼Tœ¿wÜð»é–¿õ,å}¿»Ò2R臭¿ys¸V{Ø›¿¯w¼W­œ¿“ߢ“¥Ö›¿—6Êú¿vãÝ‘±Úœ¿d²¸ÿÈt˜¿„}›¿vþÓ ˜¿ÒQf`˜¿b.©Úž¿a¥‚Šª_™¿ö#EdXÅ›¿CpìÙs™¿¥Ÿpvk™œ¿y²›ýh ¿|ïoÐ^}œ¿NœÜïP˜¿¥hå^`–¿¯½7† ¿Zg|_\ª¢¿„+ PO¡¿ðO©eŸ¿—Æ/¼’ä¡¿EÕ¯t><£¿€FéÒŸ¿¨n.þ¶' ¿–B —8ò ¿~b¼æU¿÷ʼUסš¿¯zÀVðÛ“¿n‡†Å¨›¿D¥3û<–¿×ÜÑÿr¿Íù†š¿ølìMœ¿U1•~ÂÙ¿7TŒó7¡ ¿ÇÖ3„c–¿vþí²_wš¿)uÉ8F²—¿“Žr0›“¿l°p’晿º„Coñðž¿g¸ŸF ¿wIœQ¿\ AñcÌ¿ë˜Ü ¿¹oµN\Ž—¿ûëÜ ¿CW"Pýƒ ¿#ô3õºE ¿3ÂÛƒ ¿ZÙ>ä-WŸ¿ˆ»z ¿ û=±N•Ÿ¿‹ßV*¨ ¿š]÷V$& ¿&¦ ±ú#œ¿ /Á©$Ÿ¿ÍsD¾K©›¿UÙYœ¿£çº¡¿Ì|?q¿7ê°Â-Ÿ¿déCÔ·œ¿€fØñŸ¿LkÓØ^ ¢¿\Uö]üŸ¿REñ*k›¿´„Ö×™¿áFÊI»¡¿ølìM¤¿!®œ½3Ú¢¿ïW¾Û¼¡¿á@H0£¿rÝ”òZ ¥¿áî¬Ýv¡¡¿¨5Í;NÑ¡¿Ê‰vR~¢¿]kïSUh ¿Èì,z§ž¿M÷:©/K›¿­¢?4ó䢿d¬6ÿ¯:¢¿£’:M„¥¿rÞÿÇ £¿›©¾ó›¿NšEó–¿)%«êå—¿óªÎj=–¿ L§uÔž¿•-’v£™¿*;ý .R ¿Õ—¥šË¿”ƒÙ–Ÿ¿þ°Víš ¿wõ*2¢¿ãpæWs€ ¿`<ƒ†þ ž¿™¸U]›¿|,}è‚ú–¿¯Z™ðKýœ¿Q._x%¡¿‹Ã™_Í¢¿Â26t³? ¿—⪲¿’xy:W”¢¿sž M›¿­¡Ô^DÛ¡¿„„(_ÐB¢¿'÷;¢¿ "RÓ.¦¡¿vý‚ݰm¡¿“ `ÊÀ¡¿¬ãø¡Òˆ¡¿¸çùÓFu¢¿UÝ#›«æ¡¿Y‰yVÒŠŸ¿Ó0|DL¡¿’ê;¿(AŸ¿VñF摟¿ºM¸Wæ­¢¿Tã¥›Ä  ¿6çà™Ð$¡¿½në ¿Û§ã1•¡¿(îx“ߢ£¿IŸVÑš¡¿$Õw~Q‚ž¿ÄZ| €ñœ¿{Cr2q£¿h^»ï¦¿mw Ny¤¿%]3ùf›£¿Ov3£¥¿fI€šZ¶¦¿LkÓØ^£¿Ã9}=_£¿’?xî=¤¿“þ^ ¢¿ÍÈ w¦ ¿/ýKR™bž¿zÄè¹…®¤¿ä1•ñ÷±‚߆§¿Dioð…ɤ¿tϺFËž¿ègêu‹À˜¿£çºš¿n§­Á8˜¿ÒIØ·“ ¿üÂ+Ižë›¿ñ*k›âq¡¿µ¤£Ì& ¿eû·\ý ¿U…bÙÌ¡¿ò$éšÉ7£¿Ánض(³¡¿6>“ýó4 ¿%ušž¿D3O®)™¿¦¶ÔA^Ÿ¿²ºÕs¢¿X}w+£¿h+ømˆ¡¿T5AÔ}¢¿å™—ÃL©KÆ1’¿0JÐ_裿Åv÷Ý—£¿úDž$]£¿t^c—¨Þ¢¿ç6á^™·¢¿î`Ä>£¿ÝéÎÏÙ¢¿ÐѪ–t”£¿ƒR´r/0£¿±á镲 ¡¿Lþ'÷Ž¢¿àõ™³>å ¿GËjÛ ¿ßÞ5裿šë4ÒR¡¿ÅÅQ¹‰Z¢¿;«ö˜H¡¿kaÚ9Í¢¿Áüýb¶¤¿­¢?4ó䢿ëâ6À[ ¿äòÒoŸ¿Í鲘Ø|¤¿@øP¢%§¿$·&Ý–È¥¿Tàd¸¥¿qæWs€`¦¿æXÞU˜§¿¸éÏ~¤ˆ¤¿³}È[®~¤¿c|˜½l;¥¿iâàI£¿§Z ³ÐΡ¿ àfñba ¿ ¦–­õ¥¿˜‡LùT¥¿ˆc]ÜF¨¿Èì,z§¦¿‚âǘ»– ¿™Kª¶›à›¿ÓNï⿤ö{bš¿bž•´â¢¿²/Ùx°Åž¿L‰$zÅ¢¿û²´Ss¹¡¿v“þ^¢¿”i4¹£¿[aú^Cp¤¿IƒÛÚÂó¢¿ôzÄè¡¿7ünºe‡ ¿»µL†ãùœ¿: ûv¡¿?T1³Ï£¿hÊN?¨‹¤¿à÷o^œø¢¿‘îçäg£¿yÈ”A¥¿FÒnô1 ¿úÒÛŸ‹†¤¿Ü¸ÅüÜФ¿áñí]ƒ¾¤¿XSYvQ¤¿Bí·v¢$¤¿-xÑWf¤¿Mø¥~ÞT¤¿xñ~Ü~ù¤¿ä÷6ýÙ¤¿pìÙs™¢¿òšWuV ¤¿Åoò[t¢¿TS’u8º¢¿ÑË(–[Z¥¿Ž?QÙ°¦¢¿¸Ë~ÝéΣ¿Ñ[<¼çÀ¢¿µP29µ3¤¿¨PÝ\ü¥¿C —8ò@¤¿n/†È¡¿çÇ_ZÔ'¡¿âú}ÿ楿Õ’we¨¿®)ÙYô¦¿¦™îuR_¦¿©ƒ¼LЧ¿zûsÑñ¨¿6±ÀWt륿ÙÎ÷S㥿¹nJy­„¦¿\wóT‡Ü¤¿!‘¶ñ'*£¿Êk%t—Ä¡¿Ÿ ±Ý=@§¿Ͻ‡K¦¿Ó–x@©¿'Ü+óV]§¿˜Û½Ü'G¡¿Œ¿í Û¿…"ÝÏ)ÈŸ¿@/ܹ0Ò›¿QMIÖá袿¾ù  R ¿‚ŽVµ¤£¿ê”G7¢¢¿ŠÌ\àòX£¿8½‹÷ãö£¿:Ì—`¥¿ùÙÈuSÊ£¿`V(Òýœ¢¿žÒÁú?‡¡¿»·"1A Ÿ¿a8×0Cã¡¿_%» ”¤¿@Û5x_¥¿%è/ôˆÑ£¿6Ü,^¤¿Œ¢>+¦¿,ÒSä¡¿£’:M„¥¿†« î꥿0º¼9\«¥¿ë¬Øc"¥¿Ø›6㤿œÀtZ·A¥¿Ïg@½¥¿1[²*Â¥¿MùT^¥¿@¤ß¾œ£¿U¿Òù𤿃§Z£¿?æI£¿ì0&ý½¦¿…$³z‡£¿¼z¤¿ |(Ñ’Ç£¿0[wó¤¿Ç/¼’书¿¾0™*¥¿Ù@ºØ´¢¿ÿ‚¢¿ÕèÕ¥¡¦¿g s‚6©¿31]ˆÕ§¿:“6U§¿~£<ór¨¿…Ì•AµÁ©¿?§ ?¹¦¿µ5"—¦¿£±öw¶G§¿ Q…?Û¥¿s ßû¤¿‚<»|ëâ¿ 0,¾-¨¿ Ü¶ïQ§¿øßJvlª¿ÛàDôkë§¿°’ÝJ¢¿ õôøÃŸ¿¡MŸt"¡¿ž—Šy¿Ú©¹Ü`¨£¿Uð2ÃF¡¿}Ê1Yܤ¿t|´8c˜£¿b k_@/¤¿NŸt"Á¤¿oõœô¾ñ¥¿.ÎR²œ¤¿‡Q<¾½£¿üÞ¦?û‘¢¿4»î­ ¿n¿|²b¸¢¿£êW:ž¥¿‡Áü2W¦¿CæÊ ÚिyxÒÂe¥¿FAðøö¦¿²½ôÞ¢¿aR||Bv¦¿ 'iþ˜Ö¦¿{„ò>ަ¿dËòu¦¿j1x˜ö¥¿ªÒ×øL¦¿ÒYùe0¦¿LÝ•]0¸¦¿ì@1²d¦¿ 3iSu¤¿U‰²·”ó¥¿w+Kt–Y¤¿<š$–¤¿Ä\RµÝ§¿ŒÛho¤¿8, ü¨†¥¿J´¤¿ ûvþ¥¿Þt_Îl§¿j¼t“¦¿MÚTÝ#›£¿úîV–è,£¿Ø¼ª³Z`§¿“màÔ©¿Cr2q«¨¿j¡drjg¨¿.u׃I©¿Ó£©žÌ?ª¿›r…w¹ˆ§¿ò[t²Ôz§¿ŸÉþy¨¿¬ò“jŸ¦¿ŸÛ2à,¥¿+‡ÙÎ÷£¿Žx²›ý¨¿-ÌB;§Y¨¿Ìï4™ñ¶ª¿ž)t^c—¨¿’v5yÊ¢¿ TûtËóàT4¸­-¤¿qÇ›ü¤¿«Ñ«JC¥¿3MØ~2¦¿ëTùž‘¥¿$ð‡Ÿÿ¤¿™Õ;Ü £¿.äÜH¡¿à‚lY¾.£¿¡JÍh¦¿}$%= ­¦¿²ó66;R¥¿DÂ÷þí¥¿}uU ƒ§¿îÍo˜h¢¿~ŠãÀ«å¦¿½á´àE§¿Æjóÿª#§¿ž'ž³„¦¿Ùî ûr¦¿ \7¥¼¦¿T§YO­¦¿Êû8š#+§¿75Ð|Îݦ¿œ5x_• ¥¿ŽË¸©¦¿Üã5¯ê¤¿NA~6rݤ¿K?ªa§¿Äy8餿ta¤µû¥¿'…y3¥¿Œ-9(a¦¿ßÁO@¿§¿‘aod¦¿Ïd¤¿_|Ñ/¤£¿AƒMGŧ¿<‡2TÅTª¿U¿Òù¨¿’±Úü¿ê¨¿"rúz¾f©¿[š[!¬Æª¿{úüáç§¿uæ¾§¿@¼®_¨¿'ù¿b §¿½VBwIœ¥¿‚«<°S¤¿ñ*k›âq©¿®e2Ïg¨¿3¨68ýª¿Ð캷"1©¿{0)>>!£¿ ý\¬ ¿NÑ‘\þC¢¿fgÑ;pŸ¿rúz¾f¹¤¿wõ*2¢¿—¬Šp“Q¥¿ Íui©¤¿B–¥¿´Yõ¹ÚŠ¥¿0Ø Ûe¦¿HÀèòæp¥¿ò`‹Ý>«¤¿ ñ+Öp‘£¿w|ÓôÙ¡¿ $(~Œ¹£¿Üd:tz¦¿ ×£p= §¿#ÜdTÆ¥¿ïäÓc[¦¿ãÝ‘±Úü§¿É˻ꣿ¿+‚ÿ­d§¿œýrÛ¾§¿( µ¦y§¿¯Ñr ‡Ú¦¿…y3Mئ¿„çÞÃ%§¿Œó7¡§¿Ð}9³]¡§¿ûu§;O<§¿¦{Ô—¥¥¿ÌÑã÷¦¿3ÀÙ²|¥¿s-Z€¶¥¿h%­ø†Â§¿Zd;ßO¥¿žÏ€z3j¦¿‘{ººc±¥¿‘|%»¦¿[z4Õ“ù§¿||BvÞÆ¦¿±¢Ó0|¤¿’—5±ÀW¤¿>°ã¿@¨¿€~ß¿yqª¿Ù\5Ï©¿B_zûsѨ¿TªDÙ[Ê©¿¸­-#£¿¡/½ý¹h ¿¥žÐëO¢¿%”¾rÞŸ¿þî5&Ĥ¿ÿ±G¢¿Ù=yX¨5¥¿ÅªA˜Û½¤¿wIœQ¥¿ÛûTˆ¥¿AG«ZÒQ¦¿7ÆNx N¥¿À>:u峤¿³_wºóÄ£¿KPᢿ6WÍsD¾£¿Éª7U¦¿í S[ê §¿P«”žé¥¿·Œõ L¦¿yGsdå§¿=€E~ý£¿(·í{Ô_§¿ñcÌ]Kȧ¿xak¶ò’§¿!yvù¦¿½Þýñ¦¿T:Xÿç0§¿Â1Ëž6§¿o¼;2V›§¿ ÛÝt§¿Èa0…Ì¥¿¦·? §¿û®þ·’¥¿° ÍX4¥¿û“øÜ ö§¿ñKý¼©H¥¿EØðôJY¦¿ß—ª´¥¿”2©¡ À¦¿Dl°p’æ§¿î#·&ݦ¿z9ì¾cx¤¿ÓMbX9¤¿ˆc]ÜF¨¿ÚX‰yVÒª¿s¼Ñ“2©¿œá|~©¿„Iññ Ù©¿½5_%«¿(¸XQƒi¨¿ˆc]ÜF¨¿x]¿¨¿õiý¡™§¿¶¢Íqn¦¿• •-¯¤¿Ì&À°ü©¿ N} y稿èô¼ «¿æ²Ñ9?Å©¿‘cëÂ1£¿«Ì”Öß ¿wÙ¯;Ýy¢¿0*©ÐD ¿4¢´7øÂ¤¿•~ÂÙ­e¢¿‚TŠC¥¿¥„`U½¤¿OÌz1”¥¿ší }°Œ¥¿VGŽtF¦¿ÑË(–[Z¥¿RGÇÕÈ®¤¿“ߢ“¥Ö£¿Ú:8Ø›¢¿HùIµOÇ£¿Û¦¶ÔA¦¿=,Ôšæ§¿¤¨3÷𥿑aod¦¿[z4Õ“ù§¿¤ý°Ví¢¿åòÒo_§¿["œÁß§¿ EºŸS§¿ñœú@ò¦¿ iQŸä¦¿§’ Š§¿&©L1A§¿M.ÆÀ:ާ¿ïÊ.\s§¿ ~þ{ðÚ¥¿š\Œu§¿!®œ¥¿>#ÁÆ¥¿õœô¾ñµ§¿ÃbÔµö>¥¿*9'öÐ>¦¿5&Ä\Rµ¥¿µÀ)ͦ¿)Íæq̧¿Õ@ó9w»¦¿f¼­ôÚl¤¿ûʃô9¤¿~R›8¹§¿¨«;Û¤ª¿¨o™Óe1©¿g)YNB©¿ÒåÍáZí©¿žACÿ«¿5–°6ÆN¨¿ébÓJ!¨¿Ï£âÿލ¨¿4MØ~2Ƨ¿ÚŽ©»² ¦¿MÛ¿²Ò¤¤¿Ù¯;Ýyâ©¿~©Ÿ7©¨¿333333«¿ÿ“¿{G©¿[rPÂL£¿ÍXäן¿]øÁùÔ±¢¿ y7R¶ ¿ÛÝt_Τ¿vþí²_w¢¿D¤¦]L3¥¿ÛÝt_Τ¿ R)v4¥¿KqUÙw¥¿JìÚÞnI¦¿¿˜-Y¥¿šÏ¹ÛõÒ¤¿”‚Uõò£¿»¶·[¢¿þEИIÔ£¿ˆißÜ_=¦¿ì1‘Òl§¿ðiN^d¦¿ÊÝçøhq¦¿XÎüj¨¿ ^ô¤£¿þE>‘§¿lê<*þ§¿M†ãù ¨§¿Ú–?ߦ¿U2Tqã¦¿š´©ºG6§¿ =bôÜB§¿—9]›§¿PŒ,™cy§¿ePmp"ú¥¿=_³\6:§¿rûå“Ã¥¿¶¿³=zÃ¥¿%<¡×ŸÄ§¿¡„™¶e¥¿[³•—üO¦¿ê—ˆ·Î¿¥¿ësµû˦¿Y‰yVÒŠ§¿Õ@ó9w»¦¿^ Pj¤¿w+Kt–Y¤¿+j0 ç¿&rÁüýª¿šë4ÒR©¿%W±øM©¿áy©Ø˜×©¿Ò¥I*«¿jhwH1¨¿¥žÐë§¿BÌ%UÛM¨¿‡ýžX§Ê§¿ÃØBƒ¦¿ %“S;ä¿üÁÀsï©¿\Ëd8žÏ¨¿4õ»°5«¿*äJ= B©¿ñf ÞW墿ˆLùTž¿£7ün¢¿¯–;3Á ¿õfÔ|•¤¿+5{ ¢¿nLOX⥿‚ŽVµ¤£¤¿A ]Þ¤¿ÀnÝÍS¥¿—¡Ÿ©×¥¿FÏ-t%¥¿ìÁ¤øø„¤¿î•y«®£¿Ûmšë4¢¿Žèžu–£¿!±Ý=@÷¥¿µÀ)ͦ¿ pzïÇ¥¿Ñtv28J¦¿ìOâs'ا¿p%;6ñ¢¿+TTýJ§¿ Й´©º§¿5E€Ó»x§¿µ5"—¦¿Ý a5–°¦¿®)ÙYô¦¿q¬‹Ûh§¿¦Óº j§¿‘·\ýØ$§¿üá翯¥¿¶óýÔxé¦¿é Œ¼¬‰¥¿EºŸSŸ¥¿Tr3Ü€§¿µÆ B¥¿ÀzÜ·Z'¦¿ŒÜÓÕ‹¥¿?tA}Ëœ¦¿ö  Y2§¿e‹¤Ýèc¦¿'ÙêrJ@¤¿¬q6¤¿N™›oD§¿Aó9w»^ª¿6\䞮^d~$©¿w0bŸŠ©¿“6U÷Èæª¿"Þ:ÿvÙ§¿¨¨ú•·§¿SäG¨¿· b k§¿Ãõ(\Â¥¿œú@òΡ¤¿yæå°ûŽ©¿ c AJ¨¿ÝéÎÏÙª¿…^Ÿ;©¿&rÁüý¢¿e‹¤Ýècž¿G’ \…¢¿p^œøjG¡¿[ìöYe¦¤¿­„î’8+¢¿øŠn½¦¥¿=Gä»”º¤¿ÁÆõïú¤¿?8Ÿ:V)¥¿Ü:åÑ¥¿‰ëW\¥¿;9CqÇ›¤¿TTýJ磿'‡O:‘`¢¿ï©œö”œ£¿ Ö8›Ž¦¿2!æ’ªí¦¿Ç¹M¸W楿ÿ]Ÿ9ëS¦¿í‚Á5wô§¿³A&9 £¿øo^œøj§¿E¼uþí²§¿¬9@0G§¿,*ât’­¦¿ð‡Ÿÿ¼¦¿3TÅTú §¿ƒ¦%VF#§¿ý3ƒøÀާ¿°çk–ËF§¿¬„¹ÝË¥¿&Q/ø4'§¿ ¦}s¥¿sôø½¥¿mýôŸ5§¿\ªÒ×ø¤¿`Ç @¦¿st´ª¥¿’Ìên‡¦¿+2: û¦¿iÄÌ>Q¦¿­†Ä=–>¤¿›äGüŠ5¤¿j ùœ»]§¿£®µ÷©ª¿üo%;6©¿¤«tw ©¿ Ê4š\Œ©¿&rÁüýª¿J³yó§¿ÑÍþ@¹m§¿‹üú!6¨¿H¾D„§¿Õ—¥šË¥¿¢+Üò‘¤¿óèFXTÄ©¿!ɬÞáv¨¿í-å|±÷ª¿¨o™Óe1©¿ |(Ñ¢¿"©…’É©¿¼ "5íb¢¿9€~ß¿y¡¿Võò;Mf¤¿Gÿ˵h¢¿(F–̱¤¿ß‹/Úã…¤¿¦ÒO8»µ¤¿CqÇ›ü¥¿Å °rh‘¥¿#Ûù~j¼¤¿^ò?ù»w¤¿Eh׿£¿!èhUK:¢¿Â5wô¿\£¿• Uܸ¥¿­k´衦¿ß¿yqâ«¥¿ƒ0·{¹O¦¿œMG7‹§¿ì¢èÁ¢¿T:Xÿç0§¿¤µûU€§¿F´Swe§¿–u¦¿G>¯xꑦ¿¤Q“mি³•—üOþ¦¿š´©ºG6§¿ÞÈ<ò§¿™E(¶‚¦¥¿l@„¸rö¦¿;ŠsÔÑq¥¿3ÀÙ²|¥¿Ú‘a§¿\wóT‡Ü¤¿j1x˜ö¥¿þ oÖà}¥¿Ͻ‡K¦¿3ü§(𦿵ÑvLÝ¥¿¸#œ¼è£¿¹‰Zš[!¤¿ÁþëÜ´§¿V]ûª¿tys¸V{¨¿ÅËÓ¹¢”¨¿]‰@õ"©¿¦E}’;lª¿ýfbº«§¿” ¿Ð#F§¿ –\Åâ§¿úšå²Ñ9§¿_&Šº¥¿–Ð]gE¤¿÷!o¹ú±©¿o l•`q¨¿‰{,}肪¿MõdþÑ7©¿qXøQ £¿Ã€%W±ø¿¿a¢A ž¢¿f¤ÞS9í¡¿}—R—Œc¤¿è£Œ¸4¢¿’]i©÷¤¿Sz¦—ˤ¿i­hsœÛ¤¿ p¥¿ÖmPû­¥¿0œk˜¡ñ¤¿ð…ÉTÁ¨¤¿¬q6¤¿+Kt–Y„¢¿t|´8c˜£¿¬r¡ò¯å¥¿B]¤P¾¦¿Žuq ॿ"ÇÖ3„c¦¿uŽÙëݧ¿DÔ·Ì颿ïr߉Y§¿Ù"i7ú˜§¿ò[t²Ôz§¿‹v“¦¿ô噗æ¿MÀ¯‘$§¿ùgñ§¿¨þš¬Q§¿¹4~á•$§¿ê"…²ðõ¥¿º ¾eN§¿Ýa™¹À¥¿Öýc!:¦¿/N|µ£8§¿ `­Ú5!¥¿9· ÷ʼ¥¿àg\8’¥¿ÚæÆô„%¦¿@2:=樂g+/ùŸü¥¿ŒHZÖý£¿\Y¢³Ì"¤¿ pA¶,_§¿µ¿³=ª¿Ü~ùdŨ¿¹û-Ψ¿p^œøjG©¿ò˜ù~ª¿/lÍV^ò§¿ û=±N•§¿>ù*¨¿d”g^»§¿–´â Ÿ¥¿dT8‚¤¿5*p² Ü©¿U¡X6s¨¿CŒ×¼ª³ª¿Ö‹¡œhW©¿O"¿£¿Ýì”Ûö¿6#ƒÜE˜¢¿"Ã*ÞÈ<¢¿CSvúA]¤¿†:¬pËG¢¿¬ÉSVÓõ¤¿N¶;P§¤¿nÁR]Àˤ¿ô¥·?¥¿¦H¾H‰¥¿ÛÝt_Τ¿E¹4~ᕤ¿ß¿yq⣿¿a¢A ž¢¿‚ëßõ™£¿¶¿³=zÃ¥¿îyþ´Q¦¿›sôø¥¿¢©ÛÙW¦¿ù…W’<×§¿l”õ›‰é¢¿ïr߉Y§¿)Bêvö•§¿žxÎZ§¿PãÞü†‰¦¿Õ@ó9w»¦¿=Ô¶a§¿yÎZ§¿Ž •b§¿ÒŦ•B §¿dÍÈ w¦¿—®`ñd§¿—6Êú¥¿:êè¸Ù¥¿#KæXÞU§¿R h¥¿ºÙ(·¥¿Z!«[¥¿dXÅ™G¦¿ Ï.ßú°¦¿ª›‹¿¥¿Qkšwœ¢£¿]©gA(m±§¿78ýÚú©¿íHõ_”¨¿ŒºÖÞ§ª¨¿˜PÁá©¿dT8‚Tª¿,Ó/o§¿¬Zd;§¿@ÛjÖß§¿Œi¦{Ô§¿P:‘`ª™¥¿–Ð]gE¤¿´€ÑåÍ©¿¤#ÖâS¨¿m¡õðeª¿è…;Fz©¿iâàI£¿Aœ‡˜ž¿H¨REñ¢¿µá°4𣢿Ú(·í{¤¿ˆ jôj€¢¿ˆ…ZӼ㤿mæÔBɤ¿AØ)V ¤¿ô¥·?¥¿ÎÅßö‰¥¿š'×È줿Òබ𼤿«x#óȤ¿HøÞß ½¢¿_ÔîW¾£¿Šä+”Ø¥¿„F°qý»¦¿ì0&ý½¦¿rÁüýb¦¿¯zÀU¥¿èl¡õð¥¿X7Þ«¥¿t'Ø›¦¿-²ï§¦¿bg ר¥¿¡eÝ?¢£¿gd»S¤¿H‡‡0~§¿pê”G7ª¿õ_” ¿¨¿pZ𢯨¿BÎûÿ8©¿¦E}’;lª¿·Ï*3¥õ§¿:Yj½ßh§¿4ØÔyTü§¿'¢_[?ý§¿KY†8ÖÅ¥¿ÂLÛ¿²Ò¤¿ÚâŸÉþ©¿è¡¶ £¨¿òÓ¸7¿aª¿šë4ÒR©¿-'¡ô…£¿Íä›mnLŸ¿d"¥Ù<£¿Ø}ÇðØÏ¢¿Š§wñ~¤¿Õ”dŽ®¢¿­¤ßPø¤¿€»ì×Ø›6㤿x|{× /¥¿"QhY÷¥¿òC¥3û¤¿Ù²|]†ÿ¤¿Z.óS¤¿‡3¿š£¿ÒÂe6¤¿ £YÙ>䥿‰²·”óŦ¿‹RB°ª^¦¿¨ÿ¬ùñ—¦¿\Uö]ü§¿vOjM£¿—á?Ý@§¿v6äŸħ¿/áÐ[<¼§¿Ç/¼’书¿Å7>[§¿+TTýJ§¿0)>>!;§¿Í”Ö߀§¿XVš”‚n§¿\>’’†¦¿ßÁO@¿§¿Üôg?RD¦¿G>¯xꑦ¿x N} y§¿=e5]Ot¥¿2«w¸¦¿s¹ÁP‡¦¿²×»?Þ«¦¿¸®˜Þ¦¿, ‘Ó×ó¥¿íÖ2Žç£¿ÄwbÖ‹¡¤¿Ž±^‚S§¿CV·zNª¿âuý‚ݨ¿Á¨Sݨ¿IZÖýc©¿—ä€]Mžª¿–̱¼«¨¿õiý¡™§¿ûõ×+,¨¿ üÝ;jL¨¿…(_ÐB¦¿î²_wºó¤¿J m6 ª¿Z Ý!Å©¿yËÕMª¿ÍçÜíz©¿Vס𒬣¿âZía/ ¿´tÛˆ'£¿Øî û¢¿+Üò‘”¤¿Ø}ÇðØÏ¢¿0[wó¤¿òC¥3û¤¿¹§«;Û¤¿o.2¥¿ò Ùy›¥¿]ݱØ&¥¿B>èÙ¬ú¤¿›?Œ ¿IØÕä)£¿r3܀ϣ¿ò`‹Ý>«¤¿I+¾¡ðÙ¢¿:tzÞ¥¿ýöuàœ¥¿÷XúÐ¥¿èÙ¬ú\m¥¿d’‘³°§¥¿$ñòt®(¥¿‡À‘@ƒM¥¿7¨ýÖN”¤¿û!6X8I£¿sg&Î5¤¿–>tA}£¿31]ˆÕ§¿éI™ÔШ¿9(a¦í§¿Æ¡~¶¦¿OÎPÜñ&§¿>BÍ*Ч¿f¿ît牧¿•Öÿ9̧¿áìÖ2ާ¿Tÿ ’!Ǧ¿Ð¸p $ ¨¿[ìöYe¦¿Ô x'Ÿ¦¿/áÐ[<¼§¿X7Þ«¥¿Ö­ž“Þ7¦¿’‘³°§¦¿»a̦ۢ¿½:Ç€ìõ¦¿ª*4Ëf¦¿y ý\¤¿µùÕ‘#¥¿Ž<Y¤‰§¿|E·^Óƒª¿oÓŸýH©¿¨o™Óe1©¿"¥Ù<ƒ©¿6Vbž•´ª¿}ëÃz£V¨¿^€}têʧ¿—W®·ÍT¨¿‚âǘ»–¨¿—Çš‘A¦¿geû·\¥¿¯@ô¤Lª¿ã6À[ ©¿B•š=Ъ¿Õv|Óô©¿ÙYôN¤¿DP5z5@¡¿ˆ×õ v£¿ ‹Š8d£¿Xqªµ0 ¥¿«ÏÕVì/£¿‹Q×ÚûT¥¿Až]¾õa¥¿'…y3¥¿ù g³ês¥¿õ¢v¿ ð¥¿_›•˜g¥¿ìjò”Õt¥¿B³ëފĤ¿ÄC?{£¿ãÄW;Šs¤¿ýh8en¦¿4ºƒØ™B§¿‚9züÞ¦¿‚sF”ö§¿2g—o}¨¿o›©Ä£¿i㈵ø¨¿5–°6ÆN¨¿B™F“‹1¨¿y ²H§¿UPQõ+§¿¸w úÒÛ§¿œ…=íð§¿æë2ü§¨¿/lÍV^ò§¿×1®¸8*§¿Ž²~31]¨¿Ý•]0¸æ¦¿ëZaú¦¿‘(´¬û§¿ú™zÝ"0¦¿°Éõ¦¿nˆñšWu¦¿½ÅÃ{,§¿6•EaE§¿Üd:tz¦¿Öß—ª¤¿Á7MŸp¥¿ˆ0~÷æ§¿ûèԕϪ¿ÞU˜‡L©¿úœ»]/M©¿Ð YÝê©¿aÄ>#«¿rNì¡}¬¨¿‰–<ž–¨¿‹ßV*¨¨¿›V \⨿þðó߃צ¿s.ÅUeߥ¿£çºª¿´„Öש¿¥ØÑ8Ô謹úE ú =ª¿ñgx³é`ýŸÃ|¡¿ä²ó66£¿×Ý<Õ!7£¿rR˜÷8Ó¤¿‘ Îà2ZGUD¥¿­‡/EH¥¿x|{× /¥¿¢·xxÏ¥¿Ù[Êùb勵Rewƒh¥¿+ö—Ý“‡¥¿4J—þ%©¤¿iqÆ0'h£¿uÍä›mn¤¿ªÒ×øL¦¿IºfòÍ6§¿ÒâŒaNЦ¿©»² §¿­½OU¡¨¿!u;ûÊ£¿¢œhW!å§¿LqUÙwE¨¿ú™zÝ"¨¿c›T4Öþ¦¿‚®}½p§¿4MØ~2Ƨ¿}XoÔ Ó§¿=}þð󧿯zÀ§¿eãÁ»}¦¿Øô  ­¤¿º½¤1ZG¥¿õôøÃϧ¿°ŒØ'€ª¿Ñ”~P©¿Í[uª)©¿¯Ì[uª©¿» ”X«¿)ë7Ó…¨¿¸ õô¨¿ ‡Ú6Œ‚¨¿ò—õI²/Ùx°Å¦¿…ÐA—p西ÆPN´«ª¿ðĬC9©¿M¼Zœ©¿g Ü¶ï©¿czÂ(«¿辜ٮ¨¿—W®·ÍT¨¿üd訿,ÒSä©¿@2:=樂G«ZÒQ¦¿;‡ú]ت¿,ռ̰©¿ˆfž\S «¿pê”G7ª¿óuþÓ ¤¿í”Ûö=¢¿½qR˜÷8£¿ó¯å•ëm£¿¸Ì鲘ؤ¿½5_%£¿ðûY,¥¿_Cp\ÆM¥¿Â/õó¦"¥¿ÛûTˆ¥¿ÕÊ„_ê祿ö?ÀZ¥¿œ’“‰[¥¿ÞãL¶Ÿ¤¿óWÈ\T£¿õfÔ|•|¤¿[ÏŽY¦¿ÇÒÁú?§¿ø©*4˦¿Ú–?ߦ¿Hk :!t¨¿³Z!«£¿¶ƒû¨¿ŸÉþy0¨¿ñFæ‘?¨¿I/j÷«§¿4fõ‚§¿¼S”Kã§¿É>Ȳ`â§¿¸ õô¨¿Ûˆ'»™Ñ§¿ì1‘Òl§¿ô‡fž\S¨¿@gҦꦿÃ)só覿¥žÐë§¿ÈDJ³y¦¿[ ³ÐÎi¦¿/¥.ÇH¦¿Éå?¤ß¦¿ùé·¯§¿þµ¼r½m¦¿Q¼ÊÚ¦x¤¿‚Uõò;¥¿œ¥d9 ¥§¿Ð`ÿunª¿j¿µ%©¿ÎŽTßùE©¿ÔUø3¼©¿™J?áìÖª¿É¬Þávh¨¿ã4ô§¿j¡drjg¨¿nmáy©Ø¨¿ËǺ¸¦¿¦^·Œ¥¿³#Õw~Qª¿¶Øí³ÊL©¿ŽÊMÔÒܪ¿$cµùÕ©¿ÍËa÷ã¿•~ÂÙ­e¢¿û–9]£¿{ˆFw;£¿¿ [³•—¤¿‹lçû©ñ¢¿\wóT‡Ü¤¿Ÿ;Áþ뤿¯Ïœõ)Ǥ¿ R)v4¥¿¢·xxÏ¥¿£Ì™d䤿j'÷;¥¿Võò;Mf¤¿r3܀ϣ¿vmo·$¤¿?VðÛ㥿g|_\ªÒ¦¿Ùî ûr¦¿‘ñ(•ð„¦¿ÇF ^×/¨¿,ôÁ26t£¿ì÷Ä:U¾§¿‘Ešx¨¿ÍwðЧ¿Ú¬ú\mŦ¿sôø½M§¿ãúw}欧¿Ùz†p̲§¿ÉæªyŽÈ§¿&䃞ͪ§¿âeS®ð¦¿Tã¥›Ä ¨¿¶hÚV³¦¿äÜ&Ü+󦿣 x|{§¿ ®¹£ÿ¥¿[[x^*6¦¿Èì,z§¦¿ˆØÒ£©¦¿…ÏÖÁÁ¦¿¼?¦¿ï<ñœ- ¤¿¾0™*¥¿XVš”‚n§¿ãÂ,`ª¿SYvQô¨¿TŒó7¡©¿ÁŠS­…©¿¿¹¿zÜ·ª¿{Ý"0Ö7¨¿—qSͧ¿‰îY×h9¨¿“Qewƒ¨¿Üôg?RD¦¿/üà|êX¥¿;TS’u8ª¿—uÿXˆ©¿ª¹Ü`¨Ãª¿æZ´m«©¿^¾õa½Q£¿®,ÑYf¢¿–±¡›ý¢¿²ƒJ\Ǹ¢¿™~‰xëü£¿+•Ô h¢¿f¼­ôÚl¤¿œoD÷¬k¤¿£’:M¤¿«Íÿ«Ž¤¿¢$$Ò6þ¤¿?ÆÜµ„¤¿L¨àð‚ˆ¤¿¸#œ¼è£¿i¢¢¿ŠWYÛ£¿··[’v¥¿Ø»?Þ«V¦¿:覿j¼t“¦¿ÚUHùIµ§¿—Çš‘AÜõÒN§¿k+ö—Ý“§¿´«ò“j§¿I¹û-¦¿5Ïù.¥¦¿@Š:s §¿ÖþÎöè §¿]j„~¦^§¿à.ûu§;§¿û$wØDf¦¿E¼uþí²§¿ç<š$¦¿ÁUž@Ø)¦¿Å5| 릿E×…œO¥¿åÓc[œ¥¿˜ßi2ãm¥¿ÞïU+¦¿ûÌYŸrL¦¿,)wŸã£¥¿ $(~Œ¹£¿bƒ…“4¤¿»_øn󦿈‚S°Æ©¿øpÉq§t¨¿»&¤5¨¿Ð¹ÛõÒ©¿Êþy0Hª¿ƒlY¾.ç¿ÐšiQ§¿cì„—àÔ§¿KæXÞU¨¿È—PÁ᥿÷¯¬4)¥¿°WXp?à©¿Ní S[꨿ªó¨ø¿#ª¿Ñw·²Dg©¿ëR#ô3õ¢¿PqxµÜ¡¿†¯¯u©¢¿ö á˜eO¢¿æZ´m£¿ Oèõ'ñ¡¿u:õÔꣿÙYôN¤¿CV¸å£¿Üò‘”ô0¤¿z9ì¾cx¤¿=)“Ú¤¿š †s 3¤¿ùNÌz1”£¿;TS’u8¢¿r‹ù¹¡)£¿Ÿ;Áþ뤿…ÐA—p西r£ÈZC©¥¿@1²dŽ¥¿³x±0DN§¿ö—Ý“‡…¢¿Q¡º¹øÛ¦¿JbI¹û§¿ß£þz…§¿ ©¢x•µ¥¿Ðïû7/¦¿¹Æg²ž¦¿PnÛ÷¨¿¦¿žbÕ Ìí¦¿Ú¬ú\mŦ¿ºƒØ™B祿‘BYøúZ§¿Ë¿–W®·¥¿Ä[çß.û¥¿]¤P¾¾¦¿w¾Ÿ/ݤ¿P§<º¥¿f‚á\à ¥¿&â­óo—¥¿cšé^'õ¥¿ŸÛ2à,¥¿G°qý»>£¿ ܺ›§:¤¿IœQ}¦¿íI`sž©¿\­—㨿ûõ×+,¨¿"T©Ù­¨¿Þèc> Щ¿JíE´S§¿UÚâŸÉ¦¿Ž •b§¿Ó¾¹¿zܧ¿ñÖù·Ë~¥¿?rkÒm‰¤¿Å­j©¿3ßÁO¨¿¸É¨2Œ»©¿órØ}Çð¨¿OqN`¢¿\•›¨¥¡¿“ `ÊÀ¡¿¥/„œ÷ÿ¡¿Üñ&¿E'£¿åòw﨡¿†1zn¡£¿hyܵ£¿ã§qo~£¿*É:]¥£¿Œ 1“¨¤¿Ý µ‰“£¿È_ZÔ'¹£¿NzßøÚ3£¿ë©ÕWW¢¿Ð³Yõ¹Ú¢¿pìÙs™š¤¿X7Þ«¥¿·_>Y1\¥¿p +TT¥¿lèf Ü¦¿«&ˆº@¢¿UOæ}“¦¿k ¥ö"Ú¦¿ \7¥¼¦¿¢_[?ýg¥¿Ñ‘\þCú¥¿9DÜœJ¦¿AŸÈ“¤k¦¿°Éõ¦¿×]~p¦¿|HøÞß ¥¿J ,€)§¿ÅÈ’9–w¥¿»ðƒó©c¥¿g¶+ôÁ2¦¿z9ì¾cx¤¿J%<¡×Ÿ¤¿ÞãL¶Ÿ¤¿º½¤1ZG¥¿'2sËc¥¿ˆ NÒ¤¿Í"[AÓ¢¿<ö³XŠä£¿`” ¿Ð#¦¿ÎSr3ܨ¿ÅUeßÁ§¿ –\Åâ§¿÷æ7L4¨¿Êˆ @£t©¿«˜J?á즿( ß÷o¦¿c›T4Öþ¦¿œh>çn§¿ZKþ)¥¿÷‘[“nK¤¿2V›ÿW©¿õôøÃϧ¿ð÷‹Ù’U©¿æ¾÷7¨¿Êà(yuŽ¡¿2V›ÿW¡¿ ´¾L¡¿óUò±»@¡¿G’ \…¢¿¾¿A{õñ ¿ ŠcßO—n£¿¼³vۅ梿ADjÚÅ4£¿æ!S>U£¿ëR#ô3õ¢¿ÂÝY»íB£¿Ð(]ú—¤¢¿‚X6sHj¡¿WCâK¢¿h׿룿:tzÞ¥¿`°¶-ʤ¿éî:òϤ¿vR_–vj¦¿hwH1@¢¡¿ÖµÂô¥¿[[x^*6¦¿¥Û¹à ¦¿¼?Ƥ¿Ýξò =¥¿×Â,´sš¥¿šxxÒÂ¥¿í¸áwÓ¥¿|{× /½¥¿K;5— ¥¿n0Ôa…[¦¿Øô  ­¤¿ÿy0Hú¤¿ÂÂIš?¦¥¿ç7L4HÁ£¿Ëø÷¤¿Xp?िbƒ…“4¤¿ÖqüPiĤ¿¹‰Zš[!¤¿32È]„)¢¿v?T1£¿ÚÈuSÊk¥¿7qr¿CQ¨¿±‡ö±‚ߦ¿ÒŦ•B §¿Õ^DÛ1u§¿!!Ê´¨¿ŸwcAaP¦¿-\Va3À¥¿iÄÌ>Q¦¿GqŽ::®¦¿1 òn¤¿>–>tA}£¿.ÿ!ýöu¨¿J•({K9§¿¡º¹øÛž¨¿ZGUDݧ¿«uâr¼¡¿¯yUgµ ¿W_]¨Å ¿Š;Þä· ¿E¡eÝ?¢¿!!Ê´ ¿·˜Ÿš¢¿-±2ù¼¢¿|E·^Óƒ¢¿pìÙs™¢¿@†ŽT⢿ŒÙ’Un¢¿}гYõ¹¢¿?{ó&¢¿Ãƒf×½¡¿n¾ݳ®¡¿;8Ø›’£¿<š$–¤¿# Â¤R¤¿;V)=ÓK¤¿2 {½û㥿눻z¡¿J›ª{ds¥¿:’ËH¿¥¿½þ$>w‚¥¿,µÞo¤¿A ]Þ¤¿ùk¸È=¥¿+øDk¥¿*ÖT…¥¿­‡/EH¥¿œú@òΡ¤¿È—PÁ᥿3Œ»A´V¤¿{¡€í`¤¿Ç›ü,¥¿±n¼;2V£¿~7ݲ£¿èÙ¬ú¤¿ Š·˜§¿NÕ=²¹j¦¿ô噗æ¿;ÆG妿Å8 ¨¿B\9{g´¥¿$ñòt®(¥¿sôø½¥¿Þs`9B¦¿ž·±Ù‘ꣿøý›'¾¢¿>°ã¿@¨¿õ¸oµN\¦¿XS¨¿»·"1A §¿ÌH¿} ¿u«ç¤÷Ÿ¿vÁàš;úŸ¿sôø½MŸ¿’<×÷á ¡¿JbI¹ûŸ¿0fKVE¸¡¿2Ì Úäð¡¿9 {Úᯡ¿Ý@wòé¡¿ ]lZ)¢¿Æ§Ï ¡¿>ÏŸ6ªÓ¡¿^=ð1X¡¿ï_{fI ¿à|zlË ¿ {½ûã½¢¿‚åȳ£¿9*7QKs£¿ÙvÚŒ£¿np–’夿2kœMG ¿·Aí·v¢¤¿ˆ-=šêɤ¿çà™Ð$±¤¿kg{ô†£¿®~l’ñ£¿ŠÍǵ¡b¤¿c^G²¤¿õ¾ñµg–¤¿$Ó¡Óón¤¿Žèžu–£¿òë‡Ø`᤿겘Ø|\£¿…A™F“£¿Šuª|ÏH¤¿`Ë+×Ûf¢¿}“EÖ¢¿Pj’Ì¢¿„¸röÎh£¿…`U½üN£¿)x ¹RÏ¢¿áE_Aš± ¿&Ž<Y¤¡¿W•}Wÿ£¿OÎPÜñ&§¿wÐ}9³¥¿^.â;1륿Œ¢>+¦¿rjg˜ÚR§¿ Xr‹ß¤¿›–>tA}£¿öî÷ª•¡¿ñH¼<+¢¿‹N–Zï7¢¿ ܺ›§¢¿;‡ú]Ø¢¿=×÷á !¢¿WA tí  ¿K¦z2ÿ ¿?qý¾£¿—ýºÓ'¦¿—Ép<Ÿ¥¿è1Ê3/‡¥¿mp–’¥¿:“6U÷Ȧ¿ÞXP”i¤¿©‡ht±£¿cÓJ!K¤¿ô3õºE`¤¿}=_³\6¢¿kšwœ¢#¡¿¨§À~¦¿î²_wºó¤¿{ö\¦¦¿B’Y½Ã¥¿ðÝæ“œ¿óüÄô›¿3¸<ÖŒœ¿Gu:õÔš¿óÈ <÷ž¿<0€ð¡D›¿Ø‚ÞC ¿´Éá“N$ ¿Ǻ¸ ¿Øñ_  ¿žî<ñœ- ¿ÜõÒNŸ¿6[yÉÿ䟿[\ã3Ù?Ÿ¿ªæsîv¿xB¯?‰Ï¿¬Å9êè ¿‹Ã™_Í¢¿ƒ3øûÅl¡¿áFÊI»¡¿4Ûú`£¿ˆ»z¿B•š=Т¿ò$éšÉ7£¿p%;6ñ¢¿$cµùÕ¡¿I‚p¢¿ÿ< $}¢¿,~SX© ¢¿»(zàc°¢¿Pmp–¢¿j…é{ Á¡¿d=µú¢¿äL¶ŸŒ¡¿×ž—Š¡¿v5yÊj¢¿É¬Þávh ¿Ù^ zo ¡¿êX¥ôL/¡¿ûzáÎ…¡¿ k_@/Ü¡¿Z+Úç6¡¿m­/Úrž¿Ùƒkî蟿HÅÿQ¡¢¿´Yõ¹ÚŠ¥¿ÒÂe6¤¿ö™³>嘤¿DÞrõc“¤¿rûå“Ã¥¿a§X5s£¿m±Ÿ¢¿½qR˜÷8£¿0º¼9\£¿—¨ÞØ*¡¿›ÖtB ¿J›ª{ds¥¿q©J[\㣿Ýa™¹À¥¿¹ÿÈtèô¤¿¡ÚàDôk›¿z0Hú´š¿ˆñšWuV›¿,šÎNG™¿ÁgÓÀ¿°âÊ™¿ÐÔ뱞¿kð¾**Ÿ¿…ÏÖÁÁž¿nÃ(ßž¿yÎZŸ¿Î‹_í(ž¿¹Qd­¡Ôž¿ÐÒl#ž¿ 4ØÔyTœ¿û¯sÓfœ¿‹ßV* ¿ÃcÒßK¡¿ÿunڌӠ¿L£uT5¡¿ûå¶}¢¿”‚Uõò›¿%!‘¶ñ'¢¿›©¾ó‹¢¿´Ë·>¬7¢¿ò"ðk$¡¿”ú²´Ss¡¿#0Ö70¹¡¿Ã,´sš¢¿ÙYLü¡¿}ZEhæ¡¿Üx`¡¿‘í|?5^¢¿¨ŒŸqá ¿û“ýó4 ¿ßˆîY×h¡¿ ¹RÏ‚Pž¿# Â¤Rœ¿Awò鱿Mh’XRîž¿Œu?Tš¿wLÝ•]0˜¿c€D(b¡¿ÿÌ >°ãŸ¿^d~$¡¿U¡X6s ¿á³up°7‘¿’ñ+Öp‘¿ŒgÐÐ?‘¿ñÿ‚¿ž#ò]J]’¿2U0*©¿<ˆ)t^“¿³”,'¡”¿ÄC?{“¿^, ‘Óד¿`æ;ø‰“¿í}ª Ä’¿ÖqüPi”¿*T7Û“¿k›âqQ-’¿&¬±^’¿,F]kïS•¿Å.rOW—¿ÅçN°ÿ:—¿º¢”¬ª—¿BCÿ+š¿ÿXˆ#‘¿MØ~2Ƈ™¿æ=Î4aû™¿ª-u׃™¿°71$'—¿âåé\QJ˜¿YP”i4™¿Öã¾Õ:q™¿õ»°5[™¿½Ç™&l?™¿Ž•˜g%­˜¿þœ0a4›¿³–Òþ˜¿_yž"‡˜¿*ãßg\˜¿îx“ߢ“•¿`äeM,ð•¿U£W”–¿^ºI +—¿"1ì0–¿Eb‚¾…•¿ ÚäðI'’¿v‹ÀXßÀ”¿z‹‡÷˜¿‹5\äž®ž¿n…°KX›¿Z5Ñ磜¿^J]2Ž‘œ¿¶KKŸ¿È^ïþx¯š¿bg ט¿/ùŸüÝ;š¿9 Q…?Û¿KZñ …Ï–¿UDÝ •¿ßû´WŸ¿$ nk Ï›¿U÷Èæªyž¿»H¡,|}¿jßÜ_=î‹¿Œ 1“¨Œ¿„}‹¿hæÉ52‹¿ýˆ_±†‹Œ¿BÍ*ŠW‰¿Ó hÀ"¿<À“.«¿ ’>­¢¿/lÍV^ò¿GßÛôg¿ ²eùº ¿(Ö©ò=#‘¿Ô_¯°à~¿Á7MŸp¿æ‘?xî¿w0bŸŠ‘¿ZÊû8š“¿´up°71”¿Eóùõ“¿p>u¬Rz–¿i¬ýíÑ‹¿g~5–¿±ÁÂIš?–¿xšÌx[é•¿H3Mg'“¿ªc•Ò3½”¿™Iô2Š•¿A tí è•¿ÎPÜñ&¿•¿,)wŸã£•¿5¸­-!;oc³“¿(îx“ߢ“¿°«ÉSVÓ•¿Ön»Ð\§‘¿‘`ª™µ¿.â;1ëÅ¿Q»_øn“¿ŒòÌËa÷¿ñ+Öp‘{Š¿HÁSÈ•z–¿‰|—R—Œ“¿§@fgÑ;•¿K¯ÍÆJÌ“¿Û4¶×‚Þk¿hY÷…è`¿¥È%Ž‹¿¼“Om¿'…y3Mˆ¿€FéÒ¿$…¿›T4ÖþΆ¿Û4¶×‚Þ‹¿I›ª{dƒ¿€I*SÌA€¿Œðœú¿qäÈ"MŒ¿=·Ð•T¿+é~NAŽ¿¹¥Õ¸Çr?j’Ìê}?—Mõdþ?øí¸áws?#ÜdTÆ}?ÅÅQ¹‰Zz?ßøÚ3Kt?ù«<°s?VðÛ€?µÆ B}? IJ™Cr?Áªzù&ƒ?ÁäF‘µ†‚?2Tqãƒ?ÂøiÜ{?SçQñG„?oG8-xч?¨½ 4t?oÓŸýHy?Ï…‘^Ôîw?¯#Ù@ºx?“p! ?#½¨Ý¯¤?x%És}¦? Ùy›¡?ŠWYÛ£?_ Pj¢?ïW¾Û¼¡?¹Ä‘"£?â¦Óº ¢?Ð}9³]¡?K?ªaŸ?šEó¡?8öì¹LM¢?™Kª¶›à£?Á”-’¦?¿ÔÏ›ŠT ?ðN>=¶e ?øª• ¿ÔŸ?›sôø?†9A›>¡?ŒdP3¤š?éahur†¢?fŸÇ(ϼœ?©iÓœ?ÐÒl#žœ?ÐCmFA ?Î’Z(™œ?YÜd:tš?ñ}q©J›?äº)嵚?×¢h[Íš?&S£’š?>u¬Rz¦—?)Ð'ò$éš?ææÑ=ëš?/‡Ýw ?îw( ô‰œ?\8’L ?%@7n¡?÷åÌv…> ?ÃDƒ<…œ?1yÌ|Ÿ?çp­ö°¢?j½ßhÇ Ÿ?L©KÆ1’?tÐ%z‹—?¶÷©*4›?¸uÊ£›?NE*Œ-™?¦ï5Ç•?J$ÑË(–›?ƒÜE˜¢\š?lÏ, PS›?DŠM ˜?zˆFw›?¿îtç‰çœ?Ív…>XÆ–?MóŽSt$—?Å.rOW—?›çˆ|—R—?†§WÊ2¬?$Ó¡Óón°?¨á[X7Þ±?¢ñDçá¬?O?üü¯?:êè¸Ù­?œ6ã4D®?ÓõD×…°?ˆ‚S°®?lyåzÛL­?€ôMšE«?µ4·BX­?iÅ7>[¯?ŒºÖÞ§ª°?¸;k·]h²?e¦´þ–¬?ý»>sÖ§¬?Ðïû7/N¬?Étèô¼«?¼t“V®?»DõÖÀV©?I×L¾Ùæ®?]øÁùÔ±ª?ÝéÎÏÙª?´V´9Îmª?*ý„³[ˬ?ö á˜eOª?’±Úü¿ê¨?/3l”õ›©?³´Ss¹Á¨?_—á?Ý@©?Bêvö•©?Ùz†p̲§?žz¤Ámm©?,ÒSä©?d"¥Ù<«?-`·îæ©?-Ðîb€¬?'ÛÀ¨S®?•ص½Ý’¬?Çž=—©?³Z!««?Ø»?Þ«V®?D5%Y‡£«?ó9w»^šª?µ¤£Ì&¨?…™¶e¥©?“6U÷Èæª?Ê7Ûܘž¨?ã©GÜÖ¦?æ=Î4aû©?‚âǘ»–¨?¿}8gD©?ŽDÁŒ)¨?•AÕèÕ¨?êvö•é©?ïU+~©§?u=Ñuá§?+TTýJ§?…?Û5x§?îAÈ—Pµ?pµN\ŽW¸?Á­»yªCº? pzïǵ?¿¶~úÏš·?o·$ìj¶?~p>u¬¶?… £YÙ>¸?ÁR]ÀË ·?AG«ZÒQ¶?RóUò±»´?Ûú`¶? EºŸS·?îÑZÑæ¸?§ærƒ¡»?.É»š<µ?y!Âøµ?R›8¹ßµ?‡5•Eaµ?½ÅÃ{,·?± ØF´?«{dsÕ<·?[œ¥d9µ?²G¨REµ?IJ™CR µ?:è¶?ÀÍâÅ´?JÔ >Íɳ?E‚©fÖR´?ÌDR·³³?ûëÜ´? Šæ,ò³?4/‡Ýw ³?çfh<´?æ?¤ß¾´?Ë¡E¶óý´?ÏŽYö$´?.;Ä?léµ?fv‡·?ÕÎ0µ¥¶?‰ @£té³?£>+Nµ?ZòxZ~à¶?ׇõF­0µ?ÎOqxµ´?>>!;oc³?¬W‘ÑI´?cÐ ¡ƒ.µ?d@öz÷dz?À”Zº²?]¥»ël´?¡¸ãM~³?Ý–Ègð³?ëåwšÌx³?*q㊳?óT‡Ü 7´?¤8GW³?çá¦Ó²?3¨68ý²?‰xëüÛe³?7À[ A½?”£Q0cÀ?ë-z¨Á?:>Zœ1̽?Ð}9³]¡¿?¿3‰¾?pÑÉRëý¾?^ò?ù»wÀ?¢ ÀDˆ¿?SAEÕ¯t¾?‹3†9A›¼?`sž M¾?Ã.ŠøÀ?8 ¥+ØÀ?•~P)Â?Þv¡¹N#½?ú™zÝ"0¾?d¬6ÿ¯:¾?˜//À>:½?qvk™ Ç¿?V(Òýœ‚¼?Q¿ [³•¿?`L8 ½?ƒjƒѯ½?)øùÁ?Iõ_” Ã?/Šø¬Â?81$'·Â?àªÔìÂ?«w¸Ã?‰˜Iô2Â?á–¤¤‡Á?,bØaLúÁ?í›û«Ç}Á?ø¨¿^aÁÁ?Œ¹k ù Á?…\©gA(Á?˜õIî°Á?ó)‚ªÁ?´CV·zÂ?¥J”½¥œÁ?ËI(}!äÂ?ä-W?6ÉÃ?†®D úÃ?–~TÃ~Á?.ÿ!ýöuÂ?“x]Ã?B"mãOTÂ?œŒ*øÂ?b‚¾…uÁ?ìQ¸…ëÁ?‹‹£rµÂ?Ý ö_ç¦Á?lBZcÐ Á?Ò­£ª Â?¾ø¢=^HÁ?B°ª^~§Á?9ÔïÂÖlÁ?[\ã3Ù?Á?ïÿã„ £Á?ó-$`tÁ?ܼqR˜÷À?ä²ó66Á?e4òyÅSÁ?²¾É"Å?0/À>:uÇ?õ¡ ê[æÈ?LUÚâŸÅ?ó¬¤ßPÆ?Ñ‘\þCúÅ?( Ê4š\Æ?®ïÃAB”Ç?—z6«Æ?q­ö° Æ?ò•@JìÚÄ?иp $ Æ? ‘Ó×ó5Ç?LÃð1%È?µŒÔ{*§É?X«vMHÅ?„~¦^·Æ?€E~ýÆ?:Yj½ßhÅ?ÆOãÞüÆ?¡Ÿ©×-Å?• •-¯Æ?üã½jeÂÅ?¸Ë~ÝéÎÅ?Lÿ’T¦˜Å?£uT5AÆ?î“£Q0Å?Ü×sF”Ä?Ä\RµÝÅ?ØœƒgB“Ä?š#+¿ ÆÄ?E» )?©Ä?¹ß¡(Ð'Ä?ç6á^™·Ä?’Z(™œÄ?…A™F“Å?†q7ˆÖŠÄ?†W’<×÷Å?ÊõîÆ?ø¯=³$Æ?ê]¼·_Ä?¤8GWÅ?^ò?ù»wÆ?v¥e¤ÞSÅ?©|š“Å?j£:ÈzÄ?šÏ¹ÛõÄ?Œ‰B˺Å?7þDeÚÄ?QúBÈyÿÃ?]‡jJ²Å?jhwH1Ä?‹‡÷XŽÄ?ÈÓòWyÄ?HÞ9”¡*Ä?¦šË †Ä?ÇL¢^ðiÄ?þ)U¢ìÃ?¬©, »(Ä?žðœú@Ä?X}w+Ç?h%­ø†ÂÉ? 1—Tm7Ë?éCÔ·ÌÇ?l$ ÂPÈ? µ‰“ûÈ?`Í‚9zÈ?­KÐÏÔÉ?hÐÐ?ÁÅÈ?“EÖÈ?Šå–VCâÆ?¹ß¡(Ð'È?0/À>:uÉ?(bÃcÊ?!ä¼ÿÌ? ƈD¡eÇ?0¡‚à "È?ÿ‚:È?ò]J]2ŽÇ? Ý%qVDÉ?Št?§ Ç?bX9´ÈÈ?9 {ÚáÇ?ï«r¡òÇ?ˆÖŠ6ǹÇ?¥hå^`È?ê ¼“OÇ?±¨ˆÓI¶Æ?`“5ê!Ç?‹5\äž®Æ?±1¯#ÙÆ?ÛÝt_ÎÆ?U[rPÆ? )"Ã*ÞÆ?;Ä?léÑÆ?eÚʢ°Ç??tA}ËœÆ?Ä QºôÇ?#1ì0&É?º/g¶+È?¿Ö¥FègÆ?·³¯Ëóàî¬É? fLÁË?™€_#IÊ?·ð¼TlÌÇ?ÿ˵hÚÈ?¸¶J°8Ê?ÓNÍåCÉ?Ž•˜g%­È?xÑWf,È?UQ¼ÊÚ¦È?5— uXÉ?çû©ñÒMÈ?THÞ9”Ç?ÀêÈ‘ÎÀÈ?‹à+Ù±Ç?&¤à)È?Ì|?qÈ?¤P¾¾ÖÇ?<1ëÅPNÈ?ÀJÈ?Âj,amŒÇ?hyܵÇ?]†ÿtÈ?“S;ÃÔ–Ê?µkBZcÐÍ?1Ñ O!Ï?ò_ ¡Ë?¸uÊ£Ë?=œÀtZ·Ë?¹‡„ïýË?ݵ„|гÍ?iÄÌ>QÌ?¿ ƈD¡Ë?èKo.Ê?ê?k~ü¥Ë?ŒgÐÐ?Í? oÖà}UÎ? ´¾LÐ?c™~‰xëÊ?'iþ˜Ö¦Ë?IM»˜fºË?ÑZÑæ8Ë?mìM Í?ÃH/j÷«Ê?¼viÃaiÌ?©ƒ¼LŠË?Zd;ßOË?ŒÕæÿUË?² Ü:åË?]þCúíëÊ?•ŸTût<Ê?Ì™í }°Ê?þÒ¢>ÉÊ?˜4Fë¨jÊ?|ñE{¼Ê?Øñ_ Ê?V,~SX©Ê?ï¨1!æ’Ê?„çÞÃ%Ë?èKo.Ê?È~K‘|Ë?î\éEíÌ?ªºG6WÍË?‹O0žAÉ?ð¦[vˆÊ?ÍV^ò?ùË?UDÝ Ë?NÑ‘\þCÊ?•›¨¥¹É?Ô€AÒ§UÊ?é}ãkÏ,Ë?¸#œ¼èÉ?‘ ÎàïÉ?5ð£ö{Ê?Ä?léÑTÉ?*T7ÛÉ?¯@ô¤LÊ?c${„šÉ?õäCÊ?m±ŸÊ?¬ª—ßi2É?´"j¢ÏGÉ?oóÆIaÞÉ?¦*mqÏÌ?׆Šqþ&Ð?&Ãñ|ÔÐ?p&¦ ±úÍ?€óåØÍ?ÂO@¿ïÍ?aÅ©ÖÂ,Î?a§X5Ð?1·{¹OŽÎ??qý¾Í?„€| Ì?Eh׿Í?y>êͨÏ?Ç¡~¶fÐ?À°üù¶`Ñ?Q‚þBÍ?föyŒòÌÍ?=}þðóÍ?Æù›Pˆ€Í?Ìa÷ÃcÏ?t ò³‘ëÌ?ò`‹Ý>«Î?&Ãñ|ÔÍ?\vˆØÒÍ? 4Ô($™Í?Ã.ŠøÎ?„-vû¬2Í?Ó.¦™îuÌ?KÊÝçøÌ?. ´¾LÌ?ø¥~ÞT¤Ì?•»ÏñÑâÌ?1[²*ÂMÌ?AÕèÕÍ?÷«ßmÞÌ?šë4ÒRÍ?!”÷q4GÌ?ÜØìHõÍ?á ½þ$>Ï?ò˜Êø÷Í?J•({K9Ë?÷™Ì?\Y¢³Ì"Î?겘Ø|\Í?†è8hÌ?^fØ(ëË?e73úÑpÌ?]lZ)rÍ?És}Ì?8ó«9@0Ë?ó‘”ô0´Ì?·aoË?Ù[ÊùbïË?\qqTn¢Ì?5Ô($™ÕË?ô¾ñµgÌ?"ʼn&PÌ?~q©J[\Ë?f»B,cË?ÙYôNÌ?4hèŸàbÏ?§Ï¸®˜Ñ?úñ—õIÒ?MùT^Ð?·]h®ÓHÐ?£’:MÐ?õc“üˆ_Ð?¶†R{mÑ?›É7ÛܘÐ?Ÿp]1#Ð?Ð?šEóÑ?PŽDÁŒÏ?çà™Ð$±Ð?IVñFÐ?SçQñGÐ?åìÑV%Ð?rö´Ã_Ð?ƒn/iŒÖÏ?u“VÏ?píDIH¤Ï?EœN²ÕåÎ? $}ZEÏ?Ÿ­ƒƒ½‰Ï?î§/úÎ?L8 ¥Ï?kÕ® iÏ?›Z¶Ö Ð?ð‹KUÚâÎ?,f„·!Ð?,¸ðÀÑ?q¥]PÐ?0du«ç¤Í?+½6+1Ï?®ºÕ”dÐ?eS®ð.Ð?霟â8ðÎ?Ê1YÜdÎ?¡Ÿ©×-Ï?¡÷ÆÐ?+ùØ] ¤Î?-°ÇDJ³Í?ÎÁ3¡IbÏ?Ä[çß.ûÍ?öA–Î?p¶¹1=aÏ?;‰ÿ"hÎ?¼æUÕÏ?0žACÿÏ?!±Ý=@÷Í?l•`q8óÍ?ËhäóЧÎ??{óÑ?kïSUh Ó?g|_\ªÒÓ?²×»?ÞÑ?Êß½£ÆÑ?hz‰±L¿Ñ?\È#¸‘²Ñ?Q¡º¹øÛÒ?=Òà¶¶ðÑ?Ó/oÑ?2<ö³XŠÐ?å`6†Ñ?pî¯÷­Ò?Xþ|[°TÓ?=ƒù+dÔ?ËfI-Ñ? uXá–Ñ?ð1XqªµÑ?YÞU˜‡Ñ?„/¡‚Ò?!çýœ0Ñ?H2«w¸Ò?ßÁO@¿Ñ?®óo—ýºÑ?7§’ Ñ?ȶ 8KÉÑ?ŒÖQÕQÑ?%?âW¬áÐ?5cÑtv2Ñ?Pj’ÌÐ?ƒ÷U¹PùÐ?Täqs*Ñ?õKÄ[çßÐ?!=E7Ñ?ƒQI€&Ñ?Zd;ßOÑ?ÒŒEÓÙÉÐ?Tr3Ü€Ñ?ÝÓÕ‹mÒ?û&7ЬÑ?äGˆ,Ð?¤ÿåZ´Ñ?·ð¼TlÌÑ?L¥ŸpvkÑ?¥,CëâÐ?²,˜ø£Ð?Ã)sóèÐ?Ô)n„Ñ?en¾ݳÐ?’?xî=Ð?Q._x%Ñ?(c|˜½lÐ?ÅS4¸­Ð?Ë+×Ûf*Ñ?5Ïù.¥Ð?0[wóÐ?+Üò‘”ôÐ?yxÒÂeÐ?NÕ=²¹jÐ?ŒgÐÐ?ÁÐ?²Õ唀˜Ò?Ön»Ð\§Ô?©MœÜïPÕ?>ê¯WXpÓ?Ï:¯±KÓ?î?2:=Ó?¬ª—ßi2Ó?>>!;ocÔ?v©ú™zÓ?÷@øÒ? ¦šYKÒ?9™¸UÓ?ù½Mö#Ô?ûèÔ•ÏÔ?œ‡˜NëÕ?ù«<°Ò?¢&ú|”Ó?ÛÂóR±1Ó?1ÏJZñ Ó?ìø/Ô?-¯\o›©Ò?î"LQ.Ó?„-vû¬2Ó?lv¤úÎ/Ó? ‚Ç·w Ó?C9Ñ®BÓ?]Þ®ÕÒ?Ž«‘]iÒ?sôø½Ò?{ò%TÒ?ëæâo{‚Ò?u¯“ú²´Ò?ê^'õeiÒ?Ü~ùdÅÒ?l^ÕY-°Ò?L5³–ÒÒ?geû·\Ò?» ”XÓ?þ oÖàÓ?)²ÖPj/Ó?ÜÖž—ŠÑ?”ú²´SsÒ?J•({K9Ó?ÙÎ÷SãÒ?Š}"OÒ?h^»ïÒ?d°âTkaÒ?cò˜ùÓ?ÓUø3Ò?‘Жs)®Ñ?ôlV}®Ò?]S ³³èÑ? <÷.Ò?ðÜ{¸ä¸Ò?­/Úr.Ò?Ê4š\ŒÒ?p”¼:Ç€Ò?L8ôïÑ?dsÕ<Õ?{£V˜¾×Ô?g ר%Õ?w;Sè¼Ô?õ¢v¿ ðÔ?x|{× /Õ?ö{b*ßÔ?Â26t³?Õ?ØaLú{)Õ?gaO;ü5Õ?Ó¼ãÉÔ?¤9²òË`Õ?&©L1AÖ?w1Ít¯“Õ?’]i©÷Ó?N]ù,ÏÔ?âÈ‘EšÕ?:¸Y¼XÕ?l’ñ+ÖÔ?rÌ_!sÔ?éï¥ð ÙÔ?Ó0|DLÕ?_zûsÑÔ? ¦šYKÔ?ËMÔÒÜ Õ?®¹£ÿåZÔ?Â&S£Ô?@i¨QH2Õ?nj ùœ»Ô?g+/ùŸüÔ?K±£q¨ßÔ?—¬Šp“QÔ?ØdzˆFÔ?ßmÞ8)ÌÔ?€^»´áÕ?ÚYôNÜ×?²žZ}uUØ?û?‡ùò×?‚Êø÷Ö?†p̲'Ö?^h®ÓHKÖ?.ÿ!ýöu×?Ê4š\ŒÖ?]2Ž‘ìÖ?A¼®_°Õ?•œ{hÖ?G°qý»>×?_9ïÿã×? ¦–­õØ?’]i©÷Õ?ßRÎ{/Ö?˜Û½Ü'GÖ?²HïOÖ?:Ì—`×?î#·&ÝÕ?æCV¸Ö?jLˆ¹¤jÖ?™cyW=`Ö?Ä>#KÖ?:¸Y¼XÖ?¢ÏGqÖ?­ÀÕ­žÕ?PÈÎÛØìÕ?—6–~Õ?N·ìÿ°Õ?L5³–Ö? ø1æ®Õ?Ý—3ÛÖ?Š?Š:sÖ?€ÉcëÕ?Àx Õ?ÇeÜÔ@Õ?pÐ^}<ôÕ?òÓ¸7¿aÕ? JÑʽÕ? ¼“OÕ?Àw›7N Õ?ê!ÝAìÔ?‘™ \kÕ? ÇóPoÖ?‹T[rØ?ÎR²œ„ÒØ?U.Tþµ¼×?®fñ}qÖ?äž{×?ÃIš?¦µÖ?0œk˜¡ñ×?EóùõÖ?û¬2SZÖ?$Õw~Q‚Õ? jøÖÖ?ºùFtϺ×?wÖn»Ð\Ø?£dVïpÙ?¯èÖkzÖ?”Ù “ŒœÖ?õñÐw·²Ö?Ü:åÑÖ?~RíÓñ˜×?Ù—l<ØbÖ?²ó66;×?CV¸åÖ?=)“ÚÖ?ÁÅŠLÃÖ?ÉæªyŽÈÖ?ʉvR~Ö?i:;%Ö?Á=~oÖ?…(_ÐBÖ?@7n1Ö?ùNÌz1”Ö?Ÿqá@Ö?¤aQ§Ö?C®Ô³ ”Ö?h@½5_Ö?c%æYI+Ö?ÙvÚŒÖ?›Ça0…×?B’Y½ÃÖ?ì…¶ƒÕ?¸®˜ÞÕ?*¨¨ú•ÎÖ?z›©¾Ö?Ä&2sËÕ?}!ä¼ÿÕ?UÛMðMÓÕ?$~Å.rÖ?¥ö"ÚŽ©Õ?‹ßV*Õ? åD» )Ö?5ð£ö{Õ?ò DOʤÕ?:\«=ì…Ö?ý¡™'×Õ?"Ã*ÞÈ<Ö?öóåÖ? ¾iúì€Õ? dv½SÕ?·œKqUÙÕ?yGsdåÖ?;q9^èØ?åïÞQcBÙ?åîs|´8Ø?v‹ÀXßÀÖ?ºóÄs¶€×?¦`³é×?¯]ÚpXØ?'ÛÀ¨S×? ž^)ËÖ?„H†[ÏÕ?à ½úxèÖ?E>‘'Ø?Ò7iÍØ?dsÕÖ?Aœ‡˜N×?‚X6sHjÙ?É:]¥»Ù?0fKVE¸Ø?8ó«9@0×?ôNÜóü×?½Þýñ^×?³]¡–±Ø?˜Št?§×?ØaLú{)×?N ^ôÖ?VGŽtF×?@N˜0š•Ø?‚Uõò;Ù?åñ´üÀUÚ?î—OV W×?${„š!U×?Ê2ı.n×?m:¸Y¼×?_DÛ1uWØ?[°Tð2×?Ü,^, Ø?£¢ÑÄ×?¾Þýñ^µ×?EºŸSŸ×?ÚÊKþ'×?\:æçn×KS×?+iÅ7>×?@h=|™(×?¼’ä¹¾×?}¯!8.ãÖ?~Q‚þBÖ? –\ÅâÖ?—®`ñdÖ?®ð.ñÖ?kF¹‹0×?ÿunÚŒÓÖ?) ‰´?×?á² ›.×?~©Ÿ7©Ö?jûWVš”Ö?Ú¬ú\mÅÖ?be4òyÅ×?ZžwgíÖ?…[>’’Õ?ÀÎM›qÖ?C¨R³×?éÓ*úC3×?J_9ïÿÕ?Ѐz3j¾Õ?}?qýÕ?øRxÐìºÖ?ù…W’<×Õ?w£ù€@Õ?ðû7/N|Ö?§Y Ý!ÅÕ?ÛÝt_ÎÕ?¬Žé ×?ëâ6À[Ö?áÎ…‘^ÔÖ?¦E}’;lÖ?å³<îÎÕ?¯–;3ÁpÕ?¡¼£9Ö?¾¼ûèÔÕ?¬Å9êè×?ÝïPèØ?©|š“×?÷.9î”Õ?ðû7/N|Ö?¾½kЗÞÕ?DN_Ï×,×?ãŒaNÐ&Ö?7ÿ¯:r¤Õ?B‡D¤Ô?4÷ð½¿Õ?uèô¼ ×?c¶dU„›×?ýfbº«Ø?RÑXû;ÛÕ?@/ܹ0ÒÕ?T­…YhçÕ?Ë,B±4Ö?aà¹÷pÉÖ?ŪA˜Û½Õ?Å °rh‘Ö?²G¨REÖ? a°ä*Ö?*á ½þ$Ö?K;5— Ö?V ì1‘ÒÕ?oe‰Î2‹Õ?vâr¼ÑÕ?q:ÉVÕ?VÔ`†Õ?ñ˜õb(Ö?ÊI»ÑÇÕ?{3j¾J>Ö?Mž²š®'Ö?x¸£Õ?˜½l;mÕ? ȳ˷Õ?SÌAÐѪÖ?°Y.óÕ?ߦ?û‘"Ô?ÁþëÜ´Õ? B²€ Ö?ˆ)‘D/Ö?¯½7†Õ?N]ù,ÏÔ?Å8 Õ?ÌDR·³Õ?gÔ|•|ìÔ?Ö‹¡œhWÔ? ñ+Öp‘Õ?nmáy©ØÔ?xñ~Ü~ùÔ?yöÑ©+Ö?„%ZòxÕ?N`:­ÛÕ?"úµõÓÕ?¿ 1^óÔ?“QewƒÔ?¨PÝ\Õ?-@ÛjÖÔ?¡­Ü ÌÖ?b.©ÚÖ?nøÝtËÖ?_zûsÑÔ?‡Ä=–>tÕ?ñ¸¨ÅÔ?•!ÿÕ?<ùôØ–Õ?yæå°ûŽÔ?ãOT6¬©Ó?#…²ðõµÔ?A ]ÞÕ?ò(•ð„^Ö?Ù@ºØ´R×?>$|ïoÐÔ?û²´Ss¹Ô?¡i‰•ÑÈÔ?ú—¤2ÅÕ?¡º¹øÛžÕ?¤Æ„˜KªÔ?þµ¼r½mÕ?ެü2#Õ?xµÜ™ Õ?W•}WÕ?Çdqÿ‘éÔ?cdÉË»Ô?›ÆöZÐ{Ô?J}YÚ©¹Ô?*嵺KÔ?ú'¸XQƒÔ??6ÉøÕ?w;Sè¼Ô?`” ¿Ð#Õ?Õz¿ÑŽÕ?­iÞqŠŽÔ?°ÉõÔ?qÈÒŦÔ?p•'vŠÕ?ÂMF•aÜÔ?—¬Šp“QÓ?:èÔ?›:ŠÿÔ?§“lu9%Õ?Ô'ž³Ô?éî:òÏÓ?dw’ Ô?ž~P)”Ô?ˆ0~÷æÓ?ÈÑYùeÓ?ÁãÛ»}Ô?¾½kЗÞÓ?‰a‡1éïÓ?’‘³°§Õ?¸’xÔ?RÑXû;ÛÔ? ÐÒlÔ?éñ{›þìÓ?î"LQ.Ó?`ÈêVÏIÔ?GV~ŒÔ?ù1æ®%äÕ?ÑËØÐÍÕ?÷:©/K;Õ? JÑʽÓ?Ç,{ØœÔ?ú ¨7£æÓ?pUjöÔ?ƒú–9]Ô?× /½ý¹Ó?›WuV ìÒ?é·¯çÓ?Ók³±óÔ?ÉV—SbÕ?6>“ýó4Ö?´r/0+Ô?Ý@wòéÓ?N˜0š•íÓ?È#¸‘²EÔ?ñ„^ŸÔ?°® ÔbðÓ?MÛ¿²Ò¤Ô?Ô+eâXÔ?Z,Eò•@Ô?PV W@Ô?aÂhV¶Ô?ÕËï4™ñÓ?iþ˜Ö¦±Ó?˜öÍýÕãÓ?¾.úÓ?B±4-±Ó?—«›äGÔ?÷@øÓ?¯]ÚpXÔ?½á´àEÔ?ƒÁ5wô¿Ó?Ú×3ÂÓ?"«[='½Ó?õiý¡™Ô?–B —8òÓ?4œ27߈Ò? ˆI¸GÓ?•~P)Ô?üǙ&lÔ?œQ}>Ó?÷¬k´èÒ?CÉäÔÎ0Ó?ïTÀ=ÏŸÓ?IÛø• Ó?l®šçˆÒ?àëTùžÓ?[}uU Ó? :!tÐ%Ó?â:ÆGÔ?yé&1¬Ó?ÒyYÔ?äL¶ŸŒÓ?žíÑî#Ó?v4õ»°Ò?n‰\pÓ?ª x™a£Ó?Ó0|DLÕ?6å ïrÕ?¼Yƒ÷U¹Ô?Êû8š#+Ó?>±N•ïÔ?n…°KXÓ?Eƒ<…\Ô?ºJw×ÙÓ?±k{»%9Ó?Ëd8žÏ€Ò?]kïSUhÓ?½Þýñ^Ô?z›©¾Ô?Q½5°U‚Õ?0™ò!¨Ó?L¤4›ÇaÓ?ßÜ_=î[Ó?8ºJw×ÙÓ?ÍYŸrLÔ?²Jé™^bÓ?†Œ.oÔ?J)èö’ÆÓ?L8 ¥Ó?#Ù#Ô ©Ó?}¢|Ó?ˆ×õ vÓ?›ÖtBÓ?Y†8ÖÅmÓ?n£¼Ó?“o¶¹1=Ó?š¯’ÝÓ?HnMº-‘Ó?*¬TPQõÓ?š²ÓêÓ?H5ì÷Ä:Ó?_ðiN^dÓ?KrÀ®&OÓ?IÚ>æÔ?‚ã2njÓ?iÒá!Ò?æ°ûŽá±Ò?èÝXP”Ó?7R¶HÚÓ?}$%= ­Ò?ÑèbgÒ?…î’8+¢Ò?“[ìöÒ?X­Lø¥~Ò?Ž Ò?xµÜ™ Ó?°t>Ò?ÈCßÝÊÓ?…]=ðÓ?‚©fÖR@Ô?Í#0ðÜÔ?O²žZ}Ó?—wJÓ?Ë¡E¶óýÒ?ËgyÜÓ? ‡3¿šÓ?=Ô¶aÓ?9 {Úá¯Ó?óZ Ý%qÓ?Y§Ê÷ŒDÓ?{ò%TÓ?Ș»–Ó?<„ñÓ¸7Ó?$î±ô¡ Ó? 1—Tm7Ó?ÖsÒûÆ×Ò?+3¥õ·Ó?§îyþ´Ó?4iSulÓ?Þ’°«ÉÓ?(œÝZ&ÃÓ?ƒS°ÆÙÒ?ÜõÒNÓ?órØ}ÇðÒ?á—úyS‘Ó?”i4¹Ó?6Y£¢ÑÑ?÷”œ{hÒ?@i¨QH2Ó?st´ªÓ?Œ„¶œKqÒ?Ì(–[Z Ò?ž$]3ùfÒ?Ô¸7¿a¢Ò?þÕã¾Õ:Ò?—<ž–¸Ñ?ù‚0ºÒ?穹nÒ?ƒÜE˜¢\Ò?"ýöuàœÓ?Öwõ*Ó?aU½üN“Ó?-²ï§Ò?Q§“luÒ?$cµùÕÑ?î•y«®Ò?X9´ÈvÓ?ÚâŸÉþÔ?\ætYLlÔ?®œ½3ÚªÔ?®ž“Þ7¾Ò?¸#œ¼èÓ?%‘}eÁÒ? ò³‘ë¦Ó?ƒ¤O«èÒ?K«!q¥Ò?Gªïü¢Ò?€d:tzÞÒ?”M¹Â»Ó?øÃÏÔ?¾2oÕu¨Ô?é×ÖOÿYÓ?*¨¨ú•ÎÒ?Ù®ÐËÒ?H›V Ó?Ìa÷ÃcÓ?üÖMõÒ?Ž<Y¤‰Ó?lÎÁ3¡IÓ? `­Ú5!Ó? <÷.Ó?ÝéÎÏÙÒ?´qÄZ| Ó?s…w¹ˆïÒ?Íù†Ó?É:]¥»Ò?Ý@wòéÒ?7TŒó7¡Ó?÷=ê¯WXÓ?9 毹Ó?¼Ñ“2©Ó?}ÎÝ®—¦Ò?öE™ 2Ó?4÷ð½¿Ò?z7eÓ?ÊÂ×׺ÔÒ?Üóüi£Ñ?˜ˆ·Î¿]Ò?-ÎæÓ?†1zn¡Ó?LàÖÝ<Ò?°âÊÑ?{0)>>!Ò?MØ~2ƇÒ?@‰Ï`ÿÑ?bÚ7÷WÑ?‡ú]ØšÒ?±Â-IIÒ?%Ì´ý++Ò?Ÿ`<ƒ†Ó?)¯•Ð]Ó?ÄíаuÓ?ãǘ»–Ò?øpÉq§tÒ?ÿêqßjÑ?]Åâ7…•Ò?â[X7ÞÓ?è¡¶ £Ô?ª™µöÓ?-ÌB;§YÔ? Åoò[Ò?}Ê1YÜÓ?Käõ`RÒ?öµ.5B?Ó?L¨àð‚ˆÒ?IeŠ9:Ò?}ÍrÙèœÑ?‰yVÒŠoÒ?[ÏŽYÓ?‹Š8d«Ó?ò{›þìGÔ?…zúüÒ?Cÿ+jÒ?W%‘}eÒ?ÛÁˆ}(Ó?ÞÈ<òÓ?¢+Üò‘Ò?<Øb·Ï*Ó?W>ËóàîÒ?æ®òÂÒ?éî:òÏÒ?( ß÷oÒ?yé&1¬Ò?„fÚþ•Ò?"«[='½Ò?˜3Ûú`Ò?…$³z‡Ò?ò'*ÖTÓ?Ù³ç25 Ó?BzŠ"nÓ?¢´7øÂdÓ?g¹ltÎOÒ?ŸåypwÖÒ?ÅqàÕrgÒ?h°©ó¨øÒ?Ø›’“‰Ò?…]=ð1Ñ?ˆÙ˶ÓÖÑ?ôlV}®Ò?ÜIDøAÓ?.â;1ëÅÑ?( Ê4š\Ñ?¦{Ô—¥Ñ?Ǻ¸Ò?Égð÷‹Ñ?ÎQÚÑ?E>‘'Ò?ZFê=•ÓÑ?Cá³up°Ñ?Ùµ½Ý’Ó?ª¹nÀÒ??áìÖ2Ó?Õ%ãÉÒ?xšÌx[éÑ?U¯²¶)Ñ?NG7‹Ò?â:ÆGÒ?r¦ ÛOÆÓ?ßPølÓ?d:tzÞÓ?}vÀuÅŒÑ?_Ñ­×ô Ò?á@H0Ñ?[¶Ö mÒ?ER·³¯Ñ?YÚ©¹Ü`Ñ?è½ÅÃÐ?mŒðœÑ?á•$Ïõ}Ò?mæÔBÉÒ?-z¨mÓ?bõG,Ò?C®Ô³ ”Ñ?–² q¬‹Ñ?¡Ø š–XÒ?4J—þ%Ò?Û†Q<¾Ñ?KrÀ®&OÒ?œ6ã4DÒ?¹¨ÅäÑ?¦}sõÑ?Ü×sF”Ñ?áy©Ø˜×Ñ?H¤mü‰ÊÑ?þ)U¢ìÑ?ˆ÷XŽÑ?>Ì^¶¶Ñ?ˆLùTÒ?£uT5AÒ?È=›UŸÒ?Q¿ [³•Ò?¦îÊ.\Ñ?‘(´¬ûÑ?ÂzýI|Ñ?Öª]ÒÒ?ˆ jôj€Ñ?ÿ]Ÿ9ëSÐ?¹ªì»"øÐ?¦ÒO8»µÑ?¦E}’;lÒ?é·¯çÐ?‘·±Ù‘Ð?ßmUÙÐ?qâ«Å9Ñ?ëÇ&ù¿Ð?Þ3ßÁOÐ?áaÚ7÷WÑ? 7àóÃÑ?˜öÍýÕãÐ?bøˆ˜IÒ?yÊjºžèÑ?#KæXÞUÒ?¿ÔÏ›ŠTÑ?)±k{»%Ñ?Üx`Ð?# Â¤RÑ? :!tÐ%Ñ?£?4óäšÒ?0œk˜¡ñÑ?|îû¯sÒ?*oG8-xÐ?³)Wx—‹Ñ?Tn¢–æVÐ?-î?2:Ñ?-{ØœƒÐ?ü¥E}’;Ð?²›ýh8Ï?¦E}’;lÐ?y7R¶HÑ?ݲCüÖÑ?õ„%P6Ò?ÏdÑ?·`©.àeÐ?v“þ^Ð?’“‰[1Ñ?¤SW>ËóÐ?UOæ}“Ð?ô #Ñ?:–wÕæÐ?8ÜGnMºÐ?;nøÝtËÐ?Ûø• kÐ?mãOT6¬Ð?XÇñC¥Ð?Í >°ã¿Ð??o*RalÐ?’ÍUó‘Ð?ðN>=¶eÑ?ZÖýc!Ñ?—6–~Ñ?¦ð ÙuoÑ?Q†ª˜J?Ð?ìOâs'ØÐ?:“6UÐ?7á^™·êÐ?Kt–Y„bÐ?ÖâSŒgÎ?ž±/Ùx°Ï?«´Å5>“Ð? IJ™CRÑ?óâÄW;ŠÏ?§Z ³ÐÎ?l{»%9`Ï?žê›áÐ?,¨þA$Ï?üQÔ™{HÎ?—©Ið†4Ð?.sž±Ï?;4,F]kÏ?gaO;ü5Ñ?§Y Ý!ÅÐ?¹à þ~1Ñ?Ž®ÒÝu6Ð?lê<*þÏ?ùÙÈuSÎ?ü¥E}’;Ð?ž±/Ùx°Ï?o‚oš>;Ñ?ÒV%‘}Ð? ƒ2&Ñ?îBsFZÎ?o×KS8Ð?ak¶ò’ÿÍ?¬‘]i©Ï?NÓg\WÎ?é™^b,ÓÍ?™ðKý¼©Ì?€óâÄW;Î?bg רÏ?ÇG‹3†9Ð?#‡ˆ›SÉÐ?ã4ôOpÏ?þÒ¢>ÉÎ?­ûÇBtÎ?pïô¥·Ï?Û2à,%Ï? ü¨†ýžÎ?Ánض(³Ï?É w¦(Ï? ¢îÚÎ?¨SÝÏ?ãá=–#Î?ÄËÓ¹Î?‘&Þž´Î?•eˆc]ÜÎ?“V-Î?aTR' ‰Î? þ·’Ð?^¸sa¤Ï?ÇF ^×/Ð?*6æuÄ!Ð?ÿ ’!ÇÖÍ?ý¢ý…Ï?Üx`áÍ?©|š“Ï?i‹k|&ûÍ?ã4ôË?OÌz1”Í?Ùî ûrÎ?eRC€ Ð?á&£Ê0îÌ?Èyÿ'LÌ?‘Òl‡ÁÌ?ÇK7‰A`Í?®€B=}Ì?t ‡ÞâË?¨V_]¨Í?% &áBÍ?!®œ½3ÚÌ?ž±/Ùx°Ï?e¥I)èöÎ?Ò‹Úý*ÀÏ?‹à+Ù±Í?­‰¾¢[Í?™Ÿš²ÓË?€ ;¨ÄÍ?#M¼Ì?vÞÆfGªÍ?Yà+ºõšÊ?k¸È=]ÝÉ?æx¢'eÊ?¤SW>ËóÊ?.É&Ê?èƒelèfÉ?üü÷àµKË?Ä%ÇÒÁÊ? î<0€Ê?;M„ OÍ?_%» ”Ì?ÉU,~SXÍ? nk ÏKË?nN%@Ë?ÚÆŸ¨lXÉ?¼·_>YË?²€ ܺ›Ë?ÙëÝïÍ?`#I®€Ì?ù/ÈÐÍ?J˜iûWVÊ?¡¢êW:Ì?„ó©c•ÒÉ?Ÿ ±Ý=@Ë?®Ø_vOÊ?÷uàœ¥É?”†…$³È?3ÂÛƒÊ?‚‹5˜†Ë?5æèñË?¨äœØCûÌ? ?8Ÿ:VË?yuŽÙëÉ?¤P¾¾ÖÉ?¶yËÕË?±1¯#ÙÊ?!ê>©MÊ?0º¼9\Ë?f÷äa¡ÖÊ?ʉvR~Ê?M1AG«Ê?/À>:uåÉ?«]ÒƒÊ? ‰°áé•Ê?»|ëÃz£Ê?8kð¾*Ê?n0Ôa…[Ê?åb ¬ãøË?®¼äòwË?D÷¬k´Ì?p(|¶Ì? uXá–É?üo%;6Ë?„›Œ*øÉ?fŸÇ(ϼÊ?/°ŒØÉ?^‚SHÞÇ?I0eàÈ?ÓMbX9Ê?)ÍæqÌË?cdÉË»È?ëó)È?³²}È[®È?: ûvÉ?EÚÆŸ¨lÈ?0º¼9\«Ç?ÞJ ,€É?GV~ŒÉ?hz‰±L¿È?©¤N@aË?£té_’ÊÊ?}iÆ¢Ë?Û÷¨¿^aÉ?£>+NÉ?ûPŒ,™Ç?~ü¥E}’É?ÿwD…Ê?2:=ïÆÌ?j ùœ»]Ë?’xy:W”Ì?Ø—qSÉ?ªðgx³Ë?:=ïÆ‚ÂÈ?Cr2q« Ê?Ì(–[Z É?îÏECÆ£È?¾3Úª$²Ç?~Œ¹k ùÈ?±à‚lYÊ?*ý„³[ËÊ?瓼ÇË?Á-]Á6Ê?_\ªÒ×È?]N ˆI¸È?6ÉøkÊ?RF\¥É?µ¥òz0É?œ3¢´7Ê?Š[1еÉ?y±0DN_É?V)=ÓKŒÉ?¢ïne‰ÎÈ?6t³?PnÉ?œÁß/fÉ?(›r…É?Êp<ŸõÈ?âä~‡¢@É?Û3KÔÔÊ? ·|$%=Ê?k» ¾iúÊ?iÚV³ÎÊ?ž}åAzŠÈ?§²(ì¢èÉ?'À°üù¶È?§%VF#ŸÉ?ߺñîÈÈ?¢E¶óýÔÆ?œ0a4+ÛÇ?vp°71$É?¤ö{bÊ?Zœ¡¸Ç?GV~ŒÇ?o­mŽÇ?ã3Ù?OÈ? ýHVÇ?‡¢@ŸÈ“Æ?Jð†4*pÈ?Çg²žÈ?jÙZ_$´Ç?¾Û¼qRÊ?סš’¬ÃÉ?)A¡GŒÊ?hE,bÈ?[[x^*6È?Òá!ŒŸÆ?W|Cá³uÈ?aâ¢ÎÜÉ??Qžy9Ì?X<õHƒÛÊ?DiâàË?Nïâý¸ýÈ?Y2Çò®zÊ?:#J{ƒ/È?îx“ߢ“É?¬8ÕZ˜…È?ˆìø/È?LÂ…<‚Ç?õ·CÃbÈ?ëÙ\5ÏÉ?R·³¯<Ê? ¸Ê;Ë?€-¯\o›É?¹ãM~‹NÈ?:#J{ƒ/È?üL‡NÏÉ?*àžçOÉ?ÿ>ãÂÈ?Ÿ¡¼£É?¢&ú|”É?6X8IóÇÈ?ò—õIîÈ?áC‰–<È?6ZôPÛÈ?ž^)ËÇÈ?¥ØÑ8ÔïÈ?)狽_È?t&mªÈ?Mž²š®'Ê?Õ‘#‘É?/OçŠRBÊ?Éw)uÉ8Ê?¨ã1•ñÇ?ÁXßÀäFÉ? <÷.9È?øàµKÉ?Îé K<È?¶ƒûPÆ?É«s È^Ç?&ãÉ¡È?@‰Ï`ÿÉ?jmÛkAÇ?Ûü¿êÈ‘Æ?ÞÈ<òÇ?´Yõ¹ÚŠÇ?µÀ)ÍÆ?F|'f½Æ?>{.S“àÇ?odùƒÇ?µOÇc*Ç?ÓL÷:©É?ß§ªÐ@,É?¤ü¤Ú§ãÉ?Ѳî ÑÇ?»}V™)­Ç?›âqQ-"Æ?ǹM¸WæÇ?ñ[z4ÕÉ?ßÛôg?Ì?}±÷â‹öÊ?™Gþ`à¹Ë?žACÿÉ?â镲 qÊ?þ,–"ùJÈ?®( ‰´É?§z2ÿè›È?SäGÈ?‘ ¤‹M+Ç?~r fÈ?—ÄY5ÑÉ?pµN\ŽWÊ?çP†ª˜JË?‚‹5˜†É?£\¿ðJÈ?}“¦AÑ<È?¡½úxè»É?-B±4-É?K‘|%È?'iþ˜Ö¦É?y=˜É?.8ƒ¿_ÌÈ?á|êX¥ôÈ?âåé\QJÈ?Q}>ÊÈ?_Ÿ;ÁÈ?FÍWÉÇîÈ?Â4 SÈ?º¡);ý È?:ž%ÈÊ?g˜ÚRyÉ?êD2Ê?\«=ì…Ê?1x˜öÍýÇ?©öéxÌ@É?“Úl@È?„çÞÃ%É?³Íé KÈ?M×]~Æ?½TlÌëˆÇ?Äy8é´È?AJ˜iûÉ?I-”LNÇ?­k´è¡Æ?«‘]iÇ?¤ÅÜ Ç?úïÆ?«±„µ1Æ?“[ìöÇ?¨¨ú•·Ç?c~nhÊNÇ?p—ýºÓÉ?™Êø÷É?ÊÁl ËÉ?.㦚ÏÇ?Çž=—©Ç?cÓJ!KÆ?£w*àžçÇ?°­Ÿþ³æÉ?¼Ì°QÖoÌ?,D‡À‘@Ë?À?¥J”½Ë?|—wJÉ?QhY÷…Ê?• k*‹È?SX© ¢êÉ?«ì»"øßÈ?ÏÀÈËšXÈ?}–çÁÝYÇ?Ý‹Š8È?ÝBW"PýÉ?ñƒó©c•Ê?»' µ¦Ë?–´â ŸÉ?ZcÐ ¡ƒÈ?³ìI`sÈ?%è/ôˆÑÉ?hB“Ä’rÉ?ç6á^™·È?YLüQÔÉ? Ý%qVDÉ?š}£<óÈ?x'ŸÉ?ç,òë‡È?›h>çÈ?ëÉü£oÒÈ?]1#¼=É?àd¸uÈ?5Ñ磌¸È?,¸ðÀÊ?]éEí~É?%wØDf.Ê?)°¦ Ê?|~!<È?Õé@ÖSÉ?b×övKrÈ?æÍáZíaÉ?¾.úÈ?eÄ QºÆ?'c`ÇÇ?øˆ˜IôÈ?+ß3¡Ê?îv½4E€Ç?U¿ÒùðÆ?FzQ»_Ç?ŸæäE&àÇ?ö³XŠä+Ç?`!sePmÆ?lê<*È?I›ªÇ?Ž<»|Ç??eÄ É?€ð¡DKÉ?Ò°¨ˆÓÉ?È@ž]¾õÇ?TªDÙ[ÊÇ?\>’’†Æ?ž–¸ÊÈ?d–= lÎÉ?Ž®ÒÝuÌ?Îm½2oË?W\•›¨Ë?4¡l\É?gCþ™A|Ê?ê@ÖS«¯È?1&ý½Ê?¼æUÕÉ?ÑÎihwÈ?6t³?PnÇ?Ôê"…²È?têÊgyÊ?‘Òl‡ÁÊ?ËH½§rÚË?Ûˆ'»™É?`V(ÒýœÈ?ûY,Eò•È?uÞÉÉ?Uú g·–É?»*P‹ÁÃÈ?Ù±ˆ×õÉ?KOË\É?;ûʃôÉ?ßýñ^µ2É?ÒþX«È?÷«ßmÞÈ?Gˆ,ÒÈ?•DöA–É?‰B˺È?‰\p¿È?©¾ó‹ôÉ?\:æU…È?˜5±ÀWtÉ?¯îXl“ŠÈ?fKVE¸ÉÆ?ª¹nÀÇ?ýJçóÉ?‘)‚ªÑÉ?ÓÛŸ‹†ŒÇ?î\éEíÆ?ºk ù gÇ? ˜£ÇïÇ?Û2à,%Ç?=ð1XqÆ?k™ ÇóÈ?"û Ë‚‰Ç?^Ÿ9ëSŽÇ?6È$#gaÉ?ën‡†ÅÈ?f»B,cÉ?–AµÁ‰èÇ?„¼LŠÇ?¤p= ×£Æ?Wéî:òÇ?èy’tÍÈ?ºöôÂË?q:ÉÊ?™ðKý¼©Ê?°ÅnŸUfÈ?½ª³Z`É?¢}¬à·!È?Ž<»|É?¶…ç¥bcÈ?Ò°¨ˆÓÇ?ìL¡ó»Æ?-Ë×eøÇ?îí–ä€]É?LbõGÊ?!?¹nJË?ŸZ}uU È?ßCpìÇ?ûçiÀ éÇ?ÎQGÇÕÈÈ?Ññ(•ðÈ?È@ž]¾õÇ?ö³XŠä+É?&áBÁÈ?» ”XÈ?ºÀå±fdÈ?²gÏejÈ?ÇAœ‡È?zR&5´È?@¾„ /È?0º¼9\«Ç?Ü»}éíÇ?Ëõ¶™ ñÈ?ë8~¨4bÈ?aÂhV¶É?J^c@öÈ?’Ï+žz¤Ç?Žlê<È?9 {ÚáÇ?f£s~ŠãÈ?,¸ðÀÈ? <÷.9Æ?‰—§sE)Ç?ùdÅpuÈ?ÚÄÉýÉ?Ùî@òÆ?ølìMÆ?j†TQ¼ÊÆ?~b¼æUÇ?*A*ÅŽÆ?Q¡º¹øÅ?]ÄwbÖ‹Ç?©½ˆ¶cêÆ?"á{ƒöÆ?|ó&¤È?qh”.ýÇ?ÐÒl#žÈ?Z×h9ÐCÇ?þžX§Ê÷Æ?nOØîÆ?N(DÀ!TÇ?¹¨ÅäÇ?öBÛÁÊ?‡†Å¨kíÉ?eo)狽É?]¿ðJ’Ç?ò`‹Ý>«È?$ïÊPÇ?ÚTÝ#›«È?Õçj+ö—Ç?ΈÒÞà Ç?È@ž]¾õÅ?ë^fØ(Ç?•Ö߀È?PU¡X6É?!ÂøiÊ?ÇÚßÙ½Ç?Úå[ÖÇ?#ö  Ç?fN—ÅÄæÇ?…[>’’È? ´¾LÇ?,Eò•@JÈ?› ê>©Ç?û¬2SZÇ?î"LQ.Ç?kœMG7Ç?%Ì´ý++Ç?t´ª%Ç?ƒ/L¦ FÇ?6®×gÎÆ?èÚÐ Ç? ®¹£ÿÇ?Ê2ı.nÇ?¼é–âÈ?:uå³<È?vãÝ‘±ÚÆ?âæT2TÇ?W²c#Ç?Ÿ;ÁþÇ?ÎŒ~4œ2Ç?#,*ât’Å?äóЧiÆ?mÆÁ¥Ç?ãá=–#È?7à øü0Æ?˜ŸÅ?‰&PÄ"Æ?Á:Ž*Æ?f 2þ}ÆÅ?ßO—nÅ?CŒ×¼ª³Æ?32È]„)Æ?%wØDf.Æ?Nïâý¸Ç?6çà™Ð$Ç?¯xê‘·Ç?'0ÖmÆ?§é³®+Æ?Ù•–‘zOÅ?Ęô÷RxÆ?ÁÇ`ũ֯?"S>U£É?'†§WÊÈ?ÙwEð¿•È?˜àÔ’wÆ?0bŸŠ‘Ç? PO?Æ?Zd;ßOÇ?Ëd8žÏ€Æ?t·ë¥)Æ?#föyŒòÄ?]4du¬Æ?$Dù‚Æ?„~¦^·Æ?ÞÉ§ÇÆ?¼æUÕÇ?­¥€´ÿÆ?NÐ&‡O:Ç?¼zÆ?c+hZbeÆ?j£:ÈzÆ?YO­¾º*Æ?zVÒŠo(Æ?Øñ_ Æ?Ó£©žÌ?Æ?úüáç¿Å?\Uö]üÅ?÷WûVëÆ?Xÿç0_^Æ?Í‘•_Ç?Šçl¡õÆ?jÛ0 ‚ÇÅ?ªÒ×øLÆ?ÈBt Æ?ø2Q„ÔíÆ?ÓŸýHÆ?¼Ì°QÖoÄ?¢ ±ˆaÅ?µ‰“ûŠÆ?CqÇ›üÇ?g_yž"Å?U¡X6sÄ?²ºÕsÒûÄ?ˆ÷XŽÅ?81$'·Ä?Ðb)’¯Ä?Š[1еÅ? ¦šYKÅ?¥0ïq¦ Å?¹Ù•–‘Æ?`<ƒ†þ Æ?s¢]…”ŸÆ?BÍ*ŠWÅ?i6Ã`þÄ?YO­¾º*Ä??ÅqàÕrÅ?+Û‡¼åêÅ?|š“™È?>w‚ý×¹Ç?‚‹5˜†Ç?2;‹Þ©€Å?±ú# –Æ?;«ö˜HÅ?:\«=ì…Æ?Êà(yuŽÅ?¡¡‚‹Å?ñðžËÄ?P©eo)Å?· 8KÉrÆ?æêÇ&ùÇ?Ÿp]1#È?s*ª¸Å?¿ž¯Y.Å?iެü2Å?å^`V(ÒÅ?™(BêvöÅ?rÝ”òZ Å?òz0)>Æ?áBÁ”Å?ãÃìeÛiÅ?X¬á"÷tÅ?€FéÒ¿$Å?ÂÛƒ/Å?M ˆE Å?uÿwDÅ?íÓñ˜ÊÄ? ×£p= Å?‡†Å¨kíÅ?ˆ5•EaÅ?_·Œõ Æ?£#¹ü‡ôÅ?ΧŽUJÏÄ?‹©ôÎnÅ?õ„%P6Å?ž·±Ù‘êÅ?ÿ+j0Å?Uø3¼YƒÃ?Ï ¡Ä?-Z€¶Õ¬Å? µ‰“ûÆ?iå^`V(Ä?Á‘@ƒMÃ?,g~5Ä?JÏôc™Ä?ýjÌÑÃ?$EdXÅÃ?Q¾¾Ö¥Ä?J —UØ Ä?ïb€DÄ?üߪ›Å?1'h“Ã'Å?Cá³up°Å? ”XSÄ?CÆ£TÂÄ?§–­õEBÃ? eáëk]Ä?öÔê««Å?;¨ÄuŒÇ?éÍ<¹¦Æ?§x\T‹ˆÆ?Åoò[tÄ?]Pß2§Å?Z.óSÄ?õ»°5[yÅ?f¡Ø šÄ?‰–<ž–Ä?f0F$ -Ã?“Úl@Ä?|BvÞÆfÅ?χg 2Æ?I/j÷«Ç?š#+¿ ÆÄ?ˆ½PÀv0Ä?mÊÞå"Ä?6®×gÎÄ?ÿ”*QöÄ?u>­¢?4Ã?­¥€´ÿÄ?ÇHö5Ã?|^ñÔÁ?üä(@ÌÂ?QÁá©Ã?g¶+ôÁ2Ä?¢µ¢ÍqÂ?HùIµOÇÁ?¾£Æ„˜KÂ?ŒjQLÞÂ?zR&5´Â?åœØCûXÁ?4¢´7øÂÂ?¯]ÚpXÂ?Í™dä,Â?6ÌÐx"ˆÃ?Ÿt"ÁTÃ?†©-u×Ã?Ñ"Ûù~jÂ?"ʼn&PÂ?,F]kïSÁ?Ḍ›hÂ?Î4aûÉÃ?>”hÅ?°ÇDJ³yÄ?Ëd8žÏ€Ä?n4€·@‚Â?ÔdÆÛJ¯Ã?¸å#)éaÂ?Ë2gÃ?.Yá&£Â?‹¤Ýèc>Â?l{»%9`Á?C©½ˆ¶cÂ?nÛ÷¨¿^Ã?âVA tíÃ?|(Ñ’ÇÓÄ?2!æ’ªíÂ?}?5^ºIÂ?÷@Â?CƒfÚÂ?ßp¹5éÂ?°Žã‡J#Â?LàÖÝ<Ã?‘óþ?N˜Â?,µÞoÂ?‰B˺Â?Ñ/¤ÃCÂ?è‚ú–9]Â?’éÐéy7Â?´TÞŽpÂ?ÆàaÚ7÷Á?òÏ â;Â? ×ÜÑÿÂ?Œ…!rúzÂ?¿ž¯Y.Ã?ªœö”œÃ?Ç ¿›nÙÁ?Gègêu‹Â?Ö­ž“Þ7Â?wj.7êÂ?¤5:Â?å „bÕÀ?YvQôÀÁ?Õ{L¤Â?!;oc³#Ã?NÔÒÜ aÁ?}$%= ­À?ÎâÅÂ9Á?4MØ~2ÆÁ?çmlv¤úÀ?f,šÎNÀ?0º¼9\«Á?‘z6«>Á?B–Á?Ùî ûrÂ?Ë/ƒ1"QÂ?§wñ~ÜÂ?¾¤1ZGUÁ?Þp¹5Á?…µ1vÂKÀ?Xþ|[°TÁ?ò–«›äÁ?`æ;ø‰Ä?Tâ:ÆÃ?#ŸW<õHÃ?Î67¦',Á?\ætYLlÂ?f0F$ -Á?:w»^š"Â?6l±ÛgÁ?ߣþz…Á?Œ¢>+À?…²ðõµ.Á?vþÓ Â?ÈÒŦ•Â?çÁ=~Ã?î|?5^ºÁ?ž^)ËÁ?Ì&À°üùÀ? Ì EºŸÁ?*ß3¡Á?_²ñ`‹ÝÀ?²eùº ÿÁ?—ÒþXÁ?Üñ&¿E'Á?Þp¹5Á?ÉË»êÁ?’æimÁ?ëZaúÀ?Ké™^b,Á?Úª$²²À?Y|^ñÀ?ÇÚßÙ½Á?JA·—4FÁ?¢F!ɬÞÁ?uÞÉÁ?—㈞À?®¸8*7QÁ?7íµ ÷À?a2U0*©Á?‹lçû©ñÀ?úBÈyÿ¿?ÁâpæWsÀ?~q©J[\Á?€d:tzÞÁ?þ|[°TÀ?dv½S¿?9 {Úá¿??p•'vÀ?œÞÅûq¿?œ¡¸ãM¾?Gå&jiÀ?g×½‰ À?xìg±É¿?ÂÙ­e2Á?,¹ŠÅo Á?+ö—Ý“‡Á?¸Y¼XÀ?4MØ~2Æ¿?ÒpÊÜ|#¾?8žÏ€z3À?>éD‚©À?”2©¡ ÀÂ?»+»`pÍÁ?ÈDJ³yÂ?Ø‚ÞCÀ?‡Áü2Á?ÖÈ®´ŒÔ¿?ÒŽ~7ÝÀ?‹qþ&"À? EºŸS¿?}^ñÔ# ¾?µmÁ¿?‡ü3ƒøÀÀ?áµKKÁ?{…÷Â?•*Qö–rÀ?1 Xr‹¿?,œ¤ùcZ¿?À¯‘$WÀ?AG«ZÒQÀ?ØdzˆF¿?à‚lY¾À?À"¿~ˆ À?È éðÆ¿?‹Áôoî¿?ÏžËÔ$x¿?L8 ¥¿?Ñy]¢z¿?úüáç¿¿?ÉË»ê¿?óWya¿?é_’ÊsÀ?ÏdÀ?_%» ”À?%•C‹À?þDeÚʾ?ì†m‹2À?üǙ&l¿?n0Ôa…[À?ì«an¿?EÓÙÉà¼?®c\qqT¾?|`Ç À?CYøúZ—À?“ªí&ø¦½?N ógš¼?äL¶ŸŒ½?ÿ .VÔ`¾?˜PÁá½?9 {Úá»?Ûmšë4¾?ƒ¾ôö碽?Þ„€|½?_ÔîW¾¿?7OuÈÍp¿?^gCþ™AÀ?ó:â½?+¢&ú|”½?W \âÈ»?Ÿä›È̽?)Ð'ò$é¾?§yÇ):’Á?æI›À?Å[ÌÏ Á?ØI}YÚ©½?'öÐ>Vð¿?žÎ¥„`½?žACÿ¿?ˆ‚S°Æ½?-ê“Üa½?I¡,|}­»?WÎÞm½?Òm‰\p¿?wR~Rí¿?˜öÍýÕãÀ?;ãûâR•¾?H‰]ÛÛ-½?S£’:½?ÖŒ ra¾?¢ÕÉŠ;¾?¢E¶óýÔ¼?צ±½ô¾?emS<.ª½?_Cp\ÆM½?&Šº}½?£çº½?ðûY,½?Ñèbg ½?­‡/EH½?/†r¢]…¼?>&RšÍã¼?ØF<ÙÍŒ¾?ŠY/†r¢½?zƾdãÁ¾?i§ærƒ¡¾?LnYk¼?‹ŒH¾½?UˆGâåé¼?׈`\:¾?,cC7û½?Ot ‡º?>U£W¼?zqÈÒ½?¿¹¿zÜ·¾?{ž?mT»?pê”G7º?‹O0žA»?ÅUeßÁ»?çâo{‚ĺ?Îm½2o¹?«• ¿ÔÏ»?º»Î†ü3»?ûêª@-»?è-Þs`½?áÔ’w½?ö)Çdqÿ½?K= By»?Ñ>VðÛ»?¨ŒŸq¹?Ý[‘˜ †»? Ùy›½?™ðKý¼©À?«{dsÕ<¿?R º½¤1À?ú¸6TŒó»?C¨R³¾?.VÔ`†»?Í9x&4½?Qö–r¾Ø»?~4œ27»?Õ—¥šË¹?è/ôˆÑs»?‰ëW\½?.çR\Uö½?ÉŽ@¼®¿?²t±i¥¼?òxZ~à*»?éH.ÿ!ýº?uÍä›mn¼?!>°ã¿@¼?ÓÁú?‡ùº? ´¾L½?¼§>¼»?f»B,c»?LüQÔ™»?K= By»?òxZ~à*»?ΈÒÞà »?8Ÿ:V)=»?::ZÕ’º?*Wx—‹øº? Q¾ …¼?ÍsD¾K©»?9b->À¼?™œÚ¦¶¼?Í?ú&Mƒº?0¼’ä¹¾»?=Ô¶a»?x]¿`7¼?Åã¢ZD»?•·#œ¼¸?mÊÞå"º?ë««µ¼?0ôˆÑs ½?4óäš™¹?à øü0B¸?“o¶¹1=¹?²Úü¿êȹ?ª·¶J°¸?Q¿ [³•·?õÙ×3º?'Þž´p¹?PáR¹?@øP¢%»?–Ïò<¸;»?åïÞQcB¼?b„ðh㈹?Ý"0Ö70¹?Tüߪ·?f…"ÝϹ?Ù²|]†»?–x@Ù”¿?)wŸã£Å½?. ø1æ¾?Gå&jiº?ÉüI‚¼?=Ú¨Nº?#†Ƥ¿»?‡m‹2dº?о¢[¯¹?,ñ€²)W¸?M¿D¼uþ¹?{/¾h»?öA–¼?Êþy0H¾?Àϸp $»?1A ߺ¹?à?ÿ=x¹?Ééëùšåº?“ŠÆÚßÙº?-!ôlV¹?øo^œøj»?DMôù(#º?¡·xxϹ? Ö8›Žº?&â­óo—¹?€-¯\o›¹?ÃòçÛ‚¹?ÅS4¸­¹?Ð MÙé¹?8ù-:Yj¹?H¨REñº?Ñ=ë-º?\Æú&»?ù.¥.»?Ëõ¶™ ñ¸?½3Úª$º?+ö—Ý“‡¹?zƾdãÁº?£>É6‘¹?sf»B,·?u¬Rz¦—¸?é ÷‘[“º?D1yÌ|»?W=`2å·?­ÃÑUº»¶?À=ÏŸ6ª·?%wØDf.¸?[°Tð2·?®,ÑYf¶?t%Õ?ˆ¸?¯&OYM×·?MeQØEÑ·?1 òº?ÙÐÍþ@¹¹?÷vKrÀ®º?["œÁß·?I¡,|}­·?ý÷àµK¶?(Í9x&¸?8-xÑWº?UOæ}“¾?KÊÝçø¼? µ‰“û¾?ÊŒ·•^›¹?+ømˆñš»?_Cp\ÆM¹?Nïâý¸ýº?>Ëóà?k» ¾iú¸??¨Œ·?Þp¹5¹?¥]Pߺ?kƒѯ­»?žÒÁú?‡½?Ü ö[;Qº?´“ÁQòê¸?áñí]ƒ¾¸?çÄÚÇ º?V¼‘yäº? F³²}¸?¿¹¿zÜ·º?·³¯À¸?§/ú Ò¸?ÂÚ;á%¸? Q¾ …¸?ÊúÍÄt!º?3‰zÁ§9¹?:Xÿç0_º?‹ú$wØDº?Í‘•_c¸?E×…œO¹?:é}ãkϸ?ePmp"ú¹?N`:­Û¸?³ðõµ.5¶?ƒh­hsœ·?çá¦Óº¹?B&9 {º?ØsF”ö¶?¼‘yä¶?ö&†ädâ¶?5íbšé^·?ønóÆIa¶?u­½OUµ?è3 ÞŒš·?_¶¶F·?Û¾Gýõ ·?ŠriüÂ+¹?φü3ƒø¸?âú}ÿæ¹?¯}½pç¶?–~Tö?É;‡2Tµ?–{Y¡H·?Ÿ<,Ôšæ¹?XûVëĽ? B\9{g¼?eâVA t½?G 6u¹?Wèƒelèº?× /½ý¹¸?d°âTkaº?j'÷;¹?‹T[r¸?¯°à~À·?·Aí·v¢¸?›æ§èHº?€H¿}8»?uç‰çl½?áš;ú_®¹?}ëÃz£V¸?º«?Â0¸?| Vœj¹?G=D£;ˆ¹?8ýÚúé·?±i¥È%º?'À°üù¶¸?`Í‚9z¸?©„'ôú“¸?ø¬8ÕZ¸?ŽDÁŒ)¸?kE›ãÜ&¸?äÀ«åÎL¸?BZcÐ ¡·?¸?°È¯bƒ¹?‹3†9A›¸?ÞFN¶¹?CB•š¹?ž³„Ö÷?‹ßV*¨¸?!>°ã¿@¸?NA~6r¹?¤á”¹ùF¸?]Ot]øÁµ?ì…¶ƒ·? ´;¤ ¹?€Écë¹?¬ŒF>¯x¶?3&c`µ?ʉvR¶?›h>ç¶?‰]ÛÛ-ɵ?#/kb¯´?šÏ¹Ûõ¶?eÄ Q¶?¤7ÜGnM¶?¯˜Þ„¸? 0,¾-¸?9EGrù¹?þÖN”„D¶?³Dg™E(¶?”M¹Â»´?毹2¨¶?î$"ü‹ ¹?R û=±N½?¬ÿs˜//¼?!àFʽ?÷Xúй?gñbaˆœº?JFΞv¸??{óº?Mº-‘ θ?²^‚S¸?`‘_?Ķ?y ý\¸?“áx>ê¹?ܺ›§:äº?¯#Ù@º¼?-!ôlV¹?#½¨Ý¯¸?¦Õ°ß·?”k dv¹?L£uT5¹?ÆýG¦C§·?ìùšå²Ñ¹??N™›o¸?‹üú!6¸?® ãüM¸?½ÅÃ{¸?Ò7iÍ·?¤6qr¿·?ÊPSé·?›;ú_®E·?yè»[Y¢·?õ€yÈ”¹?iTàd¸?Q†ª˜J?¹?lîè¹¹?Äëú»a·?£’:M¸?+Û‡¼åê·?Ï„&‰%¹?biàG5ì·?Øñ_ µ?RÔ™{Hø¶?Ëõ¶™ ñ¸?êçME*Œ¹?åÒø…W¶?2âÐ(µ? š²Ó¶?"¦D½Œ¶?öBÛÁˆµ?Y2Çò®z´?ãQ*á ½¶?À"¿~ˆ ¶? ®¹£ÿµ?¢ÏGq¸?a6†åÏ·?JÑʽÀ¬¸?<ø‰è÷µ?Ï×,—ε?SëýF;n´?¥Kÿ’T¶?Eƒ<…\¹?¥¡F!ɼ?¹þ]Ÿ9ë»?š#+¿ Ƽ?û”c²¸ÿ¸?ãÂ,`º?CSvúA]¸?j’Ìê¹?Iö5Cª¸?¦`³é¸?Þvøk²¶?+Ôð-¸? K< lʹ?,*ât’­º? šyrM¼?kšwœ¢#¹?o»ì·?Û$¶»·?Ǹââ¨Ü¸?†®D ú¹?uÌyƾd·?GéÒ¿$•¹?ܵÛ.¸?ßÞ5è·?WA tí ¸?õL/1–é·?iTàd¸·?¾Û¼qR˜·?,×Ûf*Ä·? Tÿ ’!·?D…êæâo·?jÜ›ß0Ѹ?A¶,_—á·?œ5x_• ¹?á(yu޹?¬ÿs˜/·?„Ö׉"¸?"«[='½·?ÝCÂ÷þ¹?m‘´}Ì·?F—7‡kµ?çmlv¤ú¶?ß,Õ¼¸?¥e¤ÞS9¹?tðLh’X¶?„+ POµ?ºÛõÒ¶?ݵßÚ‰¶?¦œ/ö^|µ?y ý\´?±¾É¶?t ‡Þâµ?Šä+”ص?œ¥d9 ¥·?Æù›Pˆ€·?úGߤiP¸?“ `ÊÀµ?Øî<ñœµ?¶+ôÁ26´?ýKR™b¶?}iÆ¢é¸?B žB®Ô»?º ¾eN»?ÊŠ;Þä»?"¨½ ¸?‘{ººc±¹?¶ƒû¸?w„Ó‚}¹?fÝ?¢C¸?D5%Y‡£·?¯ê¬Øc¶?aâ¢ÎÜ·?Õ°ßëT¹?<Û£7ÜGº?$˜jf-¼?)ÎQGÇÕ¸?Î'…y·?U ƒ‡i·?~£<ór¸?Ižëûp¸?Xc'¼·?ИIÔ >¹?ì£SW>Ë·?4fõ‚·?©‡ht±·?1 Xr‹·?sñ·=Ab·?XŽ<·?œh>çn·?õŸ5?þÒ¶?JbI¹û·?Òýœ‚ül¸?Þ¯|·y·?ÓÜ a5–¸?ÑvLÝ•]¸?‚)[$í¶?Ófœ†¨Â·?Ìa÷Ãc·?‹3†9A›¸?ƒ5Φ#€·?ßýñ^µ2µ?ò²&øŠ¶?ÈбƒJ¸?ÐÖÁÁÞĸ?Þs`9B¶?¬Å9êè´?>w‚ý×¹µ?OXâeS¶?™ƒ £U-µ?½ÅÃ{´?Î7¢{Ö5¶?±£q¨ß…µ?èÙ¬ú\mµ?–˜g%­ø¶?»_ønó¶?õL/1–é·?öE™ 2µ? dv½Sµ?ðˆ ÕÍų?à?ÿ=xµ?Ö:q9^¸?«#G:#»? ô‰å˜,¶?lë§ÿ¬ùµ?áy©Ø˜×µ?È@ž]¾õµ?mXSYvµ?K­÷í¸µ?ñð¤…˶?½åêÇ&ùµ?Þ®Õö¶?- PSËÖ¶?n‹2d’µ? @£té_¶?ßN"¿¶?B˜Û½Ü'·?£ý…1¶?® µ?L5³–¶?ñó߃×.µ?ë8~¨4¶?ZÔ'¹Ã&¶?"ߥÔ%ã´?#0Ö70¹µ?¸æŽþ—kµ?¯ê¬Øc¶?>@÷åÌvµ?I,)wŸã³?GÇÕÈ®´´?JDøAc¶?›©¾ó‹¶?ò%TpxA´?–wÕæ!³?ÇAœ‡´? Q¾ …´?(›r…³?XŒºÖÞ§²?ñº~Án´?ˆ„ïý Ú³?Õ•Ïò<¸³?­iÞqŠŽ´?Ÿ2âд?f…"Ýϵ?•œ{h³?Á=~o³?¸àŸR%²?}ÍrÙ蜳?Vïp;4,¶?‡4*p² ¸?4,F]kï·?†óþ?N¸?Êþy0H¶?î@òè¶?R û=±Nµ?WCâK¶?ƒ3øûÅlµ?±5[yÉÿ´? „bÕ ´?ÇhUMµ?ƒù+d® ¶?]¤P¾¾¶?Bí·v¢$¸?ç9"ߥԵ?¬ä.´?À~þ{´?/PR`Lµ?ñ~Ü~ùdµ?¬ÿs˜//´?‚ý×¹i3¶?Åþ²{ò°´?•ص½Ý’´?MÛ¿²Ò¤´?'À°üù¶´?ŒƒKÇœg´? PO?´?Š!9™¸U´?mp"úµõ³?DOʤ†6´?bI¹ûµ?“Úl@´?y¯Z™ðKµ?ŒM+…@.µ?G­0}¯!´?…{eÞªë´?;´TÞŽ´?¹ÁP‡nµ?¦ÒO8»µ´?*æ èhU³?pÒ4(š´?V)=ÓKŒµ?÷!o¹ú±µ?­hsœÛ„³?€Ÿqá@H²?ÿ®Ïœõ)³? 7U†q³?ûWV𔂲?‡O:‘`ª±?8Ÿ:V)=³?lèf Ü²?ëD2䨲?·˜Ÿš²³?mp"úµõ³?ni5$î±´?m©ƒ¼L²?ªÒ×øL²?ù½Mö#±?ø¥~ÞT¤²?Tþµ¼r½µ?¾ƒŸ8€~·?øí¸áw·?«=ì…¶·?Ãõ(\µ?Âõ(\¶?ãÞü†‰µ?=œÀtZ·µ?Ñ”~Pµ?S"‰^F±´?["œÁß³?³ÅVд´?]ûzµ?CÆ£T¶?£<órØ}·?"rúz¾fµ?â‘xy:W´?:ž%È´?,,¸ðÀ´?Ž •bGã´?œ¥d9 ¥³?#„GG¬µ?… £YÙ>´?=·Ð•´?'‚8'´?‰”fó8´?vâr¼ѳ?XåBå_˳?«• ¿Ôϳ?å%ÿ“¿{³?”‡…ZÓ¼³?^žÎ¥„´?Þ:ÿvÙ¯³?å*¿)¬´?r„Ѭ´?¦}sõ¸³?穹n´?ëW:ž%´?Ó¿$•)æ´?Ct 4´?7á^™·ê²?¤oÒ4(š³?Z+Úç6µ?ŠÊ†5•Eµ?Êû8š#+³?oñðž˱?<¡×ŸÄç²?4/‡Ýw ³?}=_³\6²?¦D½Œb±?ìÙs™š³?GW#»²?Yà+ºõš²?–{Y¡H³? ñH¼<³?Z.óS´?2Ì Úäð±?Í\àòX3²?w ùg±?ïäÓc[²?˾+‚ÿ­´? ÇóPo¶?m¡õðe¶? 5?þÒ¢¶?ú~j¼t“´?&6׆е?óùõC´?Ɖ¯vç´?çQñGT´?‚pêé³?¹4~á•$³?h׿ë³?¼­ôÚl¬´?±ÀWtë5µ?¬äcw’¶?¸éÏ~¤ˆ´?ºžèºðƒ³?€ôMšE³?š&l?ã³?5 ´;¤´?g|_\ªÒ²??U…bÙ´?©¤N@a³?mýôŸ5³?“qŒdP³?ƒ5Φ#€³?IƒÛÚÂó²?_¶¶F³?*´t³?†!rúz¾²?¸"1A ß²?Нv稳?ÎýÕã¾Õ²?†©-u׳?»ÕsÒûƳ?ÖÇCßÝʲ?~¨4bfŸ³?· b k³?$ð‡Ÿÿ´?{Cr2q³?7Û$²?/àe†²²?Š!9™¸U´? š]÷V´?|~!<²?E>‘'±?-“áx>²?+hZbe4²?µˆ(&o€±?Æ‚”°?·Õ¬3¾/²?œ27߈î±?2Ì Úäð±?@ÀZµkB²?âvhXŒº²?€Ó»x?n³?çÇ_ZÔ'±?¸u7O±?1 òn°?Ó1çû’±?Ü»}éí³?0º¼9\«µ?†txã§µ?ôzÄè¹µ?Ѱu­½³?•·#œ¼´?­5”Ú‹h³?œà›¦Ï´?)êÌ=$|³?¨Sݳ?'/2¿F²?4Ûú`³?´:9Cqdz?èØA%®c´?€¸«W‘ѵ?øùïÁ³?êìdp”¼²?Ëd8žÏ€²?M ˆE ³?YRî>ÇG³?“å$”¾²?1&ý½´?n„EEœ²?‘~û:p²?ê<*þ?sõ¸oµ²?"Ã*ÞÈ<²?׈`\:²?Ö­ž“Þ7²?^ÚpXø±?WCâK²?8 ¥+ز?jh°²?¸ŸF³?Æ‹…!rú²?ͰQÖo&²?ôÄs¶€Ð²?i¢²?)_ÐBF³?²×»?Þ«²?¿)¬TPQ±?ñÿ²?Zd;ßO³?%±¤Ü}޳?ƒ3øûÅl±?ë6¨ýÖN°?Zd;ßO±?šë4ÒR±?B[Î¥¸ª°?æXÞU˜¯?8IóÇ´6±?³—m§­±?È—PÁá±?æ®%䃞±?´”,'¡ô±?±Pkšwœ²?]¥»ël°?RC€ ˆ°?ܵÛ.4¯?¯{+Ô°?ué_’ʳ?º†Oµ?ýkyåzÛ´?EœN²Õå´?˜¢\¿ð²?éI™Ôд?sõ¸oµ²?0ÕÌZ H³?‚,`·²?ƒÜE˜¢\²?`"Þ:ÿv±?Çô„%P²?ÓÁú?‡ù²?±ù¸6TŒ³?ŸçOÕé´?!Z+Úç²?Zœ¡¸ã±?r£ÈZC©±?7oœæ=²?^ô¤‹²? YÝê9±?Ýxwd¬6³?l´è¡¶±? À?¥J”±?AÕèÕ¥±?t ‡Þâá±?dY0ñG±?0×¢h[±?QÞÇÑY±?eS®ð.±?LÆ1’=B±?.çR\Uö±?Év¾Ÿ/±?32È]„)²?*á ½þ$²?BëáËD±?C;§Y Ý±?®I·%rÁ±?³Ïc”g^²?¾‰!9™¸±?FaE|°?->Àx±?6Vbž•´²?ó‘”ô0´²?#/kb¯°?“x]¯?•€˜„ y°?´¬ûÇBt°?Ã.Šø°?IVñF®?%ZòxZ~°?:ÏØ—l<°?á (ÔÓG°??U…bÙ°?Vš”‚n/±?-@ÛjÖ±?ö~£7ü®?úüá翯?œiÂö“1®?·Ï*3¥õ¯?ž˜õb('²?-y<-?´?Ëø÷´?÷®A_zû³?µÃ_“5ê±?W•}W³?Ü:åѱ?ê<*þ?o»ì×±?8MŸp]±?¶ö&†°?Û£7ÜGn±?äf¸Ÿ²?™ò!¨½²?$ð‡Ÿÿ´?¥/„œ÷ÿ±?€îË™í ±?ܸÅüÜа?`ÊÀ-]±?¾Ý’°«±?w×Ùf°?e‹¤Ýèc²?š'×Èì°?Ý 7àóð?àI —Uذ?Þrõc“ü°?D†U¼‘y°?fh<Äy°?iÀ"¿~°?7n1?7°?5B?S¯[°?ò"ðk$±?jõÕUZ°?è-Þs`±?=a‰”M±?œÃµÚÃ^°?>Y1\±?å „bÕ°?Îqn?fO›sð°?Å7>[¯?(Í9x&°?s‚69|Ò±?¢Busñ·±?s×òA¯?¬ÉSVÓõ¬?ðß¼8ñÕ®?ÄÑUº»Î®?hñÿ­?b k_@/¬?FAðøö®?îîº/g®?üŠ5\äž®?-Ë×eø¯?á² ›.°?ÎSr3ܰ? p­?Šÿ;¢B­?X zR&5¬?4iSul®?6<½R–±?Ø*Áâpæ³?†1zn¡³?ÙvÚŒ³?ñKý¼©H±?ýh8en²? -ëþ±±?l“ŠÆÚß±?åìÑV%±?äÖ¤Û¹°?*¬ÿs˜¯?×L¾ÙæÆ°?#ùJ %v±?xÑWf,²?JÐ_裳?g)YNB±?±3…Îk°?å—Á‘(°?8Ûܘž°°?Òo_α?µSs¹ÁP¯?eqÿ‘éб?©ù*ùØ]°?| Vœj-°?»—ûä(@°?‚«<°S°?%±¤Ü}ޝ?÷Ç{ÕÊ„¯?‚‘—5±À¯?µ:uå«?`ºò©?¶eÀYJ–«?ía/°¬?§x\T‹ˆª?·ÑÞ©?r4GV~¬?¤8GW«?ÏkìÕ[«?ãOT6¬©¬?óÆIaÞã¬?ÃFY¿™˜®??ªa¿'Ö©?ûY,Eª?&Î5ÌШ?­¢?4óäª?è3 ÞŒš¯?Wzm6Vb²?r‰#D²?ǹM¸Wæ±?JbI¹û¯?²,˜ø£°?:;%¯®?~£<ór°?–#d Ï.¯?^¹Þ6S!®?ãþ#Ó¡Ó«?DMôù(#®?„~¦^·°?£ x|{×°?HÅÿQ¡²?ßû´W¯?8, ü¨†­?35 ÞF­?¯ ?8Ÿ:®?ÛÂóR±1¯?…±… %¬?)ßÞ5°?lâuý‚­?D¤¦]L3­?2ZGUD­?–´â Ÿ­?l®šçˆ¬?äRìh¬? š–X¬?_ÔîW¾«?f1±ù¸6¬?HS=™ô­?f1±ù¸6¬?( 5 I®?ªÒ×øL®?]©gA(ï«?>]ݱØ&­?4hèŸàb­?²ºÕsÒû®?uÿwD­?Æ2ýñÖ©?@üü÷൫?*Wx—‹ø®?iâàI ¯?ʉvR~ª?ó¬¤ßP¨?Ìyƾdã©?CŒ×¼ª³ª?Ùî@ò¨?Ý(²ÖPj§?A~6rÝ”ª?¹nÀ燩?W°x²›©?,œ¤ùcZ«?Ò¬lò–«?½Ãíа­?Ky ²¨?ùõCl¨?]Þ®Õ¦?ª¸q‹©?4hèŸàb­? ¥ö"ÚŽ±?‘ð½¿A{±?ïá’ãNé°?LàÖÝ<­?º ¾eN¯?}Ê1Yܬ?ñœú@ò®?º2¨68­?ß¿yqâ«?ÁŠS­…©?[Î¥¸ªì«??{ó®?3¥õ·à¯?”ÝÌèGñ?"àªÔ¬?+iÅ7>«?³A&9 «?¦´þ–ü«?yW=`2­?-@ÛjÖ©?»Õ”d®?0-ê“Üa«?”ÁQòê«? ^ô¤«?f»B,c«?eßÁÿVª?)Z¹˜ª?åÐ"Ûù~ª?žz¤Ámm©?®,ÑYfª?À=ÏŸ6ª«?}²b¸:ª?¬Så{F"¬?I,)wŸã«?O>=¶eÀ©?S ³³èª?a§X5«?ógš°ý¬?0 Xr«?2rö´Ã§?åòw﨩?Y¤‰w€'­?ëTùž‘­?ú~j¼t“¨?rÁüýb¦?€ñ ú'¨?ÐÏÔë©?ÌÑã÷¦?^‘šv1¥?ì ×1®¨?¡l\ÿ®§?zÇ):’˧?Z+Úç6©?Ó…Xý†©?ÁR]ÀË «?1îÑZѦ?üâR•¶¸¦?¹ÿÈtèô¤?,H3Mg§?$*T7«?ÒŒEÓÙɰ?Kªɰ?‡3¿š°?†vN³@»«?Tþµ¼r½­?þœ‚üläª?¥ßPøl­?›ÆöZÐ{«?q¯Ì[uª?;oc³#Õ§?«&ˆº@ª?]~p>u¬?[ÏŽY®?•!ÿ°?.VðÛ¨?I€šZ¶Ö§? îêUdt¨?g×½‰ ª?ùómÁR]¨? s‚69|ª?&ßlsczª?æ¾÷7¨?eû·\ý¨?+*ÿZ^©?{Cr2q«?ôiý¡©?O’®™|³¥?ÝÑÿr-Z¨?hæÉ52«?«w¸«?·›à›¦Ï¦?_%» ”¤?9ÕZ˜…v¦?â¶ôhª§?ßÜ_=î[¥?5™ñ¶Òk£?‘ñ(•ð„¦?íE´Sw¥?M,ðÝz¥?Ïg@½5§?æÍáZía§?=órØ}Ǩ?l³±ó¬¤? ß÷o^¤?ûèÔ•Ï¢?‰ëW\¥?æ}“¦A©?´W}¯? ßû´W¯?Ü.4×i¤­? k_@/Ü©?-σ»³v«?vß1<ö³¨?•'vŠU«?Úç6á^©??;àºbF¨?Øõ vö¥?±k{»%9¨?v¦Ðy]ª?ÅE¹‡¬?KæXÞU°?t È^ïþ¨?¥J”½¥œ§?Y1\q§?™€_#I¨?s÷9>Zœ©?Û¦¶ÔA¦?Ïõ}8Hˆª?ý¾óâħ?l¸ [–§?²€ ܺ›§?["œÁß§?ožê›á¦?/0+é~¦?Ž¿·éϦ??VðÛã¥?"úµõÓ¦?ó!¨½¨?¿Òùð,A¦?穹n¨?Ø Ûe6¨?Á­»yªC¦?†¬nõœô¦?ð¥ð Ùu§?ªÕWWj©?€Ó»x?n§?l dv½£?XøQ û¥?±á镲 ©?â[X7Þ©?õfÔ|•¤?ŒÙ’Un¢?,ºõš¤?è1Ê3/‡¥?J¶ºœ£?>Y1\¡?<š$–¤?¨T‰²·”£?ùö®A_z£?gµÀ)¥?35 ÞF¥?ôpÓiݦ?}гYõ¹¢?ž#ò]J]¢?ëTùž‘ ?Cª(^em£?Šå–VCâ¦?>Àx ­?+Üò‘”¬?nú³)"«?»òYžw§?­P¤û9©?õ-sº,&¦?¯èÖkz¨?ÆüÜД¦?å`6†¥?qW¯"£?*ÖT…¥?­lò–«§?Ÿ·±©?æ@µm­?AG«ZÒQ¦?—qS¥?XŽ<»¤?ÊŒ·•^›¥?~Wÿ[ɦ?‚ëßõ™£?·D.8ƒ¿§?›Z¶Ö ¥?î'c|˜½¤?î'c|˜½¤?y$^žÎ¥?ú—¤2Ť?N]ù,Ï£?O#-•·#¤?ADjÚÅ4£?þEИIÔ£?HÀèòæp¥?-Z€¶Õ¬£?=HO‘CÄ¥?¨V_]¨¥?šë4ÒRy£?Í‘•_c¤?ö$°9Ϥ?Æ¡~¶¦?„œ÷ÿq¤?1{ÙvÚ¡?Xª x™a£?—Çš‘A¦?ÊRëýF;¦?¦bc^G¢?Ä QºôŸ?]Ot]øÁ¡?Ìz1”í¢?ŒôzÄ ?†=íð×d?NF•aÜ ¢?«‘]i¡?"ߥÔ%ã ?ì4ÒRy;¢? m9—â¢?z9ì¾cx¤?cD¢Ð²îŸ?‰îY×h9 ?nÞ8)Ì{œ?r¦ ÛOÆ ?ðúÌYŸr¤?‡¾»•%ª?)"¦?PÈÎÛØì ?·¸Æg²ž?° ÍX4?Ý Ì EºŸ?@¡ž> ?‡4*p² œ?“[ìö¡?“§¬¦ë‰ž?.7ê°Â?U¯²¶)ž??Ä 'iž?=‚)[$?GÇÕÈ®´œ?Ë,B±4?Ý µ‰“›?¯D ú‘œ?Š[1еŸ?‹PlMKœ?Š!9™¸U ?‘`ª™µ ?ƒ5Φ#€›?FИIÔ ž?À%W±ø?1]ˆÕa ?‡ú]Øš?qå Z˜?N 4Ÿs·›?ñd73úÑ ?0Hú´Šþ ?sŸˆ‚™?–Ð]gE”?)uÉ8F²—?‹ˆbò˜™?zqÈÒ•?IÚ>æ’?J +‡™?·Ï*3¥õ—? ר%ª—?ÿ .VÔ`š?/\sGÿ›?º„Coñðž?˜üOþî•?ž~P)”•?j…é{ Á‘?ï7ÚqÃï–?éðÆOãž?_%» ”¤?rÞÿÇ £?¤ÃC?£?NBé !ç?U¿Òùð,¡?\;Qi›?Êf/Ûž?Kê46œ?MÌ΢wš?¤§È!â–?ÄB­iÞqš?ð†4*p²?žî<ñœ- ?®ÕöB£?™×‡l ?ˆŸÿ¼v™?aü4îÍo˜?ÝAìL¡óš?gF?N™›?|,}è‚ú–?Q¡º¹øÛž?’we¨Š™?Úl@„˜?؃Iññ ™?s¼Ñ“2™?3ýñÖù—?êAA)Z¹—?J–“PúB˜?¦|ªF¯–?>u¬Rz¦—?5^ºI ›?ä¢ZD“—?{ö\¦&Á›? $(~Œ¹›?6ZôPÛ–?ú`º™?àõ™³>å˜?L§uÔ~›?>ë-z˜?¾ݳ®Ñ’?š]÷V$–?]QJVÕ›? JÑʽÀœ?†vN³@»“?3¥õ·à? m9—⪒?p–’å$”?f£s~Šã?}w+Kt–‰?ðùa„ðh“?£?4óäš’?Ánض(³‘?Æ5>“ýó”?&ãÉ¡–?'ÁÒ¨À™?mýôŸ5?7TŒó7¡?êu‘BYˆ?u­½OU‘?§wñ~Ü~™?³Ò¤t{¡?jP4`‘Ÿ?½Œb¹¥Õ ?¡J͘?õI̜?×¥Fègê•?÷[;Q™?Ž®ÒÝu–?:A›>é”?íÔ\n0Ô‘?†Èéëùš•?t–Y„b+˜?³{ò°Pkš?kÓØ^ zŸ?:ž%Ș?ßøÚ3K”?¾Á&S“?Ò‰Sͬ•?¬r¡ò¯å•?(œÝZ&Ñ?ä„ £Y™?˜£Çïmú“?2ãm¥×f“?ñ,AF@…“?aO;ü5Y“?–±¡›ý’?܃/¡’?ø¥~ÞT¤’?4GV~Œ‘?ŠsÔÑq5’?õg?RD†•?å(@̘’?"Ä•³w–?´”,'¡ô•?ÍæqÌ_‘?±RAEÕ¯”?®-9 p?#0Ö70¹q?¦(—Æ/¼b?ŒòÌËa÷]¿Ž‘ìj†t?×߀Ju?í+ÒSäp?‡ˆ›SÉ€? |(ђǃ?Ù|\*Ɖ?¨p©;Z?ôiý¡i?Ôº j¿µS¿éd©õ~£m?É&pënŽ? …8„*•?>v?‰{,}肊?5&Ä\Rµ}?Ö©ò=#z?œ'¾ÚQ|?m:¸Y¼x?xìg±Éw??¨‹ÊÂw?:ÏØ—lw?`­Ú5!­?(¸XQƒix?__ëR#ôƒ?¢+Üò‘„?`sž Mr?¢c•¸Ž?~£<órx? S"‰~?÷åÌv…>x?A‚âǘ»f?sµ4·Bx?6l±Ûg…?qs*ªˆ?]ú—¤2Ål?†7kð¾*W¿—=Ô¶aT?î=\rÜ)]?°t>d?,ºõšt?f`Xþ|{?&üR?o*‚?Ä™_Í‚‰?)v4õ»€?8fÙ“Àæl?Åã¢ZDc?JΉ=´u?¤6qr¿Cq?ôï9°\?6sHj¡d‚?¯þ·’k?iqÆ0'hc?ãàÒ1çi?§çÝXPd?a‹Ý>«Ìd?æmrø¤c?ÇIaÞãLc?Œu?TZ?l%t—ÄYa?¯ ?8Ÿ:v?Ôº j¿µc?ÛOÆø0{y?‹üú!6x?ñ»é–âO?y:W”‚u?ßÝÊef?ébÓJ!p?kׄ´Æ c?*6æuÄ![¿ûÌYŸrLV?1е/ w?½8ñÕŽâ|?¯þ·’[¿Õ‘#‘w¿F[•DöAf¿÷è ÷‘[c¿ˆØ`á$Ío¿€fØñ¿G‘µ†R{a¿p˜h‚§`¿t^c—¨Þj¿ÓÝu6äŸ9?M„ O¯d?"ÇÖ3„cv?Ùéu‘By¿FÒnô1p¿ÕýL½n¿1?74e§o¿v7OuÈÍ€?Œ‰BËŠ?Vº»Î†üƒ?ªî‘ÍUóŒ?Tÿ ’!Çv?MŒJê„?¨¦$ëpte?=·Ð•t?Þèc> Ði?ôï9°\?$Dù‚2¿‚¬§V_]e?T5AÔ}r?$EdXÅy?/4×i¤¥‚?´Éá“N$x?ÕÌZ Hû??ÓÝu6äŸI¿ô4`ôie?N)¯•Ð]R?<-?p•'P¿Ÿ·±y?ŒJê4A?êÐéy7D?F´Swe7¿›ÿW9Ò)¿õ×+,¸¿·_>Y1\¿Îp>?ŒP¿óWÈ\9¿Êß½£Æ„h?MÛ¿²Ò¤4¿QÝ\ümOp?™€_#Ip?µRäG^¿P”i4¹h?¢&ú|”'?ú  RðT?]§‘–ÊÛA¿½Œb¹¥Õp¿6:èR¿_–vj.7h?¨5Í;NÑq?A,›9$µp¿ª'ó¾Iƒ¿…BB•z¿Ýµ„|гy¿z4Õ“ùG¿@7n1?‡¿÷è ÷‘[s¿áy©Ø˜×q¿ \7¥¼v¿­lò–«_¿¾IÓ hþ>©|š“i?Åoò[t‚¿Lþ'÷Žz¿5Ð|ÎÝ®‡¿]n0Ôa…{¿q­ö° x?Ø›6ã„?àòX32È}?ûë܈? ß÷o^l?F^ÖÄ?a2U0*©?úC3O®)`?¾IÓ h>?F–̱¼«.¿ÐGqhd¿Þ3ßÁO?Vbž•´â[?ôiý¡i?+Qö–r¾x?lxz¥,Cl?Öª]ÒZ¿VJÏôci¿f.py¬9?D6.6­T¿H£'ÛÀm¿Ô x'Ÿn?¸®˜Þ^¿‚ÿ‚i¿_%» ”d¿Aš±h:;i¿ø§T‰²·d¿M„ O¯d¿¨¦$ëpte¿ƒMGÅÿm¿ífF?Ni¿!‘¶ñ'*K?›Žn/f¿ü‹ 1“¨W?¾‰ jøV?dæ—Çšq¿ú~j¼t“H?k~ü¥E}b¿ÛÃ^(`;X¿Ù'€bdÉl¿ÖÄ_Ñ­w¿}\*Æùk¿‡¨ÂŸáÍJ?êè¸Ù•f?©J[\ã3y¿#ö  ‰¿÷­Ö‰Ë¿g¹ltÎO¿÷ª• ¿„¿•C‹lç‹¿¬q6|¿ææÑ=ëz¿AfgÑ;€¿j¤¥òv„s¿ò'*ÖTV¿+~©Ÿ7U?…Ѭlò†¿ƒ¡+Üò¿Û4¶×‚Þ‹¿ŽyqÈ‚¿XÎüjp?GN¶;€?æmrø¤s?®óo—ýºƒ?‘¹2¨68Q?¯ ?8Ÿ:v?|b*ß3b¿ÅËÓ¹¢”@¿U¯²¶)^¿ß6S!‰g¿dæ—Çšq¿²Úü¿êÈa¿[ ³ÐÎiF¿†7kð¾*G?ì†m‹2k?Ð_è£çV?Âj,amŒm¿oÖà}U.t¿™cyW=`^¿’$W@¡n¿ú™zÝ"0v¿%]3ùf›[?ýÇWËm¿Ò¥I*s¿¸…ëQ¸n¿rö´Ã_s¿²ó66;r¿úœ»]/Mq¿O\ŽW zr¿fI€šZ¶v¿zUgµÀs¿cbóqm¨X¿ò]J]2Žq¿^ PjD¿®I·%rÁI¿±‰Ì\àòx¿*ÅŽÆ¡~W¿ž]¾õa½q¿_`V(Òýl¿ë6¨ýÖNt¿ö–r¾Ø{¿ÜµÛ.4w¿i¬ýíÑ[¿ümOØî?HŠÈ°Š7‚¿ CÇ*q¿aÝxwd¬†¿äIÒ5“o†¿Úç6á^‰¿F˜¢\¿¿o­mŽƒ¿ IJ™C‚¿ò”Õt=Ñ…¿mò–«{¿/ó:âp¿ª ãn­U¿“¬ÃÑUº‹¿Cý.l͆¿(CUL¥Ÿ¿¸u7O…¿E+÷³Ba?£ x|{w?€Ÿqá@Hf?sõc“üˆ?Í®{+S¿ˆ»zm?l\ÿ®Ïl¿h^»ï^¿+ømˆñj¿¾PÀv0bo¿µÑvLÝu¿&«"ÜdTi¿ÿ>ãÂ`¿ ¼“OmI¿â̯æÁ\?ümOØî?é ¸çùs¿ÿ< $}z¿Ž«‘]ii¿\Y¢³Ì"t¿`ºòy¿·_>Y1\ý>’]i©÷t¿Q¿ [³•w¿¬r¡ò¯åu¿¹û-Îx¿ˆ)t^cw¿c}“Ev¿ˆe3‡¤v¿¨ükyåz{¿¥Kÿ’T¦x¿w‚ý‡¿±ýdŒ³‡¿=Õ!7à ˆ¿BÍ*ŠW‰¿_)ËǺˆ¿ŠsÔÑq5‚¿©„'ôú“ˆ¿ñ»é–â¿y=˜Ÿ€¿ñ}q©J‹¿ Tƿϸ€¿—á?Ý@‡¿‰Ð6®‡¿}w+Kt–‰¿2ZGUD¿eŽå]õ€‰¿ÝµßÚ‰‚¿3¥õ·à¿ÁV ‡3¿.‹‰ÍÇ•¿ðmú³)’¿Í?ú&Mƒ’¿ê=•Óž’“¿·Ï*3¥õ—¿3d’‘³¿g}Ê1YÜ¿öyŒòÌË‘¿Ã×׺Ô¿8½‹÷ㆿ•¸ŽqÅÅ¿o.2•¿]3ùf›“¿Fу—¿og_yž’¿íÿ°¥Gs¿Ó–x@Ùd¿OYM×]w¿nùHJzX?@KW°x‚¿=ñœ- ´n¿C®Ô³ ”‡¿×¥Fègê…¿'ù¿b ‡¿`‰”fóˆ¿ù&3ÞVŠ¿ qåìц¿ñ.ñ˜…¿82üÁÀƒ¿„ äÙå[¿þÓ x'¿~p>u¬RŠ¿0ôˆÑs ¿óèžu†¿\;Qi‹¿ü´W¿<À“.«€¿ó‘”ô0´Š¿UܸÅüŒ¿á iTàd‹¿:d¯wŒ¿ÈбƒJŒ¿i©÷TN‹¿Åä 0óŒ¿0ôˆÑs ¿~U.TþµŒ¿^×/Ø Û†¿zVÒŠo(Œ¿ŠXİØ„¿–= lÎÁƒ¿èºê¿œˆ~mý„¿Ÿp]1#Œ¿±Ûg•™ÒŠ¿ Rðr¥Ž¿:=ïÆ‚Â¿2ZGUD¿€ ;¨Ä…¿´9Îm½‚¿ŒJê4‘¿«˜J?áì–¿1C㉠Γ¿ê³®+f”¿‡5•Ea•¿ìm3⑘¿äGˆ,’¿Ì³’V|C‘¿Õw~Q‚þ’¿„H†[Ï¿l±Ûg•‰¿ˆò-$`„¿1 íœf–¿fk}‘Ж“¿ô #½˜¿>—©Ið†”¿>³$@M-{¿Åä 0óm¿E×…œO}¿™ò!¨½¿‹‹£rµ„¿ú´ŠþÐÌs¿y7R¶HŠ¿ÿêqßjˆ¿'á_‰¿²GWéAØ)V ÂŒ¿á(yuމ¿QKs+„Õˆ¿äÜ&Ü+󆿶ŸŒñaö‚¿ñƒó©c•‚¿:Ì—`¿eª`TR'¿€J•({K‰¿´å\Š«ÊŽ¿`>Y1\¿4fõ‚Oƒ¿âX·ÑŽ¿z4Õ“ùG¿˜0š•íCŽ¿ë²×»¿H¾D„¿¾IÓ hŽ¿:±‡ö±‚¿®‚èÚ¿*Æ3h迚Ìx[鵉¿ Ü¶ïQ¿HÜÖž‡¿ßºñîȈ¿NGÅÿ‘¿ÝД~P‡¿Ò§Uô‡fŽ¿•*Qö–rŽ¿^žÎ¥„¿Ü½Ü'G’¿eª`TR'¿Täqs*‰¿ô¾ñµg†¿1`ÉU,~“¿{.S“à ™¿;:®Fv¥•¿^üo%;–¿1îÑZÑ–¿ýi£:Èš¿dT8‚”¿ñ,AF@…“¿0[wó”¿"àªÔì‘¿ CÇ*q¿‚‘—5±À‡¿ÔÔ²µ¾˜¿f3‡¤J–¿ ˜À­»yš¿E/£Xni•¿$‘—5¿`"Þ:ÿvy¿ŒøNÌz1„¿yxÏåi¿ÉËšXà+Š¿öa½Q+L¿ÏÛØìHõ¿üU€ï6oŒ¿î }°Œ ¿¸4J—Ž¿ñ»é–â¿yé&1¬Œ¿ÎOqxµŒ¿ Šcܚt["‡¿óÇ´6í…¿äqs*¿ÌFçüÇ‘¿-î?2:¿­Û ö[;‘¿Pÿ>ã‘¿ŸZ}uU †¿9 3¦¿À3‰z‘¿ž´pY…Í¿ÛÄÉýE‘¿´­fñ}‘¿~Œ¹k ù¿±ÉW)‘¿–Tÿ ’‘¿¸æŽþ—k‘¿tCSvúA¿/ÛN[#‚‘¿sIÕvŒ¿¬µ“ýó”¿àœ¥½Á—¿*¨¨ú•Η¿!àFÊ™¿3âÐ(]š¿­P¤û9™¿ÛûTˆ•¿…ëQ¸•¿‡Q<¾½›¿Îp>?Œ ¿ð‰Ð6ž¿@½5_%Ÿ¿t ]‰@õŸ¿JÒ5“o¶¡¿Až]¾õa¿û"¡-çRœ¿ªd¨âÆ¿œ4 Šæœ¿ûõ×+,˜¿Ó…Xý–¿1#¼=¡¿_b,Ó/Ÿ¿[éµÙX‰¡¿ˆØÒ£©ž¿¥-®ñ™ì¿®ð.ñˆ¿ñõµ.5B¿]7¥¼VB‡¿v¦Ðy]’¿an÷rŸŒ¿p² Ü:•¿$^žÎ¥”¿—Ép<Ÿ•¿FEœN²Õ•¿JDøAc–¿>ʈ @£”¿"¨½ ”¿|dsÕ+Nµ–¿Ð_è£ç–¿=ð1Xqš¿†ŽTâ:–¿ïV–è,³˜¿!æ’ªí&˜¿gaO;ü5™¿·zNzßøš¿7¥¼VBw™¿íE´Sw•¿Sz¦—Ë”¿» ÿé œ¿Ü~ùdÅ ¿yÎZŸ¿9DÜœJ ¿5cÑtv2 ¿¬ÆÖÆØ¡¿Lo.2ž¿•»ÏñÑ✿+£‘Ï+ž¿øXŽœ¿àõ™³>嘿€µjׄ´–¿u­½OU¡¿GN¶; ¿³˜Ø|\¢¿ ÞŒš¯’Ÿ¿”ùGߤi¿Ñ=ë-Š¿Ùƒkîè¿OYM×]‡¿kaÚ9Í’¿…³[Ëd8Ž¿xšÌx[é•¿M¾ÙæÆô”¿3#…–•¿Í°QÖo&–¿·&Ý–È—¿ˆht±3•¿Ððf ÞW•¿‡¢@ŸÈ“”¿ZÔ'¹Ã&’¿FÍWÉÇî’¿/1–é—ˆ—¿BÍ*ŠW™¿“âã²ó–¿5cÑtv2˜¿I‚pš¿Lÿ’T¦˜“¿.sž±/™¿²ŸÅR$_™¿¢E¶óýÔ˜¿Ù®Ð˘¿Í>Qžy™¿ÜŸ‹†ŒG™¿´è ¸ç™¿Ž!8ö왿é8h°™¿HPüs—¿»Ð\§‘–š¿ Ý!ʼn–¿žD„4–¿•Ö߀š¿' ‰°áé•¿GJ±£q˜¿in…°K˜¿F™ 2ÉÈ™¿ù0{ÙvÚš¿•-’v£™¿P3¤Šâ•¿‰xëüÛ•¿5š\Œuœ¿Ymþ_uä ¿Æiˆ*üž¿æèñ{›ž¿ÍwðП¿Ef.py¬¡¿áC‰–<ž¿qN`:¿\ætYLlž¿wJëÿœ¿À“.«°™¿ãŒaNÐ&—¿Sìhêw¡¿Ùƒkî蟿ÇeÜÔ@ó¡¿°8œùÕ ¿|š“™¿†Ê¿–W®‡¿4-±2ùŒ¿Ü¸ÅüÜЄ¿û²´Ss¹‘¿ýˆ_±†‹Œ¿ÿæÅ‰¯v”¿>”hÉ“¿™¹Àå±f”¿Å_Ñ­×”¿Z ¦–­•¿ú—¤2Å”¿fN—ÅÄæ“¿û!6X8I“¿ÁÅŠLÃ¿Ç ¿›nÙ‘¿2«w¸–¿ Й´©º—¿ÇK7‰A`•¿`wºóÄs–¿YŠä+”˜¿©KÆ1’=’¿iÅ7>[—¿¶il¯½—¿ 1—Tm—¿x]¿`—¿„d˜¿$ ÂP¨—¿ºhÈx”J˜¿ R O˜¿ÇF ^×/˜¿EÖJí•¿Ôšæ§è˜¿¯²¶)•¿&W±øMa•¿4ö%¶˜¿†¬nõœ”¿ëþ±—¿ÅTú g·–¿Ú­e2Ï—¿ÆOãÞü†™¿ò ú'¸˜¿µ¨Or‡M”¿vß1<ö“¿Á˜2p@›¿¨ÆK7‰A ¿k=&Rš¿Ûü¿êÈ‘ž¿#…–uŸ¿èú>$D¡¿ýN“o+¿¦ï5Çeœ¿ ¼“O¿ÔÖüøK›¿ñ˜õb˜¿:轕¿á(yuŽ¡¿Ý[‘˜ †Ÿ¿@¢ ±ˆ¡¿y ²HŸ¿Xÿç0_^¿‚¬§V_]…¿ì†m‹2‹¿,.ŽÊMÔ‚¿°‘$W@‘¿¡1“¨|Š¿Ý_=î[­“¿Þ®Õö’¿YÝê9é}“¿ÿ¬U»&”¿´¾L!•¿UÞŽpZð’¿þ'÷Ž“¿.2¥’¿AfgÑ;¿^=ð1X‘¿3#…–•¿ƒ1"QhY—¿dt@ö픿Ÿ”I m–¿¿ñµg–˜¿;%¯Î‘¿°Tð2Ö¿{ž?mT—¿C€ ˆ—¿Ãžvøk²–¿¿´¨Or—¿‹p“Qe—¿ìø/˜¿™*•Ô ˜¿Õ‘#‘—¿‰Î2‹Pl•¿*ãßg\˜¿4¢´7øÂ”¿n†ðùa”¿³–Òþ˜¿¼çÀr„ ”¿(ðN>=–¿2«w¸–¿]¿ðJ’—¿vÂKpꙿÙ] ¤À˜¿__ëR#ô“¿S!‰—§“¿ê K< lš¿£ZD“7 ¿UܸÅüœ¿¨PÝ\ü¿ç§8¼Zž¿¶õÓÖü ¿üSªDÙ›¿Ü¡a1êZ›¿ÙYLœ¿5{ ²š¿B{õñÐw—¿ƒS°ÆÙ”¿Ì_!se ¿P¨§Àž¿»ì×î<¡¿UkaÚ9¿²Ø&µ¿Öß—ª„¿¢*¦ÒO8‹¿)!XU/¿ƒ¿ÜGnMº-‘¿p_ÎQŠ¿ÚYôNÜ“¿.Ç+=)“¿¤aQ§“¿ï<ñœ- ”¿¢ñDçᔿ”¤k&ßl“¿«#G:“¿×gÎú”c’¿þÓ x'¿.â;1ëÅ¿2åCP5z•¿ƒNt —¿Öß—ª”¿üVëÄåx•¿•Öÿ9Ì—¿õc“üˆ_‘¿†q7ˆÖŠ–¿q:É–¿'f½ʉ–¿ÑÌ“k d–¿ïô¥·?—¿¿Hh˹—¿b†ÆAœ—¿O—¿'ÚUHùI•¿RÕQ÷˜¿hÊN?¨‹”¿l³±󬔿x¹ˆïĬ—¿{NzßøÚ“¿=ÓKŒeú•¿‡¯yUg•¿bX9´È–¿¶ºK⬘¿íGŠÈ°Š—¿’[“nKä’¿â"÷tuÇ’¿ª`TR' ™¿ƒ¦%VF#Ÿ¿ Ôbð0훿 ƈD¡e¿vŒ+.ŽÊ¿ JÑʽ ¿2¥žÐ›¿ þ~1[²š¿ð³%«"œ¿}=_³\6š¿˜Št?—¿‚SHÞ9”¿•ñï3. ¿ YÝê9é¿ï®³!ÿÌ ¿¾NêËÒN¿|µ£8G¿“ÆhUM€¿ŸrL÷‰¿¨5Í;NÑ¿cD¢Ð²î¿`ÉU,~Sˆ¿A~6rÝ”’¿n‹2d’‘¿Äê0 X’¿kaÚ9Í’¿É’9–wÕ“¿L5³–’¿SÏ‚PÞÇ‘¿¡0(Óhr‘¿¿ž¯Y.¿çãÚP1οú—¤2Å”¿ äÙå[–¿nÀ燓¿?ýgÍ¿”¿=,Ôšæ—¿Ižëûp¿PáR)–¿âvhXŒº–¿K”½¥œ/–¿ò”Õt=Ñ•¿‡Áü2W–¿Vïp;4,–¿é˜óŒ}É–¿Ù´Rä—¿ï7ÚqÃï–¿š †s 3”¿2tì —¿Lÿ’T¦˜“¿Òq5²+-“¿³³è ¸—¿Øî û’¿‰a‡1éï•¿›kC•¿¼Ì°QÖo–¿aºÙ˜¿ 'iþ˜Ö–¿ÙêrJ@L’¿ŠsÔÑq5’¿Jy­„î’˜¿M.ÆÀ:ž¿¤ý°Víš¿| Vœj-œ¿õfÔ|•|œ¿vþÓ  ¿ò^µ2á—š¿ÁãÛ»}™¿øý›'¾š¿eqÿ‘éЙ¿š]÷V$–¿Î8 Q…?“¿v“þ^ Ÿ¿mqÏdÿœ¿Ùµ½Ý’ ¿à ½úx蛿’?xî=Œ¿6t³?Pn{¿U2TqㆿÆk^ÕY-€¿Xç½Þ¿ÕËï4™ñ†¿sGÿ˵h‘¿ Ñ!p$п³]¡–±‘¿S•¶¸Æg’¿ëª@-“¿iŒÖQÕ‘¿a7l[”Ù¿O!WêY¿Ôœ¼ÈüŠ¿+Nµf¡¿~q©J[\“¿øŠn½¦•¿ÓÀÍâÅ’¿^Iò\߇“¿¥ƒõó•¿ Á¦Î£â¿fË-­†”¿µQd=•¿¹‹0E¹”¿;á%8õ”¿ p•¿0œk˜¡ñ”¿âàI —•¿#„GG¬•¿åðI'L•¿ø8Ó„í'“¿óÇ´6í•¿ò˜ù~’¿y\T‹ˆb’¿K±£q¨ß•¿0ñGQgî‘¿îì+ÒS”¿@‡ùòì“¿Âü2W•¿Þ擼—¿ž'ž³„–¿Á;ùôØ–‘¿¬Xü¦°R‘¿÷x!˜¿† %Ì´¿{× /½ý™¿¶ÚÃ^(`›¿o›©Ä›¿†U¼‘y䟿kóÿª#Gš¿ŠUƒ0·{™¿?p•'vš¿7ünºe‡˜¿è¼Æ.Q½•¿r0›Ã’¿‚sF”öŸ¿„aÀ’«Xœ¿‹Áôoª(^emSœ¿“r÷9>ZŒ¿ta¤µûu¿&ÿ”*Q†¿IJzZ|¿l—6–Ž¿„fÚþ•…¿$Ð`SçQ‘¿~©Ÿ7©¿%W±øM‘¿¢–æV«‘¿p°71$'“¿„H†[Ï¿Ù蜟â8¿S“à iT¿*´t‹¿±S¬„¹¿çqÌ_!“¿:A›>锿õ¼ ƒ’¿úÑpÊÜ|“¿?{ó–¿JbI¹û¿÷ª• ¿”¿'½o|확¿Dioð…É”¿è0_^€}”¿³ëÞŠÄ•¿›™EN·ìÿ°•¿ß4}vÀ•¿£êW:ž•¿¨ŽUJÏô’¿dê®ì‚Á•¿Ñ•Tÿ ’¿ à- ø‘¿¼?–¿ÞFN¶‘¿ÚŒÓUø“¿ÇHö5“¿VF#ŸW<•¿C«“3—¿zã¤0ïq–¿”¿{G ‘¿V,~SX©¿­‡/E˜¿Ð{cŽ¿:ç§8¼š¿õƒºH¡,œ¿ù*8œ¿ÅUeßÁŸ¿ l#ö™¿jg˜ÚR™¿ÕY-°ÇDš¿SäG˜¿yxÒÂe•¿ó9w»^š’¿Þå"¾³ž¿.5#ƒœ¿Õ¯t>‘¿kò”Õt=‘¿ÇðØÏb)’¿Eñ*k›â‘¿zßøÚ3K’¿³ï«r¡’¿Tå{F"4’¿í‚Á5wô¿eP3¤Š’¿îаu­¿àòX32È¿sƒ¡+Ü’¿Û†Q<¾¿[{ŸªB‘¿"‹4ñ¿u­½OU¡‘¿¶Û.4×i”¿.«°à’¿È·w úÒ‹¿£dVïp‹¿vß1<ö“¿=¸;k·]˜¿øü0Bx”¿¾l;m–¿ffffff–¿} yçP†š¿-?p•'–¿ÕyTüß•¿ŽÍŽTßù•¿jjÙZ_”¿ˆ NÒü‘¿‘+õ,å¿LkÓØ^ š¿ÒŦ•B —¿ëD2äØš¿QÜñ&¿E—¿3NCTáÏ€¿¾†à¸Œ›:¿Áá©iw¿ª ãn­e¿W³Îø¾¸„¿C¨R³z¿>Zœ1Ì Š¿R_–vj.‡¿fÁÄE‰¿¬<°S¬Š¿±ÀWtë5¿ðÐïû‡¿Ñ’ÇÓò‡¿\>’’††¿öa½Q+L¿ Xr‹ß„¿2ZGUD¿Æ3h蟿/¢í˜º+‹¿(‚8'0¿bMeQØE‘¿g¶+ôÁ2†¿úC3O®)¿ Ñ!p$пÂ26t³?¿¨Ï ¡¿ô7¡‡¿ü©ñÒMb¿ž´pY…Í¿žwcA‘¿*©ÐDØ¿HÝξò ¿Útp³x‘¿ÙÎ÷S㥋¿KW°x²‹¿Yá&£Ê¿ì…¶ƒ‹¿#KæXŽ¿Ï‚PÞÇÑŒ¿½á´àE¿.È–åë’¿THÞ9”‘¿)ë7Ó…ˆ¿·œKqUÙ‡¿8h°©“¿:“6U—¿û]Øš­¼”¿/Ý$•¿Š”fó8 –¿¶-Êl™¿i‚§+•¿`uäHg`”¿.‹‰ÍÇ•¿^fØ(ë7“¿CÅ8 ‘¿éEí~à‹¿Aó9w»^š¿ZGUDÝ—¿ùMa¥‚Šš¿Tÿ ’!Ç–¿.c}“{¿”ö_˜LU?ú`ºÙo¿Äî;†Ç~V¿u>—©Ið†”¿GW#»r¿CpìÙsi?j>"¦DR¿Ü ö[;QB?ç<š$v¿‡m‹2db¿¸Z'.Ç+€¿%»¶·{¿~©Ÿ7©€¿[AÓ+£¿,ºõš„¿]S ³³è}¿3ÀÙ²|}¿¢ ê[æty¿ Ôbð0ík¿`ºòy¿ßÅûqû僿JíE´S‡¿(»™Ñ†ƒ¿9ÒyYƒ¿3âÐ(]Š¿|Ô—¥z¿ 6uÿ‡¿æ”€˜„ ‰¿·Ï*3¥õ‡¿À"¿~ˆ †¿(¸XQƒiˆ¿kÔC4ºƒˆ¿­jIG‰¿ÀYJ–“PŠ¿ ÂÜîå>‰¿˜iûWVš„¿¹§«;‹¿íÿ°¥Gƒ¿Õíì+Òƒ¿2¯#Ù@Š¿¶ŸŒñaö‚¿‘*ŠWYÛ„¿XSYvQ„¿ pA¶,_‡¿ëqßj¸Œ¿­¢?4ó䊿ŒKUÚâ¿ã4ôO€¿òz0)>Ž¿._x%É“¿—⪲èKo.’¿ÁäF‘µ†’¿[ ³ÐÎi–¿ºi3NCT‘¿d=µú꪿þ¸ýòÉŠ‘¿öa½Q+L¿ÁŒ)XãlŠ¿ôú“øÜ †¿!‰—§s•¿ ²ºÕ“¿5Dþ o–¿ûËîÉÃ’¿ÓõD×…l¿(í ¾0i?¢&ú|”'?b£¬ßLLW?ñ*k›âqq¿ã©GÜÖV¿t^c—¨Þz¿`Xþ|[°t¿îÎÚmz¿W•}Wÿ{¿ý¢ý…¿Ùéu‘By¿EœN²Õåt¿|îû¯ss¿{/¾hW¿p À?¥Jt¿ë‹„¶œK¿¨½ 4„¿g¹ltÎO¿î^î“£¿/1–é—ˆ‡¿*T7Ûs¿–?ß,…¿6±ÀWtë…¿Æ5>“ýó„¿8Ÿ:V)=ƒ¿A¼®_°†¿j‰•ÑÈç…¿lèf Ü†¿s€`Ž¿‡¿ßÝÊe†¿Åã¢ZDƒ¿UQ¼ÊÚ¦ˆ¿Ä$\È#¸¿ª$ïÊ€¿àªÔ솿=`2åC€¿ž#ò]J]‚¿eS®ð.¿ßøÚ3K„¿)Íæqˆ¿?üü÷à…¿$EdXÅy¿ð¾**ÿz¿šë4ÒRy‹¿Ž\7¥¼V’¿‹RB°ª^Ž¿k,amŒ¿©MœÜï¿A€ ;¨”¿‘ñ(•ð„Ž¿ÔGà?ÿ¿J•({K9¿@ß,ÕŒ¿™Êø÷‡¿[AÓ+£¿X zR&5”¿[$íFó‘¿5s»—û”¿Ê7Ûܘž¿a2U0*©S¿ s‚69|r?ÎáZía/d?ƒ5Φ#€k?â !Ê´`¿)ÍæqH?סš’¬Ãq¿Õê««µh¿$cµùÕq¿î[­—ãu¿F~ý,|¿uÊ£aQq¿Gä»”ºdl¿Ðîb€Dc¿Ê¤†6P?5Ð|ÎÝ®g¿]n0Ôa…{¿Ï£âÿލ€¿k¹3 çz¿ž Ž’Wçx¿=·Ð•„¿_–vj.7h¿hìK6l¿€›Å‹…!‚¿w0bŸŠ¿¸®˜Þ~¿kQLÞƒ¿÷"ÚŽ©»‚¿$µP29µƒ¿ŒøNÌz1„¿Ánض(ƒ¿Y |E·~¿CÉäÔÎ0…¿2ZGUD}¿†ZÓ¼ã}¿ú(#.‚¿ZI+¾¡ðy¿[aú^Cp|¿ž Ž’Wçx¿*ÿZ^¹~¿ñaö²í„¿h+ømˆ¿uæn¿“«Xü¦°r¿®E жš…¿X9´Èv¾¿®¶bÙ=‰¿Ì¶ÓÖˆ`Œ¿Õ´‹i¦{¿ÛP1Îß„’¿F™ 2Éȉ¿”4LkÓˆ¿Mž²š®'Š¿-ê“Üa‰¿ùNÌz1”ƒ¿o¸Üšt{¿d–= lΑ¿º„CoñðŽ¿t#,*ât’¿xî=\rŒ¿úœ»]/M¿÷Ý—3Ûu?Ô|•|ì.p?°qý»>sv?f½ʉvE¿§v†©-u`?A‚âǘ»f¿}“EÖZ¿d°âTkaf¿^H‡‡0~j¿â‘xy:Wt¿¢µ¢Íqnc¿«B±læ`¿ ûrf»BO¿_%» ”d?Û¥ ‡¥_¿1`ÉU,~s¿ç4 ´;¤x¿‰ëW\u¿‚Ç·w úr¿î^î“£¿T^ PZ¿÷ʼUסz¿)èö’Æh}¿ò$éšÉ7{¿`=î[­w¿]S ³³è}¿uæ~¿Pª}:3€¿´ç25 Þ€¿µTÞŽpZ€¿ÎR²œ„Òw¿å ZHÀè‚¿j‰•ÑÈçu¿÷è ÷‘[s¿Ç{ö|¿Òà¶¶ð¼t¿1%’èet¿Ê‰vR~r¿Tâ:Æw¿åòw憎h^»ï~¿ÖJíE´]¿€·@‚âÇh¿w‚ý×¹iƒ¿Ô($™Õ;Œ¿[ ³ÐÎi†¿Ä$\È#ˆ¿j4¹눿tÑñ(•¿ƒ1"QhY‡¿S’u8ºJ‡¿¦~ÞT¤Âˆ¿Œ¾‚4cÑ„¿ºÀå±fd€¿­¥€´ÿv¿ô7¡‡¿ìjò”Õt¿°‘$W@‘¿iSul®Š¿f¡Ø šf?Þ­,ÑYf?´­fñ}?_%» ”„? Ž’Wçp?ràÕrg&x?À“.«°Y?¬ä.Âd?Lþ'÷ŽZ?°t>{?âàI —…?JΉ=´u?¹û-Îh?ùNÌz1”S?)[$íF_?²¼«0i?dËòuI¿zR&5´x?äRìhL?hY÷…è@?dËòuI?¸­-Y1\í>Rœ£ŽŽ«a?ú´ŠþÐÌc?½Ç™&l?i?st´ª%]?¯]ÚpXJ¿{£V˜¾×0?Ot]øÁùt?zpwÖn»p?ÎR²œ„ÒW¿/÷ÉQ€(x¿E×…œOm¿: ûvq¿!¯“âãs¿Ì)1 ‚¿š±h:;l¿z7ej¿úœ»]/Mq¿ÖJíE´m¿ÁÿV²c#P¿Û4¶×‚Þ[?+¢&ú|”¿æ?¤ß¾|¿ÿ”*Qö–‚¿>BÍ*Šw¿Óhr1Ö?Ó–x@‰?å™—Ãî‹?S°ÆÙt?[AÓ+£?)H4"†?E| V|?«7U†?ÛL…x$^~?ð¾**ÿz?É;‡2TÅt?]Þ®Õ~?¾…uãÝ?·˜Ÿš‚?/†Èéë‰?+¿)¬T€?å}Í‘u?¨:äf¸o?Ön»Ð\§q?J³yów?1е/ W?S"‰^F?[#‚qpéh?ª ãn­e?ú~j¼t“h?Ã}äÖ¤Ûr?W'g(îxc?<†Ç~Ka?8ºJw×Ù`?¬<°S¬Z?x›7N ó^?1'h“Ã'm?^ PjD?öµ.5B?s?—Mõdþq?÷…è8b?ìQ¸…ëq?m±o?\>’’†v?ºÀå±fdp?nYk(µW?žB®Ô³ d?¾ù  R€?cbóqm¨x?!¯“âãC?£’°o'q¿î[­—ã5¿t^c—¨ÞZ¿ÎˆÒÞà c¿å Åoò{¿úïÁk—6\¿W?6ÉøU¿ðPèyb¿tí è…;W¿¾¤1ZGUS?Ø€qåìm?PqxµÜy¿²fd»s¿ÈCßÝÊ}¿˜3Ûú`i¿ÇF ^×/ˆ?ûÍÄt!V?©¢x•µM‘?d?‹¥H¾’?_\ªÒ׈?€»ì×îŒ?§çÝXP„?ÿ˵hÚ†?¥Kÿ’„?3mÿÊJ“‚?߀cÏ~?ñaö²í„?„EEœN²…?g›Ó–ˆ?8KÉrJ?yÈ”A…?¯ÒÝu6ä?îÎÚmz?ƒÁ5wô¿|?¿ò =E?Ï/JÐ_èq?è‚ú–9]†?^¸sa¤w?¿ïß¼8ñu?ÿ$>w‚ýw?¬TPQõ+}?êÐéy7t?ÖÇCßÝÊr?~TÃ~O¬s?g Ü¶ïq?¦Õ°ßs?%xC8y?H£'ÛÀm?«Íÿ«Ž|?;ÿvÙ¯;}?’xy:W”r?Z.óS|?4óäš™}?ª$ïÊ€?”¥Öûv|?¨p©;j?ˆ…ZÓ¼ãt?„EEœN²…?[{C‚? Ž’Wçp?%És}B¿—=Ô¶ad?¯&OYM×S?-ÒÄ;À“F?Ælɪ7i¿nùHJzX?‘›á|~X?‘*ŠWYÛT?ƒú–9]S?e9 ¥/„l?• •-¯|?°t>”hÉãi‰?±ú# –Œ?ººc±M*Š?#LQ._ˆ?.9?oò[t²ÔŠ?V(Òýœ‚Œ?)èö’Æh?ŒÙ’Un’?XŽ<‹?-ÒÄ;À“†?æ>9 ƒ?${„š!U„?GT¨n.þ†?‹3†9A›|? Š·˜Ÿ‹?î$"ü‹ ?ßà “©‚?¯A_zûs?T1³Ïc„?EeÚʢ€?0Ùx°Ån?¥-®ñ™ì?1'h“Ã'}?ïU+~©?K‘|%‚?ùK‹ú$wx?óWÈ\„?+ÁâpæWƒ?€I*SÌA€?Ÿu–=„?…Ѭlò†?aÝxwd¬†?à+ºõš„?š±h:;|?B_zûsÑ€?Ï‚PÞÇÑŒ?&åîs|´ˆ?Ö©ò=#z?nùHJzX?ÒŦ•B w?ïU+~©o?Í΢w*àn?{®GázD¿1?74e§o?@ù»wÔ˜p?’³°§þj?„-vû¬2s?®ÕöB{?ù»wÔ˜ƒ?Ñ<€E~ýP¿]ú—¤2ÅL?ßPøl\¿™cyW=`^?¢–æV«‘?ô4`ôi•?Ã,`—?ï7ÚqÃï–? PSËÖú’?${„š!U”?ÊTÁ¨¤N?^-wf‚‘?ô‡fž\S?{g´UId?ëâ6À‹?HN&nÄ?ÇG‹3†9‘?9c˜´É‘?N·ìÿ°•?sž±/Ùx?È}«uârŒ?ߺñîȈ?|DL‰$z‰?/3l”õ‹?Øó5Ëe£ƒ?8‡kµ‡½?Ñ’ÇÓò‡?pî¯÷­†?®ž“Þ7¾†?TªDÙ[ʉ?äIÒ5“o†?O²žZ}…?¦^·Œ…? ¨lXSY„?DüÖM…?wLÝ•]0ˆ?‡m‹2d‚?ì3g}Ê1‰?•óÅÞ‹/Š?·´÷XŠ?½8ñÕŽâŒ?dËòuŽ?­¦ë‰® ?AØ)V ÂŒ?M„ O¯„?àõ™³>åˆ?äL¶ŸŒ‘?³´Ss¹Á?ðHPüƒ?–ëm3âq?Ot ‡~?-ê“Üay?GçüÇw?mäº)åµb?ÙÎ÷Sã¥{?Ÿ<,Ôšæ}?àõ™³>åx?ÅrK«!?¶J°8œù…?^gCþ™AŒ?Îà L§u[?z7ej? -ëþ±M?{ˆFw;s?Ü›ß0Ñ •?àØ³ç25™?]øÁùÔ±š?Ÿ¡¼£™?”ÃÕ—?¤Rìhê—?.=šêÉü“?º½¤1ZG•? ÆÁ¥cΓ?Áªzù&“?&9 {Ú‘?ò%TpxA”?î²_wºó”?É;‡2T•?×mPû­˜?ì†m‹2”?ˆ NÒü‘?É!âæT2?ôj€ÒP£?’²EÒnô‘?o¸Üšt‹?áñí]ƒ¾”?tbíc?9¸tÌyÆŽ?¸®˜ÞŽ?øS㥛Ä?Lo.2Ž?bùómÁR?{‚Äv÷?zVÒŠo(Œ?o×KS8? Ü¶ïQ?Ÿ¡¼£‰?Ì EºŸS?ðk$ Â?Чwñ~Œ?@ù»wÔ˜?˜Û½Ü'G‘?†:¬pËG’?[{ŸªB‘?2ZGUD?c%æYI+Ž?„¹ÝË}r”?² 0(Óh’?Žs›p¯Ì‹?8‡kµ‡½€?±Ûg•™ÒŠ?Tÿ ’!dž?mɪ7…?Iô2Šåv?ò'*ÖT†?\«=ì…†?0…Ì•A…?>]ݱØ&…?C¨R³Š?|Ô_¯°àŽ?¶ö?Àz?CÅ8 ?zóWÈ|?·Òk³±ƒ?ôiý¡™?ãÿލPÝœ?»ñîÈXmž?[wóT‡œ?ìhêwa›?È·w úÒ›?0 ÃGÄ”˜?ÁŠS­…™?P5z5@i˜?ZGUDÝ—?"ÇÖ3„c–?Á¨Sݘ?@ÜիȘ?,g*™?ÅÇ'dçmœ?ãÉ¡f˜?AŸÈ“¤k–?1 òn”?èy’tÍ”??{ó–?üÈ­I·%’?Ó„í'c|˜?8Ÿ:V)=“?€'-\Va“?3Mg'ƒ“?ÊQ€(˜1•?Í.5#“?i«’È>È’?‡m‹2d’?MöÏÓ€A’?{Cr’?2“¨|š“?&Ñ:ªš?o¹ú±I~”?÷ª• ¿”?(»™Ñ†“?Âß/fKV•?±Þ¨¦ï•?w;Sè¼–?R h•?1Ít¯“ú’?ü7/N|µ“?‰ïĬC™?M-[ë‹„–?íñB:<„‘?Æø0{ÙvŠ?ØÒ£©žÌ?ŪA˜Û½Œ?qªµ0 íŒ?åBå_Ë+‡?É&pënŽ?`x%És}?¶ò’ÿÉß?p]1#¼?¯A_zûs‘?%ËI(}!”?•~P)„?x¹ˆïĬ‡?È F³‚?ª¸q‹ù‰? †s 34ž?Š?Š:s¡?ÿ“¿{G¡?£•{Y¡ ?ð¦[vˆ ?ú~j¼t“ ?.9(a¦?1ì0&ý?®ºÕ”d?Mf¼­ôÚœ?;ÃÔ–:È›?TáÏðf ž?ÍX4 ž?ÞïU+ž?–ê^fØ ?5`ôi?\;Qi›?þEИ™?lÍV^ò?™?Œ.oך?ëþ±—?Ý ö_ç¦?Ë/ƒ1"Q˜?V-²ï—?Î5ÌÐx"˜?Ñ O!Wš?Ó¾¹¿zÜ—?&ÅÇ'd—?:vP‰ë—?ƒNt —?8ó«9@0—?¤5:˜?}@ 3iS•?•·#œ¼˜?8h°©ó˜?í ¾0™*˜?QôÀÇ`Å™?XŒºÖÞ§š?©iÓL÷š?Ș»–š?«A˜Û½Ü—?ƒÚoíDI˜?‚7¤Q“?Ñ–s)®*›?g,šÎN—?ÄAB”/h‘?h]£å@•?b.©Ún’?ûËîÉÃ’?XøQ û?.=šêÉü“?bøˆ˜I”?•~P)”?L⬈šè“?Ù[Êùbï•?Ä#ñòt®˜?n¤l‘´?b×övKr?rÝ”òZ ?‹¦³“ÁQ’?‘œLÜ*ˆ¡?¢]…”ŸT£?4„c–= ¤?|(Ñ’ÇÓ¢?ð¾**ÿ¢?ê”G7¢¢?PÈÎÛØì ?>”h¡?…{eÞªë ?³´Ss¹Á ?]¡·x ?MõdþÑ7¡?óWÈ\¡?¦D½Œb¡?%ÊÞRΣ?§<º¡?'ú|” ?²/Ùx°Åž?z3j¾Jž?þ™A|`ÇŸ?£#¹ü‡ô›?BëáËD¡?2=a‰”?=e5]Ot?ªæsîv?\Âõ(\Ÿ?Þ3ßÁOœ?š”‚n/iœ? ¡ƒ.áЛ?6:ç§8œ?ÎQÚœ?NÓg\Wœ?>w‚ý×¹™?éd©õ~£?®€B=}ž?û¯sÓfœ?ª*4Ëfž?Ñêä ÅŸ?]¥»ëlÈŸ?àŸR%Êž?â’ãNé`?Awòé±?¿rÞÿÇ¡? »(zàc ?{ö\¦&Á›?Îo˜h‚—?’Z(™œš?¸å#)éa˜?wþEИ?ì/»' •?¢(Ð'ò$™?¦',ñ€²™?Ûl¬Ä<+™?ÎR²œ„Ò—?-σ»³v›?I¹û-ž?z8é´n“?pxADjÚ•?[ìöYe¦”?»@j'—?Õ[[%X¤?dXÅ™G¦?ºÙ(§?ê?k~ü¥¥?“Ä’r÷9¦?[•DöA–¥?sò"ðk¤?$Ó¡Óón¤?n£¼¤?p–’å$¤?¦)œÞÅ£? ò³‘릤?𢯠ÍX¤?5·BX%¤?' ‰°áé¥?î±ô¡ ê£?’–ÊÛN£?™·ê:TS¢?ôNÜóü¡?t^c—¨Þ¢?(ó¾IÓ ?:#J{ƒ/¤?ý-ø§T¡?N(DÀ!T¡?žÒÁú?‡¡?¾ݳ®Ñ¢?õ»°5[¡?ض(³A&¡?+„ÕXÂÚ ?o+½6+¡?„Ó‚}¡?j¿µ%!¡?>ÍÉ‹LÀŸ?h+ømˆ¡?¼viá?‚‹5˜†¡?Š}"O¢?ù»wÔ˜£?GãP¿ [£?·ïQ½Â¢?Oæ}“¦¡?9c˜´É¡?פÛ¹à¤?ÖR@Úÿ£?-Í­Vc¡?gµÀ)?ð¦[vˆ ?M JÑÊ?tbícŸ?·zNzßøš?ƒ‡ißÜŸ? Áq75 ?Á9#J{ƒŸ?7R¶HÚ? àfñba ?q¯Ì[u¢?ÓJ!K™? ß÷o^œ?-σ»³v›?þ´Qd?ª™µö§?å ïrß©?ݵßÚ‰ª?kšwœ¢#©?y7R¶Hª?²¼«0©?ÍwðЧ?Àé]¼·§?¶†R{m§?§uÔ~k§?+½6+1§?ÛàDôkë§?8†àس§?F´Swe§?“ÇÓòW©?‹p“Qe§?9`W“§¬¦?ÛûTˆ¥?k» ¾iú¤?%uš¦?ûçiÀ é£?ÊSVÓõD§? šyrM¤?‡-y¤?9}=_³¤?Šä+”Ø¥?CSvúA]¤?;‰ÿ"h¤?K’çú>¤?½Â‚û¤?ê[ætYL¤?׆Šqþ&¤?oò[t²Ô¢?Ûúé?k~¤?«>W[±¿¤?”0Óö¯¬¤?²ó66;R¥?h^»ï¦?ôú“øÜ ¦?XûVëÄ¥?ñ.ñ˜¥?ÃbÔµö>¥?I€šZ¶Ö§? ËŸo ¦?³}È[®~¤?Ìyƾdã¡?TTýJç£?Î7¢{Ö5¢?’v5yÊ¢?ï9°!¡?=€E~ý£?#÷tuÇb£?v?T1£?lë§ÿ¬ù¡?ÐѪ–t”£?W[±¿ìž¤?‚”0ÓöŸ?SYvQô ?öÐ>VðÛ ?oÔ Ó÷¢?’!ÇÖ3„«?s-Z€¶­?“Ä’r÷9®?”0Óö¯¬?:°!y®?‘šv1ͬ?s ßû¬? õôø«?‹Š8d««?ÙvÚŒ«?Ž<»|«?Ÿp]1#¬?Õ=²¹jž«?ŸÌ?ú&M«?ИIÔ >­?¿ 1^óª?®J"û ˪?–ëm3â©?¾_´Ç ©?ùjGqŽ:ª?V¶yËÕ§?Æ‚”0«?jÜ›ß0Ѩ?¶ºœ“¨?³´Ss¹Á¨?Ûmšë4ª?ÒŒEÓÙɨ?[˜…vN³¨?›8¹ß¡(¨? Tƿϸ¨?kÔC4ºƒ¨?T;ÃÔ–:¨??§ ?¹¦?tÑñ(•¨?„ £U-é¨?§wñ~Ü~©?¢²aMe©?>•Óž’sª?ÀYJ–“Pª?îÎÚmª?¢–æV«©? —8ò@d©?ˆìø/¬?—Mõdþ©?sž±/Ù¨?P«”žé¥?ìOâs'ا?õ-sº,¦?º¡);ý ¦?=Òà¶¶ð¤?ÕËï4™ñ¦?—9]›§?#£’°o§?n/¦?&©L1A§?¯˜Þ„¨?©‡ht±£?G 6u¥?T­…Yhç¤?ðiN^d¦?«Ì”Öß°?Y¤‰w€'±?ö´Ã_“±?ÊÂ×׺°?eqÿ‘éб?¢A žB®°?Xÿç0_^°?¡×ŸÄçN°?ð¤…Ë*°?Pª}:3°?:ÏØ—l<°?ÒUº»Î†°?œ3¢´7°?…÷°?Ní S[ê°?‚‘—5±À¯?³³è ¸¯?L5³–Ò®?å`6†­?ùgñ¯?#Ûù~j¼¬?á' ß÷¯?ÉYØÓ­?zâ9[@h­?lâuý‚­?B˜Û½Ü'¯?ß4}vÀu­?«Ñ«JC­?/iŒÖQÕ¬? ¸çùÓF­?'…y3­?Øô  ­¬?Y…Íd«?æ!S>­?©öéxÌ@­?Ù³ç25 ®?XûVëÄ­?ÏJZñ …¯?ÕËï4™ñ®?[yÉÿäï®?ƒ¦%VF#¯?4œ27߈®?¬:«ö˜°?±‡ö±‚ß®?Fí~à»­?R %“S;«?íôƒºH¡¬?ù»wÔ˜«?þEИIÔ«?ªó¨ø¿#ª?øá !ʬ?íœfv‡¬?<š$–¬?›ÃòçÛª?lЗÞþ\¬?ŒÜÓÕ‹­?Ky ²¨?^‚SHÞ©?ù*8¼ ª? Hû`­ª??ÿ=xíÒ²?‰ @£té³?†óþ?N´?‹S­…Yh³?.â;1ëÅ´?Ì †:¬p³?œh>çn³?7ê°Â-³?¨qo~ÃD³?÷<Ú¨N³?#÷tuÇb³?®›R^+¡³?ÂÝY»íB³?$¶»è²?îv½4E€³?W ‡3¿²?ßp¹5é²?XU/¿Ód²?J&§v†±?¬à·!Æk²?ÊQ€(˜1±?¬o`r£È²?´Yõ¹Úб?ʈ @£t±?'Þž´p±?kÖß—²?‰±L¿D¼±?>Ëóà?Q29µ3L±?X7Þ«±?´­fñ}±?mu9% &±?Òýœ‚ül°? “©‚QI±?‘œLÜ*ˆ±??øùï±?ÙÐÍþ@¹±?·›à›¦Ï²?W"PýƒH²?Ó.¦™îu²?úñ—õI²?u”ƒÙ²?GßÛôg³?ôzÄè±?–çÁÝY»±?³}È[®~°?•}Wÿ[±?@Â0`ÉU°?Ñ<€E~ý°?J³yó¯?¥¡F!ɰ?~eÁı?Ü›ß0Ñ ±?Œô¢v¿ °?3úÑpÊܰ?‘ð½¿A{±?¦òz0)®?¤oÒ4(š¯?’çú>$°??;àºbF°?åñ´üÀU¶?n…°KX·?D5%Y‡£·?^+¡»$ζ?‰)‘D/£¸?”i4¹·?¨qo~ÃD·?øý›'¾¶?àKáA³ë¶?»·"1A ·?¨qo~ÃD·?ÐF®›R^·?Y¿™˜.Ķ?#¢˜¼f¶?ÝéÎÏÙ¶?‡Áü2W¶?®ž“Þ7¾¶?Oq¶?ÒƒNµ?EõÖÀV ¶? 34žâ´?µÝß4}¶?ÐÏÔëµ?¬TPQõ+µ?Ú‘ê;¿(µ?qâ«Å9¶?÷’ÆhUµ?¤ŠâUÖ6µ?ZHÀèòæ´?BëáËDµ?ï‘ÍUóµ?³\6:ç§´?'¢_[?ý³?e#Ù#Ô´?w¾Ÿ/Ý´?cc^G²µ?³z‡Û¡aµ?=™ôMš¶?Yùe0F$¶?46<½R¶?ëÇ&ù¿¶?“EÖ¶?G“‹1°Ž·?)#.Òµ?|'f½ʵ?þEИI´?4-±2µ? B²€ ´?B_zûsÑ´?“âã²³? Ñ!p$д?p² Ü:µ?/PR`Lµ?á (ÔÓ³?¹û-δ?_—á?Ý@µ?~âú}ÿ²?¥¢±öw¶³?,ºõš´?d²¸ÿÈt´?9´Èv¾Ÿº?¸ä¸S:X»?½¬‰¾¢»?l@„¸röº?ö&†ä¼?ˆñšWuV»?˜À­»yª»?úcZ›Æöº?\ã3Ù?O»?˜À­»yª»?S°ÆÙt¼?0™ò!¼?O"¿»?Ž;¥ƒõº?î%Ñ:ªº?÷ʼUסº?O­¾º*P»?{Øœƒgº?Å‘"‹4¹?ŸF6º? ´;¤ ¹?`V(Òýœº?îí–ä€]¹?”¢•{Y¹?‰zÁ§9y¹?CYøúZ—º?·FãàÒ¹?ŠY/†r¢¹?-B±4-¹?Ÿ·±¹?[•DöA–¹?UܸÅü¸?Ë+×Ûf*¸?:è¹?g¹ltÎO¹?rl=C8º?‰±L¿D¼¹?\X7Þ»?PÅÈ’9º?Ä%ÇÒÁº?9&‹ûL»?øý›'¾º?Ø*Áâpæ»?‚rÛ¾Gý¹?é~NA~6º?þ oÖà}¹?¥Û¹à º?4žâ<œ¸?ºÙ(·¹?I¢—Q,·¸?:x&4I,¹?V W@ܹ?:êè¸Ù¹?,ºõš¸?Ä ·|$%¹?±§þš¬¹?æèñ{·?c+hZbe¸?,ÒSä¹?éšÉ7Ûܸ?•Öÿ9Ì¿?øRxÐ캿?Ó‡.¨o™¿?ô66;R}¿?>Î4aûÉÀ?ÍXä¿?~įXÃEÀ?’ÍUó‘¿?Wéî:ò¿?éÖkzPPÀ?¾ˆ¶cê®À?M1AG«À?=)“ÚÀ?ߣþz…¿?øU¹Pù×¾?¸sa¤µ¿?±¿ìž<,À?ì£SW>¿?f¤ÞS9í½?i¢¾? Ö8›Ž¾?‘îçäg¿?¸Y¼X¾?Yùe0F$¾?¬JCB¾?Õ'¢_¿? 'LÍʾ?|E·^Óƒ¾?†!YÀ¾?PÂLÛ¿²¾?CUL¥Ÿp¾?6Y£¢Ñ½?ÓNÍåC½?–ëm3â½?¸Y¼X¾?ëZaú¾?*SÌAÐѾ?5Ô($™Õ¿?ΈÒÞà ¿?ú´ŠþÐÌ¿?v¨¦$ëpÀ?ý¾óâÄ¿?ÖâSŒgÀ?ⱟÅR$¿?)Bêvö•¿?,¼ËE|'¾?rßj¸¿?M¾ÙæÆô¼?vÝ[‘˜ ¾?’Ï+žz¤½?”Kã^I¾?G¬Å§¿?0žACÿ¿?°p’æi½?¿3‰¾?̘‚5ξ?¯yUgµ¼?åòw悔?Üôg?RD¾?<Û£7ÜG¾?Š‘%s,ïÂ?‚:åѰÂ?ŒØ'€bÂ?ž%ȨÂ?¬;Û¤¢Ã?®×gÎúÂ?l\ÿ®ÏÂ?1~÷æ7Â? S"‰Â?Ëõ¶™ ñÂ?Ó…Xý†Ã?YÝê9é}Ã?A'„ºÂ?¦´þ–üÁ?ó8 æ¯Á?0¹Qd­¡Â?AaP¦ÑäÂ?tD¾K©KÂ?¼ÏñÑâŒÁ?V W@ÜÁ?/‰³"j¢Á?…]=ð1Â?&ŒfeûÁ?aU½üN“Á?½Œb¹¥Á?j>"¦DÂ?»íBsFÂ?ÙYôNÂ?¶½Ý’°Á?Õz¿ÑŽÂ?mÇÔ]ÙÁ? $ ˜À­Á??ãÃìeÁ?·D.8ƒ¿Á?kÌÑãÁ?›8¹ß¡(Â?ܵÛ.Â?cAaP¦ÑÂ?ý¡™'×Â?ùdÅpuÂ?£:ÈzjÃ?â"÷tuÇÂ?(¹Ã&2sÃ?׿ë3g}Â?&Ñ:ªšÂ?¥ƒõóÁ?\-Ë×eÂ?©MœÜïÀ?¾K©KÆ1Â?@¤ß¾œÁ? 8KÉrÂ?˜`ºÂ?ò´üÀUžÂ?£:ÈzjÁ?8 ¥+ØFÂ?å*¿)¬Â?&¤à)äÀ?™Gþ`àÁ?’;l"3Â?#½¨Ý¯Â?îîº/gÆ?3SZKÆ?g¹ltÎOÅ?32È]„)Æ?9débÓÆ?uuÇb›TÆ?cšé^'õÅ?³^ åDÅ?ß—ª´Å?d¬6ÿ¯:Æ?ˆfž\S Ç?XÅ™GþÆ? a°ä*Æ?ÏÜCÂ÷þÄ?…]=ð1Ä?'‚8'Æ?’•_cDÆ?úÑpÊÜ|Å?Æ3hèŸàÄ?í+ÒSäÄ?Æ5>“ýóÄ?îCÞrõcÅ?3d’‘³Ä?…î’8+¢Ä?a7l[”ÙÄ?¨ŒŸqÅ?Í#0ðÜÅ?ì±¾Å?­NÎPÜñÄ? ™+ƒjƒÅ?¬¬mŠÇEÅ?ÇñC¥3Å?AaP¦ÑäÄ?ð2ÃFYÅ?Œ„¶œKqÅ?ã6À[ Å?>–>tA}Å?‰ jøÖÅ?J/…ÍÄ?Z!«[Å?µßÚ‰’Æ?F\¥KÅ?*ãßg\Æ?ÊýE>Å?°«ÉSVÅ?VeßÁÿÄ?g˜ÚRyÅ?Ëž6çàÃ?Z-°ÇDJÅ?¡¼£9²Ä? Ï.ßú°Ä?ÈDJ³yÆ?._x%ÉÅ?ù†Å?%è/ôˆÑÅ?€ñ ú'Æ?@ÛjÖßÃ?mìM Å?&Ä\RµÝÄ?XŽ<»Ä?å ÅoòÉ?é)rˆ¸9É?m‡ÁüÈ?K­÷í¸É?¨á[X7ÞÉ?Á=~oÓÉ?É=]ݱØÈ?‘¶ñ'*È?¥À˜2pÈ?"Ä•³wFÉ?G’ \…Ê?àºbFx{Ê?¿+‚ÿ­dÉ?ÅY5ÑçÇ?>Î4aûÉÆ?=›UŸ«É?· £ x|É?5š\ŒuÈ?Käõ`RÈ?Ú­ÀÕÇ?Ú<ƒù+È?ɪ7UÈ? õôøÃÇ?[C©½ˆ¶Ç?œà›¦ÏÈ?¤9²òË`È?ØFì@É?xíÒ†ÃÒÈ?E€Ó»x?È?Mò#~ÅÈ?¬äcw’È?qÈÒÅÈ?*Æù›PˆÈ? 2tìÈ?¢zk`«É?û]Øš­¼È?åBå_Ë+É?st´ª%É?f1±ù¸6È?ô£á”¹ùÈ?ùƒçÞÃÉ?ãý¸ýòÉÈ?„¼LŠÉ?_—á?Ý@É?pÍý/×È?â¶ôhÈ? á˜eOÉ?¼UË?Kè.‰³"Ì?RµÝß4É?p—ýºÓË?©iÓÊ?x*àžçOË?}±÷â‹öÌ?xÓ-;Ä?Ì?i©¼á´Ì?ÏdÎ?Jî°‰Ì\Î?¿cxìg±Ê?Kê46Ì?_—á?Ý@Ë?úA]¤PÌ?r¥ž¡Ð?¢ÏGqÐ?ªb*ý„³Í?‘ 9¶žÐ?½ 4Ô(Ð?ØF<ÙÍŒÐ?¨ŒŸqáÎ?›©¾óÍ? š]÷VÎ?Ë¿–W®·Ï?LŠOÈÐ?—nƒÀÐ?ÜFx $Ð?I÷s ò³Í?´æÇ_ZÔË?ÿ?N˜0šÐ?¬TPQõ+Ð?ºõš”Î?Ih˹WÏ?ž #½¨ÝÍ?’¯Rb×Î?âKº Î?÷@Î?­Mc{-èÍ?LR™b‚Î?„elèfÎ?+iÅ7>Ð?Ä[çß.ûÏ?<£­J"ûÎ?6­¹ÄÏ?¿Õ:q9^Ï?5cÑtv2Ð?TáÏðf Ð?ˆißÜ_=Ð?>çn×KSÐ?÷êã¡ïnÏ?>çn×KSÐ?¸!Æk^ÕÏ?O=Òà¶¶Î?¹Pù×òÊÏ?x¼W­LÐ?<2V›ÿWÏ?R×ÚûTÐ?óUò±»@Ð?T8‚TŠÏ?·Aí·v¢Î?ØEуÏ?û‘"2¬âË?vãÝ‘±ÚÎ?ý¡™'×Î?/ö^|ÑÎ?¼êó)Ð?X¬á"÷tÏ?¬Zd;Ð?GqŽ::®Ð?+0du«çÐ?ÑÌ“k dÎ?Ô—¥šÏ?W[±¿ìžÎ?BÑ<€E~Ï? R)v4Ò?7+1ÏJÑ?TTýJçÏ?¢Ð²î Ò?ô‹ôzÑ?+ÞÈ<òÒ?öF­0}¯Ð?dyW=`Ð?§ ?¹nÐ?’“‰[1Ñ?‘º}åAÒ?32È]„)Ò?w‚ý×¹iÑ?’`ãúÏ?ñ·=Ab»Í?{.S“à Ò?-Ðîb€Ñ?HnMº-‘Ð?_ PjÑ?¸<ÖŒ Ð?f¡Ó,ÐÐ?&â­óo—Ð?!ɬÞávÐ?TææÑ=Ð?¹Æg²žÐ?’Ìên‡Ð?¥Kÿ’T¦Ñ?8N ógÑ?|(Ñ’ÇÓÐ?óUò±»@Ñ?oÓŸýHÑ?.®ñ™ìŸÑ?Ù•–‘zÑ?3ù¼â©Ñ?Ív…>XÆÑ?ÊmûõÐ?cC7ûåÑ?D¢Ð²îÑ?Áàš;ú_Ð?:ÉV—SÑ?ÄÏ^»Ñ?2ÉÈYØÓÐ?•|ì.PRÑ?>Ì^¶¶Ñ?^fØ(ëÐ?RD†U¼‘Ð?8ÙîÐ? __ëR#Î?ð1XqªµÐ?w+Kt–YÐ?+ùØ] ¤Ð?Ú¨N²Ñ?ðlÞpÑ?,Öp‘{ºÑ?½ 4Ô(Ò?ê K< lÒ?ÒÈçO=Ð? š]÷VÑ?ÑÎihwÐ?»]/MÑ?wõ*2Ó?ˆ-y<-Ò?Ìï4™ñ¶Ð?ÕÎ0µ¥Ó?·–Ép<ŸÒ?ÊPSé'Ó?1DN_Ï×Ñ?—qSÑ?ßß ½úxÑ?Z×h9ÐCÒ?GéÒ¿$•Ó?ó¬¤ßPÓ?³x±0DNÒ?x—‹øNÌÐ?øŒDhÏ?v?T1Ó?ä÷6ýÙÒ?ú+d® ªÑ?q©;Ò?*œÞÅûÐ?Ú7÷WûÑ?÷vKrÀ®Ñ?²žZ}uÑ?Êþy0HÑ?±§þš¬Ñ?EºŸSŸÑ?a4+Û‡¼Ò?’!ÇÖ3„Ò?ŽÍŽTßùÑ?,óV]‡jÒ?Ð캷"1Ò?Ùy›©Ò?ðRê’qŒÒ?]øÁùÔ±Ò?áÎ…‘^ÔÒ?%ušÒ?ÿx¯Z™ðÒ?c('ÚUHÒ?ü¨†ýžXÑ?F}’;l"Ò? ‹Q×ÚûÒ?î ÛÝÒ?ü6ÄxÒ?[ï7ÚqÃÒ?Cr2q« Ò?€aùómÁÑ?(Ö©ò=#Ò?ù¾¸T¥-Ð?{úüáçÑ?öBÛÁˆÑ? ßû´Ñ?Ç,{ØÒ?í”Ûö=Ò?be4òyÅÒ?á iTàdÓ?Ȩp©Ó?t›p¯Ì[Ñ?ž$]3ùfÒ?¤aQ§Ñ?Üž ±Ý=Ò?½Â‚ûÔ?æI›Ò?CSvúA]Ñ?p]1#¼Ó?ßÁÿV²Ó?jßÜ_=îÓ?ØÓMÖÒ?ëÿæËÑ?%@7nÒ?·_>Y1\Ó?rûå“ÃÔ?5ëŒï‹KÔ?ëþ±Ó?úÐõ-sÑ?Ú:8Ø›Ð?vþÓ Ô?ÅËÓ¹¢”Ó?°u©ú™Ò?j’ÌêÒ?ïÚÄÉÑ?aãúw}æÒ?ÅËÓ¹¢”Ò?˜Ü(²ÖPÒ?<Øb·Ï*Ò?Õ‘#‘Ò?‹v“Ò?Ã`þ ™Ó?5ñð¤…Ó?ÄÑUº»ÎÒ?zã¤0ïqÓ?ŠÇEµˆ(Ó?Ê2ı.nÓ?TÆÝ ZÓ?Útp³xÓ?iR º½¤Ó?÷XúÐõÒ?é ¸çùÓ?4Ûú`Ó?à»ÍÒ?.å|±÷âÒ?áµKKÔ?œß0Ñ Ó?™·ê:TSÓ?‰$zÅrÓ?óÊõ¶™ Ó?iUK:ÊÁÒ?Tàd¸Ó?}^ñÔ# Ñ?>zÃ}äÖÒ?%]3ùf›Ò?ªÉÒ?ð‹KUÚâÓ?oƒÚoíDÓ?]›kÓ?¤ý°VÔ?{ØœƒgÔ?!<Ú8b-Ò?Ð`ÿunÓ?Ü.4×i¤Ò?˜¦pzÓ?`·îæ©Ô?™D½àÓÒ?J}YÚ©¹Ñ?æ‘?xîÓ?èˆ|—R—Ô?.ÿ!ýöuÔ?ë˜Ü(²Ó?9&‹ûLÒ?!”÷q4GÓ?‡‹ÜÓÕÔ?åÓc[œÕ?ÐDØðôÔ?ô4`ôiÓ?Ñ\§‘–ÊÑ?çUÕ{Ð?Ž?QÙ°¦Ô?²›ýh8Ô?VJÏôcÓ?VIddYÓ?” ¿Ð#FÒ?{Á§9y‘Ó??;àºbFÓ?p|í™%Ó?‰`\:æÒ?_ì½ø¢=Ó?REñ*kÓ?´r/0+Ô?\…zúÔ?z9ì¾cxÓ?ÒyYÔ? 'LÍÊÓ?žâ<œÀÓ?9}=_³Ó?½àÓœ¼ÈÓ?CV¸åÓ?{K9_ì½Ó?vª|ÏH„Ô?¹S:XÿçÓ?"¦D½ŒÒ?—åë2ü§Ó?Õ^DÛ1uÕ?cGãP¿ Ô?@7n1Ô? +TTýÓ?zUgµÀÔ?äõ`R||Ó?=Òà¶¶ðÓ?Õv|ÓôÑ?‡ú]Øš­Ó?€+Ù±ˆÓ?½üN“oÓ?Ø}ÇðØÏÔ?ãŽ7ù-:Ô?"8öì¹Ó? жšuÆÔ?€¸«W‘ÑÔ?­ö° ØÒ?¬Så{F"Ô?þî5&ÄÓ?¥]PßÓ?®~l’ñÔ?o›©ÄÒ?ßÅûqûåÑ?ú·Ë~ÝéÓ?¼A´V´9Õ?=›UŸ«Ô?²t±iÔ?ïOZ¸Ò?i©¼áÓ?õHƒÛÚÂÔ?Vó‘ïRÖ?ª, »(zÕ?M,ðÝzÓ?Íí)Ò?ûZ—¡ŸÐ?‡1éï¥ðÔ? pzïÇÔ?´“ÁQòêÓ?y‘ ø5’Ó?qs*ªÒ??áìÖ2Ô?€ ;¨ÄÓ?PáR)vÓ?üU€ï6oÓ?Úþ••&¥Ó?òC¥3ûÓ?sñ·=AbÔ?CV·zNzÔ?Ý•]0¸æÓ?ôiý¡Ô?Vò±»@IÔ?÷«ßmÞÓ?ZòxZ~àÓ?ð6oœæÓ?ì…¶ƒÔ? ¦šYKÔ?¬Å9êèÔ?U†q7ˆÔ?p|í™%Ó?ñE{¼Ô?i©÷TNÖ?ž`ÿunÚÔ?LÝ•]0¸Ô?‹·˜ŸÔ?´’V|CáÔ?¾öÌ’5Ô? îêUdtÔ?Õ‘#‘Ò?ʦ\á]Ô?<Þä·èdÔ?®*û®þÓ?÷”œ{hÕ?Ø}ÇðØÏÔ?Ênfô£áÓ?8©0¶Õ?Ñ;pÏóÔ?ÈбƒJÓ?¼Ñ“2©Ô? F%ušÔ?~p>u¬RÔ?í”Ûö=Õ?£“¥ÖûÒ?òB:<„ñÑ?NŸt"ÁÓ?ÇWËÕ?”ˆð/‚ÆÔ?ª™µöÔ?»¶·[’Ó?Ç¡~¶fÔ?1~÷æ7Õ?®~l’ñÖ?#‡ˆ›SÉÕ?<š$–Ó?Îã0˜¿BÒ?¯&OYM×Ð?h‘í|?5Õ?.2¥Õ?+*ÿZ^Ô?ð1XqªµÓ?™)­¿%Ó?ŸŠ‘%sÔ?Ž={.Ô?9¼ZîÌÓ?<¾½kÐÓ?ÀË eýÓ?nÜb~nhÔ?½VBwIœÔ?‰ jøÖÔ?Åä 0óÔ? B²€ Õ? œO«”Ô?:–wÕæÓ?Ôšæ§èÓ?ÆÞ‹/ÚãÓ?îÎÚmÔ?N¶;P§Ô?·~úÏšÕ?òë‡Ø`áÔ?Kþ)UÓ?Ëe£s~ŠÔ?¿›nÙ!×?kÔC4ºƒÕ?ºÙ(Õ?¾Û¼qRÔ?aR||BvÕ?C=·ÐÔ?{ÙvÚÕ?AG«ZÒQÓ?¹«W‘ÑÕ?¢&ú|”Õ?xî=\rÔ?iàG5ì÷Õ?a¤µûUÕ?óèFXTÄÓ?!®œ½3Õ?I†[ÏÕ?¡fHÅ«Ó?Õ?j.7ê°Ô?Ø›’“‰Õ?¦&ÁÒ¨Ò?Ë×eøO7Ò?W_]¨ÅÓ?¢&ú|”Ö?%–”»ÏñÔ?óΤMÕ?ÁÿV²c#Ó?î'c|˜½Ô?kg{ô†Õ?Üò‘”ô0×?\Uö]üÕ?|·yã¤Ó?Ô›QóUÒ?pΈÒÞàÐ?¸ä¸S:XÕ?ñõµ.5BÕ?A,›9$µÔ?J)èö’ÆÓ?ƿϸp Ó?.ÎR²œÔ?ʦ\á]Ô?ZJ–“PúÓ?" œlÔ?ÖÆØ /Ô?ë²×»Ô?3oÕu¨¦Ô?¢ñDçáÔ?vOjMÔ?è÷ý›'Õ?™Ÿš²ÓÔ?Í"[AÓÓ?/ó:âÓ?Ð^}<ôÝÓ?z©Ø˜×Ô?ˆ…ZÓ¼ãÔ?_ì½ø¢=Õ?ía/°Õ? šyrMÓ?vàœ¥½Ô?@¤ß¾œ×?§pzïÕ?Q§“luÕ?gCþ™A|Ô?Ø€qåìÕ?^‘šv1Õ?è.‰³"jÕ?÷ÍýÕã¾Ó?Võò;MfÕ?èÝXP”Õ?ŒôzÄÔ?´Ì"[AÖ?–~TÃÕ?ÿB=·Ó?Êû8š#+Õ?ʤ†6Õ?5ÒRy;ÂÓ?8öì¹LMÕ?µÀ)ÍÕ?ä0˜¿BæÔ?èO=ÒàÕ?š$–”»ÏÒ?©ƒ¼LŠÒ?ÐDØðôÓ?N³@»CŠÖ?BÌ%UÛMÕ?["œÁßÕ?uXá–¤Ó?e‰Î2‹PÕ?ã‹öx!Ö?ë²×»×?«\¨ükyÖ?ªED1yÔ?Õ>¨Ò?„‚R´r/Ñ?Ù%ª·¶Õ?ÄÑUº»ÎÕ?ñH¼<+Õ?¨o™Óe1Ô?‘ïRê’Ó?9EGrùÕ?ã©GÜÖÔ?ü«Ç}«uÔ?ŸË2Ô?d’‘³°§Ô? YÝê9Õ?±6ÆNx Õ?ÇJ̳’VÕ?”‡…ZÓ¼Ô?ƒjƒѯÕ?^üo%;Õ?ì3g}Ê1Ô?åîs|´8Ô?B•š=Ô?èÙ¬ú\mÔ?úñîÈXÕ?ÖmPû­Õ?ÒV%‘}Õ?J´äñ´üÓ?!‘¶ñ'*Õ?Ƭq6Ø?ˆKŽ;¥ƒÖ?¬„¹ÝËÕ?@QÙ°¦²Ô?ñ~Ü~ùdÖ?þí²_wºÕ?ºØ´RäÕ?p +TTÔ?LÐÏÔëÕ?£®µ÷©*Ö?o+½6Õ?1·{¹OŽÖ?9EGrùÖ?¥/„œ÷ÿÓ?Uø3¼YƒÕ?@ RÕ?ù¾¸T¥-Ô?]p¿˜Õ?ÝÍSr3Ö?¿Òùð,AÕ?Uö]üoÖ?Ö5ZôPÓ?…è8Ó?c™~‰xÔ?eVïp;4×?FÏ-t%Ö?¦{Ô—¥Ö?b¡Ö4ï8Ô?ÀÌwðÖ? õôøÃÖ?¬±^‚Ø?ï<ñœ- ×?ÛRy=˜Ô?©KÆ1’=Ó??T1³Ñ?§>¼sÖ? E¹‡„Ö?]RµÝßÕ?×L¾ÙæÆÔ?§AÑ<€EÔ?‘fØÕ?4ôOp±¢Õ?©lXSYÕ?•ô0´:9Õ?mªî‘ÍUÕ?!ºöôÕ?ëÇ&ù¿Õ?F{¼Ö?;6ñº~Õ?Y1\qÖ?=}þðóÕ?o›©ÄÔ?Á8¸tÌÔ?ð3.ÉÔ?º†OÕ?ï9°Ö?v“þ^Ö?†‘^ÔîWÖ?TÉPÅÔ? 34žâÕ?õ  ­ÜØ?{ˆFw;×?}vÀuÅŒÖ?Ï¿]öëNÕ?ôR±1¯#×?ù g³êsÖ?¾÷7h¯Ö?d#¯ëÕ?p]1#¼Ö?Ü»}éíÖ?äÙå[ÖÕ?MöÏÓ€A×?Ø(ë7ÓÖ?Á;ùôØ–Ô?þÓ x'Ö?cD¢Ð²îÕ?‡ýžX§ÊÔ?aO;ü5YÖ?¢ñDçáÖ?ˆWÎÞÕ? jøÖ×?Üôg?RDÔ?ªðgx³Ô?ÎQÚ|Õ?‹ú$wØDØ?{ÙvÚ×?ƒÁ5wô¿×?Ýyâ9[@Õ?| €ñ ×?p%;6ñ×? ]Þ®Ù?BÌ%UÛMØ?å(@̘Õ?eS®ð.Ô?Ot ‡Ò?°rh‘í|×?ñ.ñ˜×?¼aüÖ?a7l[”ÙÕ?wøk²F=Õ?j‰•ÑÈçÖ?_&ŠºÖ?ϼvß1Ö?È%Ž<YÖ?߈îY×hÖ?èô¼ ×?·ð¼TlÌÖ?I¹û-×?p@KW°Ö?aNÐ&‡×?½Â‚û×?ãþ#Ó¡ÓÕ?¥ØÑ8ÔïÕ?©0¶äÕ?1{ÙvÚÖ?_• •-×?ùŸüÝ;j×?-#õžÊi×?âÊÙ;£­Õ? ¯$y®ïÖ?å 0óüÙ?Š}"OØ?˜½l;m×?±n¼;2VÖ?•Ò3½ÄXØ?ÁŒ)Xãl×?Ze¦´þ–×?|`Ç Ö?]øÁùÔ±×?DÂ÷þí×??ªa¿'ÖÖ?ðúÌYŸrØ? ¤Ä®íí×?2“¨|šÕ?´Ì"[A×?/ó:âÖ?G ^×/ØÕ?F жšu×?£çºØ?$B#ظþÖ?¸Ku/Ù?Í•AµÁ‰Õ?²ŸÅR$_Õ?ŒôzÄÖ?LŒeú%âÙ?ûå¶}Ø?"rúz¾fÙ?šxxÒÂÖ?ômÁR]ÀØ?Ý µ‰“Ù?"úµõÓÛ?RÔ™{HøÙ?LbõG×?—㈞Õ?¼Ì°QÖÓ?ž–¸ÊÙ?_Aš±h:Ù?Ž?QÙ°¦Ø?Rf`X×?/6­¹Ö?:vP‰Ø?[°Tð2Ø?/áÐ[<¼×?—Æ/¼’ä×?Õw~Q‚þ×?}гYõ¹Ø?¦D½ŒbØ?½ŠŒHÂØ?Z)r‰#Ø?§“lu9%Ù?©‡ht±Ø?[_$´å\×?sò"ðk×?0º¼9\×?€-¯\o›×?þ)U¢ìØ?Íí)Ù?]2Ž‘ìÙ?=Ô¶a×?E¹4~á•Ø?U‰²·”óÛ?³•—üOþÙ?8öì¹LMÙ?py¬ä×?¾hÒáÙ?Òq5²+-Ù?»ðƒó©cÙ?½«0™×?Ý(²ÖPjÙ?RóUò±»Ù?Égð÷‹Ø?À#*T7Ú?5ñð¤…Ù?…è8×? â8ðj¹Ø?2;‹Þ©€Ø?.©Ún‚o×?Â¥cÎ3öØ?ëÈ‘ÎÀÈÙ?ÿ?N˜0šØ?D¨R³ZÛ?*9'öÐ>×?ÓL÷:©/×?ïTÀ=ÏŸØ?²œ„ÒBÜ?sõ¸oµÚ?Ü~ùdÅÛ?¿¹¿zÜ·Ø?:uå³<Û?__ëR#ôÛ?Wéî:òÝ?›äGüŠ5Ü?/Äê0 Ù?Ñzø2Q„×?å`6†Õ?ÎŽTßùEÛ?Ô_¯°à~Û?°WXp?àÚ?xÔ˜sIÙ?zóWÈØ?8Ûܘž°Ú?n1?74eÚ?B•š=ÐÙ?Žx²›ýÙ?˜üOþîÚ?ˆ¸9• Û?œùÕ ˜Ú?AºØ´RÛ?á¶¶ð¼TÚ?h—o}XoÛ?ÂP‡nùÚ?gd»SÙ?”N$˜jfÙ?`sž MÙ?u¬Rz¦—Ù?eS®ð.Û?ŸÛ2à,Û?Ì EºŸSÛ?úC3O®)Ù?ÌFçüÇÚ?áy©Ø˜×Þ?µ‰“ûŠÜ?Ü‚¥º€—Û?Dl°p’æÙ?4½ÄX¦_Ü?s¡ò¯å•Û?J´Û?xìg±ÉÙ?„Iññ ÙÛ?µN\ŽW Ü?Eð¿•ìØÚ?:õÔê«Ü?Úã…txÜ?ÙYôNÙ?+‡ÙÎ÷Ú?XûVëÄÚ?§#€›Å‹Ù?:Yj½ßhÛ?ƒ3øûÅlÜ?€cÏžËÚ?ÖXÂÚ;Þ?pÏó§Ù?#ظþ]ŸÙ?)²ÖPj/Û?øO7Pàß?;ŠsÔÑqÝ?Á=~oÓÞ?¢ ê[ætÛ?˜ù~âÞ?¶ö>U…ß?·)Õ¢à?Ii6Ã`ß?(HlwÐÛ?Ä\RµÝÚ?Þ:ÿvÙ¯×?Â/õó¦"Þ?Ž“Â¼Ç™Þ?šÐ$±¤ÜÝ?ŒHZÖýÛ?«7U†Û?I¢—Q,·Ý?( 5 IÝ?'µ¿³Ü??ÿ=xíÜ?æèñ{›þÜ?º/g¶+ôÝ?‰|—R—ŒÝ?]†ÿtÞ?~Œ‰BÝ?‰Ð6®Þ?ŸèºðƒóÝ?eRC€ Ü?¤ŠâUÖ6Ü?ÀÌwðÜ?@ RÜ?™šoH£Þ?¡fHÅ«Þ?U‰²·”óÞ?ÍXä×Û?>ÏŸ6ªÓÝ?g×½‰ á?§/ú Òß?»}V™)ß?>zÃ}äÖÜ?˜Ãî;†Çß?i㈵øß?PSËÖú"ß?ÊPSéÜ?¿3‰ß?H¥ØÑ8Ôß?ˆñšWuVÞ?ë8~¨ß?+¾¡ðÙ:ß?„ô9DÜÛ?„4fÞ?S°ÆÙtÞ?Ù=yX¨Ü?É8F²G¨Þ?†s 34žß?£ x|{Þ?X«vMHëà?l|&ûçiÜ?\>’’†Ü?ñI'L5Þ?œmnLOØá? Šcnà?CSvúA]á?µmÁÞ?<3Áp®áà?Ô›QóUrá?÷Žb®â?/ˆHM»˜á? pß?K‘|%Ý?sò"ðkÚ?rþ&"àà?j†TQ<á?_\ªÒ×à?–Ïò<¸;ß?¸Ì鲘ØÞ?åÔÎ0µ¥à?Ê2ı.nà?¶»è¾à?ޱ^‚Sà?Ý%qVDMà?ÝÑÿr-Úà?æ—Çš‘à?#KæXÞÕà?‚X6sHjà?pCŒ×¼*á?BÌ%UÛÍà?{»%9`Wß?pÏó§ß?Vó‘ïRß?£Ë›Ãµß?„ £U-éà?÷éxÌ@åà?Ù"i7úá?ä „™¶ß?,šÎNÇà?K’çú>ã?Ù±ˆ×õá?¼•%:Ë,á?óŽSt$à?ûY,Eòá?÷­Ö‰Ká?œ’“‰[á?i«’È>Hà?ô£á”¹yá?š#+¿ Æá?:ùÙÈõà?•Zºâ?iÒá¡á?HŠÈ°Š7ß?ýÁÀsïáà?ˆ¡ÕÉŠà?st´ª%à?È$#gaOá?Ęô÷Røá?:Ì—àà?M!u;ûâ?šÒú[ðß?Z)r‰#à?å?mXSYöä?ÌÔ$xCšä?øª• ¿Ôä?föyŒòÌä?ž%Ȩå?V+~©å?ÕýL½nå?[wóTå?Ž!8öìå?ˆ jôj€å?§>¼óã?j¥È%ä?É&pëîã?ÃòçÛ‚%ä?ùƒçÞÃå?aÃÓ+eå?ìm3âæ?ât’­.'ä?8½‹÷ãvå?í ¾0™ªè?lâuýç?˜Št?'æ? ¦šYKä?é ¶Oöæ?¸w úÒ[æ?ñœú@ræ?ûPŒ,å?Øó5Ëe£æ?.:Yj½ßæ?=¸;k·Ýå?Y"§¯gç?‚§+õæ?€GT¨n®ã?š?¦µilå?:õÔê+å?¦€´ÿÖä?”¼Ǚ&æ?it±3…ç?©L1AÇå?º ¾eÎç?m®šçˆüã?ö LnYä?Y |E7å?ø§T‰²é?*ât’­.ç?Êk%t—Äè?46<=æ?UlÌëˆCè?QhY÷é?vÿXˆê?·bÙ=ùè?K?ªa?æ?e73úÑðä?)?©öéøâ?{1”í*è?Kw×Ùè? uXáè?­ÃÑUº;æ?úµõÓVæ?ߨ*Áâç?þ¸ýòÉŠç?\ÿ®Ïœõæ?µý++MJç?+TTýJç?ý\¬¨Aè?‹O0žÁç?¾0™*è?²¼«0ç?^ÛÛ-Éè?¤ß¾œ3è?ÅS4¸-æ?Sê’qŒdæ?üáç¿/æ?Ø,—Îyæ?.É»š<è?ñÖù·Ëþç?¯_°¶­è?¢);ý ®æ?ŒjQL^è?úDž$]ë?qÿ‘éÐéé?d Ï.ßúè?'iþ˜Ö&ç? 4ØÔé?›äGüŠ5é?Ñ\§‘–Jé?è1Ê3/è?jÛ0 ‚é?H¾Äé?»&¤5è?Ïdê?ÏJZñ …é?ÚV³Îø>æ?\-Ë×åç?—¬Šp“Ñç?2rö´Cç?k¸È=Ýè?$Dù‚ê?Tn¢–æVè?gó8 æ¯ê?á “©‚Qæ?Ù|\*Ææ?3NCTáÏç?LüQÔ™ì?£#Öé?E·^Óƒ‚ë?»}V™©è?ËôKÄÛê?O!WêYë? ܺ›'í?>“ýó4àë?“ÅýG¦Ãè?eüûŒ ‡ç?žB®Ô³ å? Rðrê?ñ¸¨Eë?_]¨Åàê?Á:Ž*è?ðÚ¥ ‡¥è?Ø›6cê?an÷rŸê?Í[uª©é?ô߃×.íé?ïäÓcÛé?PÅ[Ìê?ó‘”ô04ê?ÄáÑÆ‘ê?ÌAÐѪê?¶J°8ë?b†ÆAœê?…[>’’žè?ü¨†ýžØè?Á;ùôØ–è?uv28JÞè?LP÷°îê?:3Pê?´äñ´ü@ë?X zR&5é?||BvÞÆê?Rðr¥î?ÆüÜДì?`V(Òýœë?jÂö“1¾é?Êû8š£ì?‡¿&kÔÃë?‚”0Óöë?é|x– £ê?ã§qo~Cì?йÛõÒ”ì?SçQñGë?ÙwEð¿•ì?äõ`R|üë?>î?VðÛãµï?-è½1€î?EŸ2â‚ë?$ nk Ïé?ø§T‰²ç?~p>u¬Rí?4iSuìí?ý‡ôÛ×í?LŠOÈNë?;‡ú]Xë?LÄ[çß.í?ù0{ÙvÚì?¦ ÐRì?ŸÇ(ϼœì?w1Ít¯“ì?c ¹§«í?}þðóßì?‘<»|kí?·ð¼TlÌì?àI —UØí?ÂzýI|í?"8öì9ë?é~NA~ë?þÖN”„Dë?•-’v£ë?ŸZ}uU í?¹Æg²í?!u;ûÊî?øÁùÔ±Êë?(Óhr1†í?¢DKOKð?Ñ´­fï?à‚lY¾.î?»ÑÇ|@ ì?å'Õ>ï?vOjÍî? ø5’áî?X)±k{í?ƒ0º<ï?_ðiN^dï?é+H3Íí?è25 Þï?è÷ý›—î?‰Ð6®ë?~SX© "í?ÇL¢^ðéì? ©ÛÙWžì?ßû´Wî?ÎÅßö ï?KY†8ÖÅí?yvùÖ‡ï?Ùy›)ë?_›•˜çë?!Îà L§ì?¨Š©ôÎð?Z/†r¢Ýî?fLÁgSð?ïs|´8ãí?€(˜1ëï?C«“3Tð?ÁR]ÀË ñ?³(ì¢èð?aSçQñÿí?é×ÖOÿYì?‡‡0~÷é?‹ßV*¨ï?Q/ø4'/ð?Í;NÑ‘Üï?f‡ø‡-½í?l%t—ÄÙí?Q}>ʈï?^emS<.ï?-[ë‹„¶î?ï‘ÍUóï?}¢üî?6uÿ÷ï?˜`:ï?I›ªï?ò¶Òk³1ï?¡õðe"ð?2ZGUÄï?IºfòͶí?IØ·“ˆðí?=œÀtZ·í?Ì}r î?_EF$áï?Cÿ+jï?y<-?pð?âut\î?‡kµ‡½Ðï?ê]¼·_ñ?Gu:õ”ð?|a2U0*ð?OçŠRB°î?KÈ=›•ð?±¢Ó0<ð?IóÇ´6Mð?–%:Ë,Âï?0Úr.…ð?€E~ý›ð?ðN>=¶%ð?¾¼ûèÔð?Õ³ ”÷qð?F6ŽØí?¼é–âŸï?õ×+,8ï?obHN&îî?êu‘Bð?^»´á°ð?ï9°ð?GV~ŒÑð?†æ:´Tí?³)Wx— î?}=_³\¶î?·¶ð¼T¬ñ?¦™îuRŸð?uÇb›Ttñ?…$³z‡ð?h@½5ñ?iàG5ìwñ?Ïõ}8Hò?eP3¤Šñ?o»,ð?€D(b‘î?¸\ýØ$?ì?’?xîýð?JA·—4Fñ?¿b ¹'ñ?®J"û ð?eS®ð.ð?w Nytãð?{£V˜¾×ð? Ý%qV„ð?ËLiý-Áð?~TÃ~O¬ð?Û6Œ‚à1ñ?µ÷Xºð? ¦}sÿð?šxxÒÂð?ÐDØðôJñ?j0 ÃGñ?çÂH/j÷ï?e#Ù#ð?h\WÌð?¾Ý’°+ð?í¸áwÓíð?¬o`r£Èð?ΪÏÕV,ñ?÷Ž"ð?àÙ½áþð?Ì&À°üyò?:<„ñÓ¸ñ?4-±29ñ?â镲 1ð?¢´7øÂ¤ñ?°Œ ÝìOñ?¾÷7hñ? ]Þœð?ª·¶Jpñ?ª¸q‹yñ?²ºÕ3ñ?kaÚ9Íñ?Ÿqá€ñ?Êß½£ð?y ýÜð?¿‚4cÑôð?Pj’Œð?Ž¿·éOñ?]ݱØ&Õñ?£dVï0ñ?ÌÑã·ñ?w÷Ý—3ï?PäIÒ5ð?*¬ÿsXð? raŠrò?Š}"O’ñ?‡P¥&ò?JíE´ñ?Žàñ?ŠriüÂ+ò?\ÊùbïÅò?5´Ø€Hò?6çà™Ð$ñ?J ,€)Cð?#ÝÏ)ÈOî?_š"ÀéÝñ?&S£’úñ?‚69|ÒÉñ?žðœúñ?2WÕ'ñ?ãý¸ýòÉñ?÷ʼUסñ?ÃJUñ?,H3M§ñ? …8„ªñ?¥žÐëñ?]7¥¼V‚ñ?ÿunÚŒÓñ?ëTùž‘ñ?`sž ò?çýœ0áñ?¨SÝñ?¯&OYMñ?Âû ñ?ï7ÚqÃ/ñ?Jôñ?IÛø•Íñ?GßÛô'ò?@‡ùò,ñ?YRî>Çò?UˆGâå)ó?┹ùFtò?×øLöÏSò?ù¿b Wñ?‡Q<¾}ò?}–çÁÝYò?ÉÊ/ƒ1bò?V+~©Ÿñ?©2Œ»Atò?8½‹÷ãvò?I¹ûíñ? ’>­bò?Ð}9³ò?„ò>Ž&ñ?ò–«›¤ñ?L¤4›Ç¡ñ?€H¿}xñ?EHÝÎþñ?Ð(]úWò?€™ïà'ò?g*‰ò?$cµù•ð?[•DöAñ?0JÐ_èQñ?±Ã˜ô÷ó?‹¥H¾Hò?fÚþ•Õò?ð£ö{âñ?¿3‰ò?=Ô¶aÄò? $}ZEó?ßÞ5è ó?kÕ® ò?l^ÕY-0ñ?ؼª³Z ð?» ”˜ò? =bôÜÂò?pënžêò?#ö  Ùñ?˜¾××ñ?€ ˆWŽò?z9ì¾cxò?©h¬ý-ò?¢ ê[ætò?Ôšæ§hò?¶øã™ò?žxÎZò?F^ÖÄ‚ò?Lª¶›à[ò?œ¡¸ãÍò?Ð}9³ò?ªò=#Úñ?LŒeú%âñ?–“PúBÈñ?3§Ëbbóñ?*©ÐD˜ò?>\rÜ)ò?cì„—àÔò?°rh‘íüñ?Óg\WŒò?á—úyS‘ó?…A™Fó?ðN>=¶¥ò?¥Û¹à ò?Ñ@,›9äò?qåìÑÖò?†7kð¾êò?3¥õ·`ò?§wñ~Üþò?ë8~èò?/3l”õ›ò?ZÊû8Zó?ožê›áò?ˆÙ˶Ӗñ?&S£’zò?[²*ÂM†ò?>BÍ* ò?Êjºžèºò?ùÙÈuS ó?ïs|´8£ò?²ˆ×õËò?V€ï6o\ñ? F³²½ñ?‡6áñ?cFx{‚ó?½f¾ƒßò?®HLPÃ7ó?Ç.Q½5pò?¼ÉoÑÉó?²ºÕsÒ;ó?À%ÿ”ªó?úDž$ó?]¿`7l›ò?{.S“àÍñ?ÆPN´«ñ?ÅS4¸-ó?}"O’®ó?Fê=•ÓÞò?Q¾¾Öeò?µÃ_“5jò?3‡¤J&ó?•»ÏñÑ"ó?—«›äÇò?³˜Ø|\ó?Æk^ÕYíò?Tn¢–æó?xak¶òÒò?ð2ÃFó?áçSÇêò?I®€B=ó?<‡2TÅó?Ánض(sò?·ìÿ°eò?ßÜ_=î[ò?Mu€”ò?Ó÷‚ã2ó?ÇeÜÔ@óò?ÆnŸUfJó?…—àÔ’ò?fj¼! ó?Å °rhô?߀cOó?ØÖOÿY3ó?Z.óÓò?µ§äœØCó?óùõCó?LS8½Kó?%És}Îò?o+½6[ó?4óäšYó?oÖà}U.ó?$™Õ;ÜÎó?óWÈ\ó?–$Ïõ}8ò?ÀêÈ‘ÎÀò?ö@+0d5ó?±øMa¥Bò? ËŸoKó?º2¨68‘ó?odùƒÁò?Ø€qå,ó?3NCTáò?øÃÏ^ò?ƒ0·{¹ò?uèô¼ ô?o»ló?-y<-¿ó?vüó?Mjh°ó?ެü2£ó?'/2¿ô? Qºô/Éó?HÀèòæ0ó?ÿëÜ´gò?Ęô÷R¸ñ?en¾ݳó?\8’Œó? W@ÜUó?J}YÚ©ùò?¨+õ,ó?öÐ>Vð[ó?7ê°Âmó?Ÿ ±Ý=@ó?q‘{ººcó?>ê¯WXpó?œ¡¸ãó?¯½7†@ó?×ûvÜpó?ÿ²{ò°Pó?ŒÙ’U®ó?3NCTáó?ÎÄt!Vÿò?Ú(·íûò?øÞß ½úò?ªžÌ?ú&ó?øá !ÊWó?¼#cµùó?Äy8©ó?cò˜ùó?ÀzÜ·Zgó?¼•%:Ëlô?Wÿ[ÉÎó?)í ¾0™ó?\Æú&ó?ZFê=•“ó?DP5z5€ó?Üóüi£ó?¾÷7(ó?‘`ª™µ”ó?gð÷‹™ó?øÁùÔ±Jó?õœô¾ñµó?·bÙ=9ó?~R›8¹ò?ûõ×+ìò?%W±øMó?1•~ÂÙ­ò?,œ¤yó?b‚¾…õó?¢ ±ˆaó?úµõÓ–ó?ÂøiÜ›_ò?êX¥ôL¯ò?Ù=yX¨µò?–]0¸æÎó?A€ ]ó?ÐF®›Ržó?äÙå[ó?<ƒ†þ nó?¸Ì鲘˜ó?N³@»CÊó?«#G:ãó?9í)9'vó?DÀ!T©™ò?s÷9>Z\ò?¯`ñd÷ó?c${„šó?$š@‹Xó?hz‰±Lÿò?ìW\ó?}ëÃz£–ó?THÞ9”ó?ø‹Ù’UQó?7ünºe‡ó?FΞvxó?óWÈ\”ó?p|í™%Aó?Òýœ‚üló?¬ÿs˜/oó?ôiý¡ó?_zûsÑó?PR`Ló?°¹2¨öò?¼’ä¹¾ó?K %vm/ó?ž–¸Ê“ó?bž•´â›ó?E¡eÝ?ô?d=µúêjó?Wzm6Vâó?AeüûŒKô?™*•ÔÉó?tµûË.ô?úµõÓ–ó?¥¡F!Éìó?ÐñÑâŒáó?S=™ô ô?Eò•@Jló?}¯!8.#ô?ªó¨ø¿ãó?t±3…Žó?RD†U¼Ñó?ÊÄ­‚hó?¿dãÁ;ó?X­Lø¥>ó?b*ß3’ó? ÓÚ4¶ó?+‡ÙŽó?ó:âÍó?,H3M§ó?R›8¹ßáó?Çg²žó?Cý.,ó?Œfeû7ó?KXc'ô?AH0Ûó?F~ýìó?(`;±ó?b.©ÚnÂó?-'¡ô…Ðó?„bÕ ô?ô¦"ÆVô?N¶;Pçó?‚ŽVµ$ó?=·Ð•Èò?sôø½ ô?!ÍX4Ýó?H0[·ó?ñÕŽâuó?¬9@0Gó?„œ÷ÿqô?  Y2ô?éCÔ·Ìó?o.2Þó?ë7Ó…Øó?µùÕ‘ãó?rNì¡}¬ó?!WêYÊó?Õ•Ïò<¸ó?n¡+¨þó?¼S”Kãó?צ±½tó?r7ˆÖŠvó?w;Sè|ó?n¾ݳ®ó?TÉPÅ ô?|ÓôÙô?…%P6%ô?v3£ §ó?«x#óÈßó?©¥¹ªô?>Ab»{ô?ur†âŽ7ô?, ‘Ó׳ó?²,˜ø£èó?·Ï*3¥uô?Ž<Y¤‰ô?w„Ó‚ýó?¿ïß¼8±ô? m9—bô?Ù@ºØ´Òó? †7k0ô? 6®×§ó?˜Q,·´šò? ý\¬ó?, ‘Ó×óó?ßÜ_=î[ó?k :!tÐó?4w¼Éïó?ådâVAÌó? %“S;ô?„4f’ó?šÚRÌó?5s»—»ó?=×÷á aô?Ÿ·1ô?÷@ô?§ ?¹îó??;àºbô?äÜ&Ü+3ô?âåé\QJô?õÕUZŒô?A¹mߣ>ô?EõÖÀV‰ó?4"1ló?V ÂÜîeô?Üôg?Rô?÷.9îô?Š9:ZÕó?ÃIš?¦õó?t`9B2ô?ûÍÄt!Vô?¢ÏGqô?Ÿâ8ð*ô?·Aí·v"ô?'á_Mô?ëŽÅ6©èó?O?ü<ô?ú'¸XQô?Å7>[Gô?‚rÛ¾G=ô?ÁÆõïúó?<2V›ÿ×ó?LŒeú%âó?cÙ=yô?¼=ùRô?E¼uþírô?åѰ¨ˆô?é Œ¼¬ ô?xµÜ™ Fô?1]ˆÕ¡ô?ÎÝ®—¦Hô?9~¨4bfô?- PSËô?s‚69|Rô?Ó+£‘Oô?¿ó‹ôWô?&üR?oêó?€J•({ô?1îÑZQô?6t³?Pîó?€'-\V¡ô? 5?þÒâó?(›rÅò?À éÓ*ºò?Ç€ìõîÏô?¯\o›©Ðò?iâà ô? EºŸSô?·D.8ƒ?ó?$~Å.òó?מ—Š ô?¥ßPø,ô?æ[Ö5ô?iª'ó¾ô?ž#ò]Jô?t(CULeô? åD» )ô?¡ƒ.áÐ[ô?u?§ ?[ô?OYM×ô?@1²dõ?zóWˆô?†Ê¿–Wîó?¶Mñ¸¨Öó?§!ªðg¸ô?¾Ý’°kô?:\«=ìEô?¡GŒž[(ô?%ZòxZ>ô?ޱ^‚“ô?ÜÖž—Êô?QLÞ3_ô?ó:â dô?;5— uô?Ôe1±ùxô?ÔÔ²µ¾Hô?»G6WÍsô?^ÒƒNô?sôø½ô?øpÉq§tô?~p>u¬Rô?Ý^Ò-ô?ò˜ù>ô??ªa¿'Vô?mrø¤“ô?|HøÞß ô?j‰•Ñȧô?7Þ«ô?iqÆ0'¨ô?­ø†Âgëô?Â26t³ô?zÄè¹…®ô?©MœÜïô?—qSô?¦bc^Gœô?O!Wê™ô?ê‘·µEô?!Ky ô?'¢_[ô?å}Íó?†!rúz~ó?q!àF ó?£W”†šò?íÿ°¥ó?Ãdª`TRó?áÔ’wŽò?åCª"ó?¾hÒaó?¨½ 4ó?À•ìØô?Ì^¶¶†ô?ãâ¨ÜDmô?¥2Å]ô?çÄÚÇÊô?LnYk¨ô?'0Ö­ô?0Ø Û¥ô?aO;ü5™ô?ôiý¡ô?dËòuÙô?1ëÅPN´ó?µÿÖªô?€šZ¶Öô?('ÚUH9ô?Um7Á7Íô?m±ô?"Æk^ÕYô?›YKiô?©iÓLwô?I+¾¡ð™ó?™~‰xë¼ó?½ûã½jeó?}—R—Œ£ó?ç7L4Hô?ý†K®ô?GJ±£qô?.óSœô?d:tzÞô?j ùœ»Ýô?­5”Ú‹¨ô?:\«=ì…ô?eU„›Œjô?w’ `ô?¾†à¸Œ›ô?“þ^ ó?ÑvLÝ•Ýô?i­hsœÛô?&P6åÊô?Õ"¢Øô?œˆ~mýó?IZÖý#ô?9 {Úáô?~Q‚þBÏô?4ØÔyT¼ô?*œÞÅ»ô?ioõ?G­0}¯aô?$´å\Šëô?¸Üšt›ô?¡l\ÿ.ó?¹¨Ådó?XU/¿Ó$ó?5cÑtv²ò?SçQñÇò?±ÀWtëµó?o¸Üšôò?4¡l\ó?ß©€{ž?ó?¯]ÚpXZó?8h¯>:ô?T:Xÿçpó?`¯°à~€ó?̶ÓÖˆ`ó?êé#ð‡Ÿó?k'JB"­ó?Å’r÷9~ó?©ƒ¼LŠó?ø¥~ÞTdó?/ßú°Þhó?ep”¼:‡ó?Í‘•_ãó?h@½5ßó?kׄ´Æ ó?¸éÏ~¤Èó?b†ÆA\ô?ÕèÕ¥aó?³¶)Uó?F–̱¼kó?e73úÑpó?¢ðÙ:8˜ó?Ç€ìõîÏó?=ƒù+¤ó?”¥Öû¶ó?¨Åàašó?n£¼’ó?¢³Ì"[ó? ˆI¸‡ó?[| €ñŒó?ê?k~ü¥ó?©„'ôú“ó?¦~ÞT¤‚ó?dyW=`^ó?t—ÄYuó?Ôž’sbó?æ]õ€yÈó?Jëÿ¦ó?„~¦^·Èô?¸£îô?ÿy0Húô?=›UŸ«íó?OÏ»± °ó?`YiR zô?ÿêqßjô?õL/1–©ó?r1Öq¼ô?iÚV³õ?qXøQô?ê¯WXpÿô?ÇeÜÔÀô?W=`2åò?—qSÃó?cÐ ¡ƒ.ó?G 6u^ò?NÒü1-ó?Ô¹¢”ìó?kò”Õt}ò?ë˜Ü(2ó?Î4aûÉXó?-B±4íò?žÏ€z3êó?ù0{Ùvšó?¢+Üò‘ó?e73úÑpó?’“‰[±ó?¿CQ O¤ó?8‡kµ‡}ó?JA·—4†ó?J&§v†ió?Ɖ¯vgó?íFóó?6\íó?±§þšìó?:<„ñÓ8ó?Ní S[jó?Ä&2sËó?ý¡™'—ó?&rÁü=ó?F жšuó?‡¥Õpó?Øî »ó?õfÔ|Õó?3oÕu¨¦ó?›kCŸó?vüÂó?ÙCûXÁ¯ó?Ò4(špó?[%XŽó?nߣþz…ó?X¾Û¼±ó?ظþ]Ÿ¹ó?âeS®°ó?¬±^‚ó?ý‡ôÛ×ó?ˆŸÿ¼¶ó?G­0}¯¡ó?Õ@ó9wûó?7n1?7ôó?´Swe×ó?¦Õ°ßó?[[x^*öó?AŸÈ“¤«ó?£V˜¾×ô?°|·yãó?8¿a¢AÊó?Ÿ;Á¾ó?©MœÜ/õ?C —8òÀô?ŽW zR¦ô??ÿ=xíÒô?$Dù‚Òò?ޝ–;só?k‚¨û¤ò?Ùëݯò?ÁþëÜ´™ò?ÖÆØ ¯ó?ðÝzMÏò?€ôMšÅò?Ð캷"ñò?÷!o¹úñò?y”JxBïó?/¨o™Ó¥ó?ðO©e¯ó?›sô¸ó?㦚Ϲó?jjÙZ_¤ó?zŠ"nŽó?~ŠãÀ«¥ó?©2Œ»Ató?Â1Ëžvó?Àêȑ΀ó?ßÛôgó?ˆdȱõŒó?:°!yó?Ù /Á©ó?´€Ñå ô?‹ÀXßÀ$ó?U/¿Ód†ó?dèØAeó?‚âǘ»–ó?o+½6+ó?h°©ó¨xó?¦º€—6ó?á@H0Aó?ñÖù·Ë>ó?jÛ0 Âó?~âú}ó?^€}têÊó?H‰]Û›ó?äø¡Òˆ™ó?Ú|a2•ó?ÿ$>w‚}ó?m‘´}Œó?Û¿²Ò¤”ó?µó¿ˆÈÍêD¡?5\!­ñ?'@1òÒ@¬y;Ýê¿(ÜÀ˜<±?¦ íjNù?r7ØéÚ2è?3‘àl4æ¿Áå3*x|õ¿d¯Ô](…ῘRyJ­é¿]Vçnñ¿eŸãÀ:@3Å;Džá¿»`…JóGÙ¿Ø ·*³û¿ñÚV «˜ÀÁÔG Ió¿4Ë>³Ð@kqK³ž ø¿o¢S’H¬?¬èŽ’c¶?|s0n/sÀCÔ@±2á¿…Ñ¿£õ¹ˆôä¿-FÞV!]²?й1¢_Øù¿à¼ò§)˜ñ¿_4ÉX¡Ðå?ÙÈ –óä¿›QE{ýö¿á¾¬ª%ô?’¼>°|XÜ¿ïÍf¦ì¿Ì} ÂßwÞ¿ xç?ÐË?¦M²7dÓ¿± ZRå¿Þ¸r~<=ð¿rÉmln¡ù?G°%\7‚ô? ÁA’¤Ã£?•UäéüHÔ?x>gjêÀŽo0{Gõ¿Ë¯¥´>7ó¿ÄnÚÑLû¿ô¾3‡î¿ @mŒpæ?Ú”–€—²ý¿-nÜæ_ÀéáÈáR—è?eàŸgl@äÛ ceë?¯ã7Ä#Ûè¿¡òê-Ðð?8˜ˆx°˜Ç?$wT·>^à?I ùÚ)Ù?X võÍ?žLˆdÀë¯ç?â]A‰y¥ß¿¸‚D-Éóõ?’bYMâaê?J×Q9Ðí¿øA)ƒqr @Øíã{'A÷¿ikUËÄ÷?ÎKà ò¿ué“Ìœ~î?‡•Àp@ w‡•܇—¿Ì qiV ÿ¿Âµ\•—~ÀtŸg±‘…¸¿†ÏFã¿#†Ù+ÔÂ?@î'VŒ£º¿ð¿°„É£¢?$,X¦/É?b ËÜ æ¿p!"xõÀ~0´—Ù¬¿q7å5sÕñ?€Wºé̶翢üŠö©û?\-©H¡Þ?r¾B*mÞ¿eLÆ!¥ÖÒ¿vZâ6j@gõ‘Úwû?¤]á Éõõ¿§=³b=Ã?¬ª|Mcã¿æ¼?ô°pÀó³, «=Ð?4Y±ºÔ„À7¸á¶iï?©Îª@;#ò¿’¬”{ë®Àì©ÞHÒ@ øëÒ¾? ,Kɽàþ¿‚‚–Rö?Jbà¶­®é?|sq¡Nð¿(nÂu·ç?Ùdž:Æ7⿹H‰J]é?¸ÊoJÜï¿màCÚÀèœÒ 翨™-Ú– ÷?ÏF=ŠŒÉö?^È)¹ÀÎäq¤ˆÀ¦ÑLçûz¿.+_=Øà¿„âi?9Uæ?µ¨˜3À¶O¦#Ý¿³ÿn}ò?1Š ƒ!;á¿PþûÚ{›è¿µ‹³Êž¶í?·úÅvó¿÷§6Ú—?ÓŽê/oÀ®—&ÒÕý¿tVÈ^Bæ¿–ñ ©h…ç¿î¬‰(.@Àaq!Ãø?<(ni[À†r…Õ{`á¿$¤íÙÖÀö¿ãg@”µ@Ž_ÞÃ?b¬¸<2@Í>; ½¿ÛRŸÚd(@ÇÎÛyÅ À²”œPYóò¿Òƒ_øÆÁ¿.Tß„üô¿©f6°Ö¾?¸Vk­îâ¿È“šÑ¿›0,“éò¿Ï3Pþ”0À¡¹¹ÿ?à†«û-çÜ¿ìM° »5ñ?ŽÞî±ðÀÀxÚYLÌÔÞ?œ½È“+í¿Z¯½Àù¨«“{Ú¿ü¯¸s$Ú?¤|™ñAà÷¿L¾¡%õíù?,³[qv'õ?Tc×uið?V”Ãîüô?0ž¤GT^«¿‚)ù6åù?XÂlîÀ`"5…–Rð?Ë89Äq Àð#²½âË¿øì”C¥ä¿€k1e? Æe·Ã" À‚왲P:׿ÐNBÀ¬Ï>Žx@Âý á λ?0ÿRÝÜò?Ô‘Ù.è0à? ÖŸc¿ À<“å5€Eú¿ÓÕ¯¢¯~ÀLUwçû¿ÜB Æ?Ki=ŸÀÚ^ï@&û?½äŠ‚€ Ú?Ù:µâgì?=,Ñ.¬» Àô¢þB©í?$kDˆ)ú?8&QtÖ?¿ÐÂIî@íZ?h˜ö@]Å1׿â¡ʬ@·A.oCÀ4Bê×k@$þƒ+ôrð¿7ˆetÚ À|{û<¢+ÿ?ýGñ›Âß@*Žbò· @‹a (ªë¿, uìzGÀâi4?„À¾Ð¬Á[Ö¿­¦ Í“ì¿1i ·Ê ü?î¾*‡BÚæ¿BVË´8ó¿vÌžÑŠß ÀoˆRvÌZå?@ROÅí @Ã= á @Ä_RR@kÂ<jÅí?†vð„BËù¿˜mOë ˆÛ?цÇßuß@úÀvÁjÖÿ¿VÃ}9@-’«„¹@ë}üÄ¿ŸAÔWWêô?4ˆßß¿”8OÛÀx¼æ Z@Á¿ÇÊ¢ @ÆÿùÄQ ÀàdnÒIä¿vbæÄK¹æ¿ø”ðWIÀÙûÞÿ„ã?j€äð™rø¿ÀY>¹ Ë?ÆZ•_Ð÷¿hméT î?ÄŽ(O›ò?àÖö,…¶¿~Bû²À®~—¯£ @ÐÅmwÒ¿•îs 'š@Çâiš8À`aŸ–ãuß?àÞ¬D‚½Ù?uýÖ¯†^í¿‘ˆ½eÀ@aÕ*°À|±šˆS²@ìGä¡ßÀÔWUUéæ@æÏá¿(ê?ó\!«{Àòµâ ^£å?ÈÓo äß?òÔÈoà@yµ ½TCé?ÚÞäöH@lÖk0u¤å?óc®Ä9ÀÿÕâçÅú¿º K¯-rÒ¿ámÝØ2ÐÀ0>‹*ªiô¿´UÙÌþxÀ#ãf)€b÷?ø®öZ3¼ó¿"©æ‘ºqô¿ˆŸÓaVÇþ?ž¨Òüõ¿8KزõÃÛ?EÊiÌ #à?…ªñÁnü¿?‚ñÔ?íø?n9JxÄ5@Ð+©vó¡ò?ñ]_„t™ä¿( }Nú?;3v£ûê¿ìùž6`ÀܿȄFd”À5ÍëÃîñð?n¹EÅ”^ÀHЩؙ‘ @d·á§@ÄJŒÀƒɲ¤Ø?}%Žá?àŸg­n÷?ćÔ¶óæ¿ä©KÝÀ•Ê¿ÊNn·+%á?3»×Ì5óõ?†1¬"‡â?Ô‹ûMëÇ¿}™v[ÛÈÿ¿©`í´?Åà ”¼7é¿×NÉj}²?#ÑK‰Ìø?¯”ÄîBí¿Šš0¨ˆsñ¿ÿtd¬–±õ¿VyV‹å–@±ä5SÝ¿Áþ«Ú?ï?à¦<@Ä*‚?ðÍâÂ#Ùý¿‹ƒÍ>ïé?‹ ìŽ7Ö?ÄbÑ?á¿àTñ©`ôè¿0¥%|¤4οʗÔ=ì=ø¿ÄÚ”Zí×?b;Êð Ó¿½h@òñ¿ GÇE¤åå¿TVËy9Ö?»v Ø—@ òÞN‰!¼¿ct\Xÿ×ò?ŽõÅ)ù¿î*8Ó8Ï?÷õTù³¾ß?­šà­ÿ¿3tnîw[ö?;²(œ.³Ú¿‚ì¢|@ö?ö*MðÞ?Ñ&á+R׿Hé“Ûyï?`¾Éá¹?w/w×dÀ?´€Ÿ3áÍ?(½"Õm4ž¿hg5Üȹä?aŸ;NC¸ñ?HUõTAÈ?9õ¶SóÑ¿X4̃þ÷¿ÈKÜzÝ?SSïå¥ðÜ¿°Åoï?-Ê?HÙôSnñÓ?ˆ~š¥«Xä?öߪ²Jå?Fž¿`Öü¿Í÷>¡(Ö¿ ‘&¡ê?sJ³&Xó¿;{õJbzê¿ 3ÔÕê¿wÕÖùÁ?Ì»8ô 쿸‘®?¤ç?nt9Чrð?Âò’¨tá?ºH}#u‰À[â#ÍwÉ¿ž²•ûÓ?2PKÑ=æ¿?ƈ7¦6Í?}HÎ[òØÑ?V ×eÍê¿xÎ×§x@[|2Q]cù?`–î28á¿ç8ÕZèá?’6.?â¿?ׯò¼NÛ¿ù«E#HÇ¿['*ž%ù?‰kfL©ä¿æPèÉ©?œ²è?Ô&õ§£·?¤Îô²ÿ™í¿þ^aèþ¿Wš TÚƒÀà…нë?Ø,1åQ°?°5ñ’mWð¿kœU}j7ú?ÂúEI¤Î¿Yâþš¿>ý¿y<%’Æ?t±JpÕ8æ¿z%lØo¬ò?,ÿ*<‡Õ?÷ƒ.Ý?ÏšB±¸àâ?f?4V=fí?Îxdý?Nðf¹Æ¿ì“MÓtþ¿n¡ó²U@Ü·Ë&†À=`œÕöÊ?^Ö•÷”Øé¿h¡y‹6Qå¿ò=Ï©ÿ¿È À²15ñ¿&.±¾Éêú?0¼„‰`{¸¿U=)æS>ï?'Ç-CÇé¿@–Lš»Uú¿w€ysçö?®ñél¥ø?9eèpôdÔ¿ ‘ô[á?¿J*ˆù7ø¿,çVÓtÕô¿žÙ1òÖ?|Ò묪„ÿžHù›ó忤äÕ$í¿ÝÛ{ ›Ë?‡Çwõ?ÙmÎJÔ?Åãará@ ;÷Þ¼vÀ8Ú‘ ÆY²¿D/æsâ•÷?4Pé²åú¿À.&Ž´¯¿RJ5½¹Mþ¿(,Aoó¡÷?/ fö¸ö?ÙÄV¸Ù¿™A9_Úó?kóáØHÏú?>S+´â¿Ø|‹N>ñ¿vÍ"¯oÚú?~@±‘Ö?ýïLgøÄï?͵)‘¥—Ù?ŒXÓýOh¢¿òµŒ~ë\À,ƒ¶þßê?&Ï8p¬Àø#èÚÈšó?P˜«‰²¿èc‰2糿D>QZ ê?ÇK·¼ÊÀœ?15)Ví¿L–f>î¿ÁS e4ø¿….FLá¿ÂÒ-*ÖòÀÄ÷WrÎe@Ð*71Ýû¿A§ƒ”íæõ?-Z2Ÿö? y˜§T³ô?ÃHû[ À \®>é‘?ÒV‚yâº? }#B¬³À?šyýÿóÍÜ¿8‹“%)Ü¿ ¼/’–â˜?ª*ší@„„[sCŸö?üÌ6éhã¿ 5¶d©í@(±Æ ¡Ü¿?(§Ù™žªÏ¿¿27#:mË?P(YíË߯?c’0dÀ©ÁÔ˜Öúê¿FÁ8A?¾å¿Ú÷ÔS–ê?Ük¹èvÐ÷¿é]J#‰À`³UCßø¿¥—ÜÊ¿ŒL.XÄð¿PöÙNšñ?Dá•Ñü?hp[dPVä?¥Ä].".ñ¿4$ðŒà¿,Joœßuñ¿ ¸ˆŽ¬É?%&Uaóû? ö+9pþè¿ìÑé“Ró?ì øŒ»Õñ?c°‚÷mí?$©\.ÊÃ?tòãfQÀî©ÊV˜í?ørtç™@ÙœKÚéú¿°«SX¡?¦¥åò?·-Ø<ôü?É”§»Ž/ø¿ú„ixò?Ì?ô.VHý?rIàN˜[ÿ?/=–]#ø¿è;B,Ýs÷¿±DªÅ·@”²XUúô¿u¹F‹T!ï¿,Wœ¨ßì?7/ðÄðW@^\/ŸÎ ÀâA¹ŒÄñ?h9yhí¿ˆæî]äˆá¿ìš#ôù¿éHXà?Ô±­Éµ‹¿Å‡2÷|š @ŽÔ'»¤à¿.»`À~¿×1ú?í˜é«<Ð?QbCÅ›™À𢪤ê?VÒ°è?‹Ÿ9öêò÷?Eqº¡¦üæ?1·j(æ¿@ŠÌBÒ¸?4†µâä @¤ç= =ñ?4ïvÈË”¿°ß} 5Ê¿ ™¨ÖWgп N"Cç,ö?ÈÞBþ„‘í¿ChA¸ò?¦Sé¸Ë?&GíHp@tü b¹Iâ¿Éâ¢#¤ìæ? Y¤ ^ð¿ÏÂU›°àâ?ˆ“^¬F%ø?žûlÒ- ÷?W ­ü¿ÆDÛñÃ?8›ÃyoÊ¿0‘šÙØBè¿™û>ŒžÓÀÀ7ÿ‡±¿ÙäëãÃbá?¿Í;ß?º©¤'@×?X3xÙEƒÛ¿ñ»—FCÀÛÏœT2Å¿,˜ÒǘõÌ¿Ž¶Œ1Cóñ?X‘hwпwøI.ÏŽó¿â)’üé?žìáÛw@À×Î1öE˜?>â@v#À0UÚçŒl@•ˆS|ËÑ¿’ˆÉò*À&Çý˜Žð?°¶nO@–Y?%@,ЪÊt¸È?+K=™ä(Àò|±ò®uô?rßÉÑ€ÊÚ?9yX.9­ì¿íá¥Æ°ó¿ŽIk$Ì@œÂá·änï?Ü)Iìßøç?¦œaîüÞî?O ƒÇ»{Û¿@dœ×ô§?‰¯XÝ „Àƒ€6òtÀ(?¤:´Öù?é̤›&ý?cîà|QEÊ?ó• ñ?X¶þÍ?¸ø?† æJÛ?*2²‚œ©ñ¿¸2¥["vÀ¢cÇ9q1ü¿Zåó{nò¿ »kïÀ«Ñ?òíǤù?d þ‘·á¿É7<¼ËaÀžüKÝý¿›ëJœ::ã¿Ý|ØG:…ú?`<­Q*=å¿Æ=6Øó¿12aõ*Ñä¿(Ã3ÝÝ\ÀYjµ QTÔ?†oj$3ì¿ÖíÃõµø¿é‹c ;‘ý¿<º–ÓuÁå?ªX¢ÿ7Òsw-WÓ¿óì£Û¿GßÑC?qñ¿aD€Ìì Å¿‘ñ:)uø?ø㞦¿¦UêsØGÆ?ÕŸÅç.kâ?ô0[ÄbÈò?×AÄ4ö?á?KWåëp&Ò¿M<þ Û?cáµÙî¿ÐqDZÚAä?¸œ*¹c ?çÒÏÁ?Ó?A$CÇ>;ÀÙ¼æ©ö?¾¶¹|¸? FÄóÌià?¡lBµÌ¿äIO5èëæ¿JÝK2,»ë?ò•{ØœLÿ?=,a², å¿ÔS@¶'v¯ÜÓ.Û¿(Dµ`kö¿òܹî`á¿)ÊkDÿñ¿Ï%3zD0â?Â\}oYÌ¿J¡]ùÀO hEVã?¼zøMçÔ?ð¼+.7ˆÙ¿qù(R”ÿé¿c»Ø?‚FüÁû¿,gj`Ø¿zh<‹œã?´ys\EÕ¿¬wYéüoп@éí‹ÁÀ¿‚øÆÀ—nÀ¨Þ¶VT`Þ?¼BæYç?€ê»(èv¿‰û ç?0j‡šÄí¿r)‡ªÿò?moEHLû?4°¸ñÙó?2¦4>§žù?‚ÿx4bÙ¿2ä`ÊXÕó¿™cñӣ迈Ã$Ø;ùâ?DLÜs~Ñ¿Á`öª ö?£+"­¹’ü?FV þ¿æÞ‹EÛ«ç? §!¢p|Ô¿‰xÈèM<ñ¿. y3^œÓ?È<×ôßÿ¿ðÓx¾'¢?4à›¼‹„ë?ï|H>µÚ?èÃÄQÖFê?ìMYÏ8)¿?Äxƒ*êó?ìíöNzQÛ¿‡¾¼Æá?€ØûMiV¿$&Yõ¦á¿†}c„^ø?õ^¾ ²Ú¿xÊoxüÿÉ?’ªX&`¢ý¿Î¿ƒy—óò¿Slä?’пAâ|±u*ò?H.•Ô7Ü?<Å»*V'¡¿´G oíÀ^âK uüú¿rÓIG¬$Àü? ˆf @{‚þ,ûÀ6´Þ~Û'Ö?SÜ?ýìû“ð?°^³1í?û–zaðÞ÷¿Ò? .éWÁ¿-õ5å½Ç¿,:íù¿’òúÁ[Þï?©6à„uÖð¿F£„Aêó?@ÎFiÐõ¿Š!ãkÄPö?}}ÿ Øn÷?²¿éðÉ?(ÇøZzBÖ?GÿÞlD&ð¿u­H,¶õÿ?òaÊ©—ÀŒïPÆ À%ªîÑbGð¿˜Háaf5ú?+… ÑÈù?’‘q£Çã¿@bYèîü¿x@¢¸6`á¿¿ê®éü*õ¿BsŠýá?$¬Ë9rÝ¿ØT˜DÙŒÛ?íÈó‡8Úå?ìÉ®N’’ ÀBT©üßÍ?âê-÷;è¿.Î÷SRÔ¿†ñL>}ž@PÙ I´å?R9LUmð?¾ ‰~Ò§ë?Q}Eo»è?Äö´¢µÜ¿6ñNMkõ¿}5Œ‰T–À†!Ïùî¿p _½s¦ñ?°Ž„”X/Ü?dËÍÙ/ÊÛ?²b­vŠoØ?/#÷_Šmð¿ÿÉøVÜt翇¥œ‰ѿ֞{’Oç?òµ"¿»PÁ?2{kAS@Ø @}"ë¿›NNu%ââ?øi’ßQö?R-j%Œò? ñ•‰Nß?"%;â¡Àå¿@èfÆ`‹¡?w¾œ0s @­¸>þgaá¿í¯9púÞ¿?¸Gº’ï¿ZÅÝ{Îû¿X Ë„;ü¿ðº‡êú¤Ý¿Xþ–YÁÖ¤¿v®T½ @CXÕ‰sùë?pG(ÙÝá?‚)p™µ@PÅ;;½ø?\¹Ãú»‚ð¿J¶€t äà?jQúÏÔ­ï¿8 20âá?w]÷‰îpù?ºî[–ãè@o/ß¿€YTg»ó÷?ßLKåäÀ´fŠ´Áæ?ó5ÖOÊê?ägf„¼Žù¿¯7ZPÖò @Ï“*Ö˜þ¿ÆVžh¹Œé¿ï«xÍö¨î?4ÊbòÒã¿›MxþI2ò?¸a!É­¿þsªÑí?Ôä«ðÇÀÀ˧ó!ì?0ò†v©©”¿Ï±$¶é¿®[¶ßâ?*B8)Zî¿;¹ëZ_@·É„mIý¿¶:?yêmø?¸Ëö¸pð?ÞwŸi,—À µž¾%hö¿¡Ö@ë¡ó?½GµI1á鿬·ü‹ Ïó¿‹Š˜·Lå¿+–«ò„Ñ?›¼RlÌ2ÀD?Ŧšê?statistics-release-1.7.3/inst/datasets/weather.mat000066400000000000000000000010671475240274700223010ustar00rootroot00000000000000Octave-1-Lyear1ÿmatrixþÿÿÿ€I@J@O@O@J@J@€I@€J@€M@€O@€M@L@€O@€R@Q@€U@€T@€Q@@Q@ÀR@@R@€H@€G@I@N@€M@N@O@€N@ÀQ@year2ÿmatrixþÿÿÿK@€J@P@€P@€L@€J@K@K@O@€P@€M@€M@ÀP@S@ÀR@€U@€T@ÀP@€R@T@ÀR@K@I@€J@O@O@O@R@N@ÀP@statistics-release-1.7.3/inst/dcov.m000066400000000000000000000113571475240274700174430ustar00rootroot00000000000000## Copyright (C) 2014 - Maria L. Rizzo and Gabor J. Szekely ## Copyright (C) 2014 Juan Pablo Carbajal ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This progrm is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{dCor}, @var{dCov}, @var{dVarX}, @var{dVarY}] =} dcov (@var{x}, @var{y}) ## ## Distance correlation, covariance and correlation statistics. ## ## It returns the distance correlation (@var{dCor}) and the distance covariance ## (@var{dCov}) between @var{x} and @var{y}, the distance variance of @var{x} ## in (@var{dVarX}) and the distance variance of @var{y} in (@var{dVarY}). ## ## @var{x} and @var{y} must have the same number of observations (rows) but they ## can have different number of dimensions (columns). Rows with missing values ## (@qcode{NaN}) in either @var{x} or @var{y} are omitted. ## ## The Brownian covariance is the same as the distance covariance: ## ## @tex ## $$ cov_W (X, Y) = dCov(X, Y) $$ ## ## @end tex ## @ifnottex ## @math{cov_W (@var{x}, @var{y}) = dCov (@var{x}, @var{y})} ## @end ifnottex ## ## and thus Brownian correlation is the same as distance correlation. ## ## @seealso{corr, cov} ## @end deftypefn function [dCor, dCov, dVarX, dVarY] = dcov (x, y) ## Validate input size if (size (x, 1) != size (y, 1)) error ("dcov: Sample sizes (rows) in X and Y must agree."); endif ## Exclude missing values is_nan = any ([isnan(x) isnan(y)], 2); x(is_nan,:) = []; y(is_nan,:) = []; ## Calculate double centered distance A = pdist2 (x, x); A_col = mean (A, 1); A_row = mean (A, 2); Acbar = ones (size (A_row)) * A_col; Arbar = A_row * ones (size (A_col)); A_bar = mean (A(:)) * ones (size (A)); A = A - Acbar - Arbar + A_bar; B = pdist2 (y, y); B_col = mean (B, 1); B_row = mean (B, 2); Bcbar = ones (size (B_row)) * B_col; Brbar = B_row * ones (size (B_col)); B_bar = mean (B(:)) * ones (size (B)); B = B - Bcbar - Brbar + B_bar; ## Calculate distance covariance and variances dCov = sqrt (mean (A(:) .* B(:))); dVarX = sqrt (mean (A(:) .^ 2)); dVarY = sqrt (mean (B(:) .^ 2)); ## Calculate distance correlation V = sqrt (dVarX .* dVarY); if V > 0 dCor = dCov / V; else dCor = 0; end endfunction %!demo %! base=@(x) (x- min(x))./(max(x)-min(x)); %! N = 5e2; %! x = randn (N,1); x = base (x); %! z = randn (N,1); z = base (z); %! # Linear relations %! cy = [1 0.55 0.3 0 -0.3 -0.55 -1]; %! ly = x .* cy; %! ly(:,[1:3 5:end]) = base (ly(:,[1:3 5:end])); %! # Correlated Gaussian %! cz = 1 - abs (cy); %! gy = base ( ly + cz.*z); %! # Shapes %! sx = repmat (x,1,7); %! sy = zeros (size (ly)); %! v = 2 * rand (size(x,1),2) - 1; %! sx(:,1) = v(:,1); sy(:,1) = cos(2*pi*sx(:,1)) + 0.5*v(:,2).*exp(-sx(:,1).^2/0.5); %! R =@(d) [cosd(d) sind(d); -sind(d) cosd(d)]; %! tmp = R(35) * v.'; %! sx(:,2) = tmp(1,:); sy(:,2) = tmp(2,:); %! tmp = R(45) * v.'; %! sx(:,3) = tmp(1,:); sy(:,3) = tmp(2,:); %! sx(:,4) = v(:,1); sy(:,4) = sx(:,4).^2 + 0.5*v(:,2); %! sx(:,5) = v(:,1); sy(:,5) = 3*sign(v(:,2)).*(sx(:,5)).^2 + v(:,2); %! sx(:,6) = cos (2*pi*v(:,1)) + 0.5*(x-0.5); %! sy(:,6) = sin (2*pi*v(:,1)) + 0.5*(z-0.5); %! sx(:,7) = x + sign(v(:,1)); sy(:,7) = z + sign(v(:,2)); %! sy = base (sy); %! sx = base (sx); %! # scaled shape %! sc = 1/3; %! ssy = (sy-0.5) * sc + 0.5; %! n = size (ly,2); %! ym = 1.2; %! xm = 0.5; %! fmt={'horizontalalignment','center'}; %! ff = "% .2f"; %! figure (1) %! for i=1:n %! subplot(4,n,i); %! plot (x, gy(:,i), '.b'); %! axis tight %! axis off %! text (xm,ym,sprintf (ff, dcov (x,gy(:,i))),fmt{:}) %! %! subplot(4,n,i+n); %! plot (x, ly(:,i), '.b'); %! axis tight %! axis off %! text (xm,ym,sprintf (ff, dcov (x,ly(:,i))),fmt{:}) %! %! subplot(4,n,i+2*n); %! plot (sx(:,i), sy(:,i), '.b'); %! axis tight %! axis off %! text (xm,ym,sprintf (ff, dcov (sx(:,i),sy(:,i))),fmt{:}) %! v = axis (); %! %! subplot(4,n,i+3*n); %! plot (sx(:,i), ssy(:,i), '.b'); %! axis (v) %! axis off %! text (xm,ym,sprintf (ff, dcov (sx(:,i),ssy(:,i))),fmt{:}) %! endfor %!error dcov (randn (30, 5), randn (25,5)) statistics-release-1.7.3/inst/dendrogram.m000066400000000000000000000337301475240274700206310ustar00rootroot00000000000000## Copyright (c) 2012 Juan Pablo Carbajal ## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} dendrogram (@var{tree}) ## @deftypefnx {statistics} {} dendrogram (@var{tree}, @var{p}) ## @deftypefnx {statistics} {} dendrogram (@var{tree}, @var{prop}, @var{val}) ## @deftypefnx {statistics} {} dendrogram (@var{tree}, @var{p}, @var{prop}, @var{val} ) ## @deftypefnx {statistics} {@var{h} =} dendrogram (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{t}, @var{perm}] =} dendrogram (@dots{}) ## ## Plot a dendrogram of a hierarchical binary cluster tree. ## ## Given @var{tree}, a hierarchical binary cluster tree as the output of ## @code{linkage}, plot a dendrogram of the tree. The number of leaves shown by ## the dendrogram plot is limited to @var{p}. The default value for @var{p} is ## 30. Set @var{p} to 0 to plot all leaves. ## ## The optional outputs are @var{h}, @var{t} and @var{perm}: ## @itemize @bullet ## @item @var{h} is a handle to the lines of the plot. ## ## @item @var{t} is the vector with the numbers assigned to each leaf. ## Each element of @var{t} is a leaf of @var{tree} and its value is the number ## shown in the plot. ## When the dendrogram plot is collapsed, that is when the number of shown ## leaves @var{p} is inferior to the total number of leaves, a single leaf of ## the plot can represent more than one leaf of @var{tree}: in that case ## multiple elements of @var{t} share the same value, that is the same leaf of ## the plot. ## When the dendrogram plot is not collapsed, each leaf of the plot is the leaf ## of @var{tree} with the same number. ## ## @item @var{perm} is the vector list of the leaves as ordered as in the plot. ## @end itemize ## ## Additional input properties can be specified by pairs of properties and ## values. Known properties are: ## @itemize @bullet ## @item @qcode{"Reorder"} ## Reorder the leaves of the dendrogram plot using a numerical vector of size n, ## the number of leaves. When @var{p} is smaller than @var{n}, the reordering ## cannot break the @var{p} groups of leaves. ## ## @item @qcode{"Orientation"} ## Change the orientation of the plot. Available values: @qcode{top} (default), ## @qcode{bottom}, @qcode{left}, @qcode{right}. ## ## @item @qcode{"CheckCrossing"} ## Check if the lines of a reordered dendrogram cross each other. Available ## values: @qcode{true} (default), @qcode{false}. ## ## @item @qcode{"ColorThreshold"} ## Not implemented. ## ## @item @qcode{"Labels"} ## Use a char, string or cellstr array of size @var{n} to set the label for each ## leaf; the label is dispayed only for nodes with just one leaf. ## @end itemize ## ## @seealso{cluster, clusterdata, cophenet, inconsistent, linkage, pdist} ## @end deftypefn function [H, T, perm] = dendrogram (tree, varargin) [m, d] = size (tree); if ((d != 3) || (! isnumeric (tree)) || (! (max (tree(end, 1:2)) == m * 2))) error (strcat (["dendrogram: tree must be a matrix as generated"], ... [" by the linkage function."])); endif pair_index = 1; ## Node count n = m + 1; ## Add default values P = 30; vReorder = []; csLabels = {}; checkCrossing = 1; orientation = "top"; if (nargin > 1) if (isnumeric (varargin{1}) && isscalar (varargin{1})) ## dendrogram (tree, P) P = varargin{1}; pair_index++; endif ## dendrogram (..., Name, Value) while (pair_index < (nargin - 1)) switch (lower (varargin{pair_index})) case "reorder" if (isvector (varargin{pair_index + 1}) && isnumeric (varargin{pair_index + 1}) && length (varargin{pair_index + 1}) == n ) vReorder = varargin{pair_index + 1}; else error (strcat (["dendrogram: 'reorder' must be a numeric"], ... [" vector of size n, the number of leaves."])); endif case "checkcrossing" if (ischar (varargin{pair_index + 1})) switch (lower (varargin{pair_index + 1})) case "true" checkCrossing = 1; case "false" checkCrossing = 0; otherwise error ("dendrogram: unknown value '%s' for CheckCrossing.", ... varargin{pair_index + 1}); endswitch else error (strcat (["dendrogram: 'CheckCrossing' must be"], ... [" either 'true' or 'false'."])); endif case "colorthreshold" warning ("dendrogram: property '%s' not implemented.",... varargin{pair_index}); case "orientation" orientation = varargin{pair_index + 1}; # validity check below case "labels" if (ischar (varargin{pair_index + 1}) && (isvector (varargin{pair_index + 1}) && length (varargin{pair_index + 1}) == n) || (ismatrix (varargin{pair_index + 1}) && rows (varargin{pair_index + 1}) == n)) csLabels = cellstr (varargin{pair_index + 1}); elseif (iscellstr (varargin{pair_index + 1}) && length (varargin{pair_index + 1}) == n) csLabels = varargin{pair_index + 1}; else error (strcat (["dendrogram: labels must be a char or"], ... [" string or cellstr array of size n."])); endif otherwise error ("dendrogram: unknown property '%s'.", varargin{pair_index}); endswitch pair_index += 2; endwhile endif ## MATLAB compatibility: ## P <= 0 to plot all leaves if (P < 1) P = n; endif if (n > P) level_0 = tree((n - P), 3); else P = n; level_0 = 0; endif vLeafPosition = zeros((n + m), 1); T = (1:n)'; nodecnt = 1; ## main dendrogram_recursive (m, 0); ## T reordering ## MATLAB compatibility: when n > P, each node group is renamed with a number ## between 1 and P, according to the smallest node index of each group; ## the group with the node 1 is always group 1, while group 2 is the group ## with the smallest node index outside of group 1, and group 3 is the group ## with the smallest node index outside of groups 1 and 2... newT = 1 : (length (T)); if (n > P) uniqueT = unique (T); minT = zeros (size (uniqueT)); counter = 1; for i = 1:length (uniqueT) # it should be exactly equal to P idcs = find (T == uniqueT(i)); minT(i) = min (idcs); endfor minT = minT(find (minT > 0)); # to prevent a strange bug [minT, minTidcs] = sort (minT); uniqueT = uniqueT(minTidcs); for i = 1:length (uniqueT) idcs = find (T == uniqueT(i)); newT(idcs) = counter++; endfor endif ## leaf reordering if (! isempty(vReorder)) if (P < n) checkT = newT(vReorder(:)); for i = 1 : P idcs = find (checkT == i); if (length (idcs) > 1) if (max (idcs) - min (idcs) >= length (idcs)) error (strcat (["dendrogram: invalid reordering that"], ... [" redefines the 'P' groups of leaves"])); endif endif endfor checkT = unique (checkT, "stable"); vNewLeafPosition = zeros (n, 1); uT = unique (T, "stable"); for i = 1:P vNewLeafPosition(uT(checkT(i))) = i; endfor vLeafPosition = vNewLeafPosition; else for i = 1:length (vReorder) vLeafPosition(vReorder(i)) = i; endfor endif endif ## figure x = []; ## ticks and tricks xticks = 1:P; perm = zeros (P, 1); for i = 1 : length (vLeafPosition) if (vLeafPosition(i) != 0) idcs = find (T == i); perm(vLeafPosition(i)) = newT(idcs(1)); endif endfor T = newT; # this should be unnecessary for n <= P ## lines for i = (n - P + 1):m vLeafPosition(n + i) = mean (vLeafPosition(tree(i, 1:2), 1)); x(end + 1,1:4) = [vLeafPosition(tree(i, 1:2))' tree(i, [3 3])]; for j = 1 : 2 x0 = 0; if (tree(i,j) > (2 * n - P)) x0 = tree(tree(i, j) - n, 3); endif x(end + 1, 1:4) = [vLeafPosition(tree(i, [j j]))' x0 tree(i, 3)]; endfor endfor ## plot stuff if (strcmp (orientation, "top")) H = line (x(:, 1:2)', x(:, 3:4)', "color", "blue"); set (gca, "xticklabel", perm, "xtick", xticks); elseif (strcmp (orientation, "bottom")) H = line (x(:, 1:2)', x(:, 3:4)', "color", "blue"); set (gca, "xticklabel", perm, "xtick", xticks, "xaxislocation", "top"); axis ("ij"); elseif (strcmp (orientation, "left")) H = line (x(:, 3:4)', x(:, 1:2)', "color", "blue"); set (gca, "yticklabel", perm, "ytick", xticks, "xdir", "reverse",... "yaxislocation", "right"); elseif (strcmp (orientation, "right")) H = line (x(:, 3:4)', x(:, 1:2)', "color", "blue"); set (gca, "yticklabel", perm, "ytick", xticks); else close (H); error ("dendrogram: invalid orientation '%s'", orientation); endif ## labels if (! isempty (csLabels)) csCurrent = cellstr (num2str (perm)); for i = 1:n ## when there is just one leaf, use the named label for that leaf if (1 == length (find (T == i))) csCurrent(find (perm == i)) = csLabels(find (T == i)); endif endfor switch (orientation) case {"top", "bottom"} xticklabels (csCurrent); case {"left", "right"} yticklabels (csCurrent); endswitch endif ## check crossings if (checkCrossing && ! isempty(vReorder)) for j = 1:rows (x) if (x(j, 3) == x(j, 4)) # an horizontal line for i = 1:rows (x) if (x(i, 1) == x(i, 2) && ... # orthogonal lines (x(i, 1) > x(j, 1) && x(i, 1) < x(j, 2)) && ... (x(j, 3) > x(i, 3) && x(j, 3) < x(i, 4))) warning ("dendrogram: line intersection detected"); endif endfor endif endfor endif ## dendrogram_recursive function dendrogram_recursive (k, cn) if (tree(k, 3) > level_0) for j = 1:2 if (tree(k, j) > n) dendrogram_recursive (tree(k, j) - n, 0) else vLeafPosition(tree(k, j)) = nodecnt++; T(tree(k, j)) = tree(k, j); endif endfor else for j = 1:2 if (cn == 0) cn = n + k; vLeafPosition(cn) = nodecnt++; endif if (tree(k, j) > n) dendrogram_recursive (tree(k, j) - n, cn) else T(tree(k, j)) = cn; endif endfor endif endfunction endfunction %!demo %! ## simple dendrogram %! y = [4, 5; 2, 6; 3, 7; 8, 9; 1, 10]; %! y(:,3) = 1:5; %! dendrogram (y); %! title ("simple dendrogram"); %!demo %! ## another simple dendrogram %! v = 2 * rand (30, 1) - 1; %! d = abs (bsxfun (@minus, v(:, 1), v(:, 1)')); %! y = linkage (squareform (d, "tovector")); %! dendrogram (y); %! title ("another simple dendrogram"); %!demo %! ## collapsed tree, find all the leaves of node 5 %! X = randn (60, 2); %! D = pdist (X); %! y = linkage (D, "average"); %! subplot (2, 1, 1); %! title ("original tree"); %! dendrogram (y, 0); %! subplot (2, 1, 2); %! title ("collapsed tree"); %! [~, t] = dendrogram (y, 20); %! find(t == 5) %!demo %! ## optimal leaf order %! X = randn (30, 2); %! D = pdist (X); %! y = linkage (D, "average"); %! order = optimalleaforder (y, D); %! subplot (2, 1, 1); %! title ("original leaf order"); %! dendrogram (y); %! subplot (2, 1, 2); %! title ("optimal leaf order"); %! dendrogram (y, "Reorder", order); %!demo %! ## horizontal orientation and labels %! X = randn (8, 2); %! D = pdist (X); %! L = ["Snow White"; "Doc"; "Grumpy"; "Happy"; "Sleepy"; "Bashful"; ... %! "Sneezy"; "Dopey"]; %! y = linkage (D, "average"); %! dendrogram (y, "Orientation", "left", "Labels", L); %! title ("horizontal orientation and labels"); ## Test plotting %!shared visibility_setting %! visibility_setting = get (0, "DefaultFigureVisible"); %!test %! hf = figure ("visible", "off"); %! unwind_protect %! y = [4, 5; 2, 6; 3, 7; 8, 9; 1, 10]; %! y(:,3) = 1:5; %! dendrogram (y); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! y = [4, 5; 2, 6; 3, 7; 8, 9; 1, 10]; %! y(:,3) = 1:5; %! dendrogram (y); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! v = 2 * rand (30, 1) - 1; %! d = abs (bsxfun (@minus, v(:, 1), v(:, 1)')); %! y = linkage (squareform (d, "tovector")); %! dendrogram (y); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! X = randn (30, 2); %! D = pdist (X); %! y = linkage (D, "average"); %! order = optimalleaforder (y, D); %! subplot (2, 1, 1); %! title ("original leaf order"); %! dendrogram (y); %! subplot (2, 1, 2); %! title ("optimal leaf order"); %! dendrogram (y, "Reorder", order); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error dendrogram (); %!error dendrogram (ones (2, 2), 1); %!error dendrogram ([1 2 1], 1, "xxx", "xxx"); %!error dendrogram ([1 2 1], "Reorder", "xxx"); %!error dendrogram ([1 2 1], "Reorder", [1 2 3 4]); %! fail ('dendrogram ([1 2 1], "Orientation", "north")', "invalid orientation .*") statistics-release-1.7.3/inst/dist_fit/000077500000000000000000000000001475240274700201305ustar00rootroot00000000000000statistics-release-1.7.3/inst/dist_fit/betafit.m000066400000000000000000000253761475240274700217410ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} betafit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} betafit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} betafit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} betafit (@var{x}, @var{alpha}, @var{freq}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} betafit (@var{x}, @var{alpha}, @var{options}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} betafit (@var{x}, @var{alpha}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the Beta distribution. ## ## @code{@var{paramhat} = betafit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Beta distribution given the data in vector ## @var{x}. @qcode{@var{paramhat}([1, 2])} corresponds to the @math{α} and ## @math{β} shape parameters, respectively. Missing values, @qcode{NaNs}, are ## ignored. ## ## @code{[@var{paramhat}, @var{paramci}] = betafit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = betafit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals of the estimated ## parameter. By default, the optional argument @var{alpha} is 0.05 ## corresponding to 95% confidence intervals. ## ## @code{[@dots{}] = betafit (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@var{paramhat}, @var{paramci}] = nbinfit (@var{x}, @var{alpha}, ## @var{options})} specifies control parameters for the iterative algorithm used ## to compute ML estimates with the @code{fminsearch} function. @var{options} ## is a structure with the following fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## The Beta distribution is defined on the open interval @math{(0,1)}. However, ## @code{betafit} can also compute the unbounded beta likelihood function for ## data that include exact zeros or ones. In such cases, zeros and ones are ## treated as if they were values that have been left-censored at ## @qcode{sqrt (realmin)} or right-censored at @qcode{1 - eps/2}, respectively. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betacdf, betainv, betapdf, betarnd, betalike, betastat} ## @end deftypefn function [paramhat, paramci] = betafit (x, alpha, varargin) ## Check X for being a vector if (isempty (x)) phat = nan (1, 2, class (x)); pci = nan (2, 2, class (x)); return elseif (! isvector (x) || ! isreal (x)) error ("betafit: X must be a vector of real values."); endif ## Check that X contains values in the range [0,1] if (any (x < 0) || any (x > 1)) error ("betafit: X must be in the range [0,1]."); endif ## Check X being a constant vector if (min (x) == max(x)) error ("betafit: X must contain distinct values."); endif ## Check ALPHA if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("betafit: wrong value for ALPHA."); endif endif ## Add defaults freq = ones (size (x)); options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; ## Check extra arguments for FREQ vector and/or 'options' structure if (nargin > 2) if (numel (varargin) == 1 && isstruct (varargin{1})) options = varargin{1}; elseif (numel (varargin) == 1 && isnumeric (varargin{1})) freq = varargin{1}; elseif (numel (varargin) == 2) freq = varargin{1}; options = varargin{2}; endif if (isempty (freq)) freq = ones (size (x)); endif ## Check for valid freq vector if (! isequal (size (x), size (freq))) error ("betafit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("betafit: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("betafit: FREQ must contain integer values."); endif ## Check for valid options structure if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["betafit: 'options' argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Remove missing values remove = isnan (x) | isnan (freq); x(remove) = []; freq(remove) = []; ## Expand frequency if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Estimate initial parameters numx = length (x); tmp1 = prod ((1 - x) .^ (1 / numx)); tmp2 = prod (x .^ (1 / numx)); tmp3 = (1 - tmp1 - tmp2); ahat = 0.5 * (1 - tmp1) / tmp3; bhat = 0.5 * (1 - tmp2) / tmp3; init = log ([ahat, bhat]); ## Add tolerance for boundary conditions x_lo = sqrt (realmin (class (x))); x_hi = 1 - eps (class (x)) / 2; ## All values are strictly within the interval (0,1) if (all (x > x_lo) && all (x < x_hi)) sumlogx = sum (log (x)); sumlog1px = sum (log1p (-x)); paramhat = fminsearch (@cont_negloglike, init, options); paramhat = exp (paramhat); ## Find boundary elements and process them separately else num0 = sum (x < x_lo); num1 = sum (x > x_hi); x_ct = x (x > x_lo & x < x_hi); numx = length (x_ct); sumlogx = sum (log (x_ct)); sumlog1px = sum (log1p (-x_ct)); paramhat = fminsearch (@mixed_negloglike, init, options); paramhat = exp (paramhat); endif ## Compute confidence intervals if (nargout == 2) [~, acov] = betalike (paramhat,x); logphat = log (paramhat); serrlog = sqrt (diag (acov))' ./ paramhat; p_int = [alpha/2; 1-alpha/2]; paramci = exp (norminv ([p_int p_int], ... [logphat; logphat], [serrlog; serrlog])); endif ## Continuous Negative log-likelihood function function nll = cont_negloglike (params) params = exp (params); nll = numx * betaln (params(1), params(2)) - (params(1) - 1) ... * sumlogx - (params(2) - 1) * sumlog1px; endfunction ## Unbounded Negative log-likelihood function function nll = mixed_negloglike (params) params = exp (params); nll = numx * betaln (params(1), params(2)) - (params(1) - 1) ... * sumlogx - (params(2) - 1) * sumlog1px; ## Handle zeros if (num0 > 0) nll = nll - num0 * log (betainc (x_lo, params(1), params(2), "lower")); endif ## Handle ones if (num1 > 0) nll = nll - num1 * log (betainc (x_hi, params(1), params(2), "upper")); endif endfunction endfunction %!demo %! ## Sample 2 populations from different Beta distibutions %! randg ("seed", 1); # for reproducibility %! r1 = betarnd (2, 5, 500, 1); %! randg ("seed", 2); # for reproducibility %! r2 = betarnd (2, 2, 500, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, 12, 15); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their shape parameters %! a_b_A = betafit (r(:,1)); %! a_b_B = betafit (r(:,2)); %! %! ## Plot their estimated PDFs %! x = [min(r(:)):0.01:max(r(:))]; %! y = betapdf (x, a_b_A(1), a_b_A(2)); %! plot (x, y, "-pr"); %! y = betapdf (x, a_b_B(1), a_b_B(2)); %! plot (x, y, "-sg"); %! ylim ([0, 4]) %! legend ({"Normalized HIST of sample 1 with α=2 and β=5", ... %! "Normalized HIST of sample 2 with α=2 and β=2", ... %! sprintf("PDF for sample 1 with estimated α=%0.2f and β=%0.2f", ... %! a_b_A(1), a_b_A(2)), ... %! sprintf("PDF for sample 2 with estimated α=%0.2f and β=%0.2f", ... %! a_b_B(1), a_b_B(2))}) %! title ("Two population samples from different Beta distibutions") %! hold off ## Test output %!test %! x = 0.01:0.02:0.99; %! [paramhat, paramci] = betafit (x); %! paramhat_out = [1.0199, 1.0199]; %! paramci_out = [0.6947, 0.6947; 1.4974, 1.4974]; %! assert (paramhat, paramhat_out, 1e-4); %! assert (paramci, paramci_out, 1e-4); %!test %! x = 0.01:0.02:0.99; %! [paramhat, paramci] = betafit (x, 0.01); %! paramci_out = [0.6157, 0.6157; 1.6895, 1.6895]; %! assert (paramci, paramci_out, 1e-4); %!test %! x = 0.00:0.02:1; %! [paramhat, paramci] = betafit (x); %! paramhat_out = [0.0875, 0.1913]; %! paramci_out = [0.0822, 0.1490; 0.0931, 0.2455]; %! assert (paramhat, paramhat_out, 1e-4); %! assert (paramci, paramci_out, 1e-4); ## Test input validation %!error betafit ([0.2, 0.5+i]); %!error betafit (ones (2,2) * 0.5); %!error betafit ([0.5, 1.2]); %!error betafit ([0.1, 0.1]); %!error betafit ([0.01:0.1:0.99], 1.2); %!error ... %! betafit ([0.01:0.01:0.05], 0.05, [1, 2, 3, 2]); %!error ... %! betafit ([0.01:0.01:0.05], 0.05, [1, 2, 3, 2, -1]); %!error ... %! betafit ([0.01:0.01:0.05], 0.05, [1, 2, 3, 2, 1.5]); %!error ... %! betafit ([0.01:0.01:0.05], 0.05, struct ("option", 234)); %!error ... %! betafit ([0.01:0.01:0.05], 0.05, ones (1,5), struct ("option", 234)); statistics-release-1.7.3/inst/dist_fit/betalike.m000066400000000000000000000157531475240274700221010ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} betalike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} betalike (@var{params}, @var{x}) ## ## Negative log-likelihood for the Beta distribution. ## ## @code{@var{nlogL} = betalike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Beta distribution ## with (1) shape parameter @math{α} and (2) shape parameter @math{β} given in ## the two-element vector @var{params}. Both parameters must be positive real ## numbers and the data in the range @math{[0,1]}. Out of range parameters or ## data return @qcode{NaN}. ## ## @code{[@var{nlogL}, @var{avar}] = betalike (@var{params}, @var{x})} returns ## the inverse of Fisher's information matrix, @var{avar}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = betalike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## The Beta distribution is defined on the open interval @math{(0,1)}. However, ## @code{betafit} can also compute the unbounded beta likelihood function for ## data that include exact zeros or ones. In such cases, zeros and ones are ## treated as if they were values that have been left-censored at ## @qcode{sqrt (realmin)} or right-censored at @qcode{1 - eps/2}, respectively. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betacdf, betainv, betapdf, betarnd, betafit, betastat} ## @end deftypefn function [nlogL, avar] = betalike (params, x, freq) ## Check input arguments and add defaults if (nargin < 2) error ("betalike: function called with too few input arguments."); endif if (numel (params) != 2) error ("betalike: wrong parameters length."); endif if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("betalike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("betalike: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("betalike: FREQ must contain integer values."); endif ## Expand frequency if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Get α and β parameters a = params(1); b = params(2); ## Force X to column vector x = x(:); ## Return NaN for out of range parameters or data. a(a <= 0) = NaN; b(b <= 0) = NaN; xmin = min (x); xmax = max (x); x(! (0 <= x & x <= 1)) = NaN; ## Add tolerance for boundary conditions x_lo = sqrt (realmin (class (x))); x_hi = 1 - eps (class (x)) / 2; ## All values are strictly within the interval (0,1) if (all (x > x_lo) && all (x < x_hi)) num0 = 0; num1 = 0; x_ct = x; numx = length (x_ct); ## Find boundary elements and process them separately else num0 = sum (x < x_lo); num1 = sum (x > x_hi); x_ct = x (x > x_lo & x < x_hi); numx = length (x_ct); endif ## Compute continuous log likelihood logx = log (x_ct); log1px = log1p (-x_ct); sumlogx = sum (logx); sumlog1px = sum (log1px); nlogL = numx * betaln (a, b) - (a - 1) * sumlogx - (b - 1) * sumlog1px; ## Include log likelihood for zeros if (num0 > 0) nlogL = nlogL - num0 * log (betainc (x_lo, a, b, "lower")); endif ## Include log likelihood for ones if (num1 > 0) nlogL = nlogL - num1 * log (betainc (x_hi, a, b, "upper")); endif ## Compute the asymptotic covariance if (nargout > 1) if (numel (x) < 2) error ("betalike: not enough data in X."); endif ## Compute the Jacobian of the likelihood for values (0,1) psiab = psi (a + b); psi_a = psi (a); psi_b = psi (b); J = [logx+psiab-psi_a, log1px+psiab-psi_b]; ## Add terms into the Jacobian for the zero and one values. if (num0 > 0 || num1 > 0) dd = sqrt (eps (class (x))); aa = a + a * dd * [1, -1]; bb = b + b * dd * [1, -1]; ad = 2 * a *dd; bd = 2 * b *dd; if (num0 > 0) da = diff (log (betainc (x_lo, aa, b, "lower"))) / ad; db = diff (log (betainc (x_lo, a, bb, "lower"))) / bd; J = [J; repmat([da, db], num0, 1)]; endif if num1 > 0 da = diff (log (betainc (x_hi, aa, b, "upper"))) / ad; db = diff (log (betainc (x_hi, a, bb, "upper"))) / bd; J = [J; repmat([da, db], num1, 1)]; endif endif ## Invert the inner product of the Jacobian to get the asymptotic covariance [~, R] = qr (J, 0); if (any (isnan (R(:)))) avar = [NaN, NaN; NaN, NaN]; else Rinv = R \ eye (2); avar = Rinv * Rinv'; endif endif endfunction ## Test output %!test %! x = 0.01:0.02:0.99; %! [nlogL, avar] = betalike ([2.3, 1.2], x); %! avar_out = [0.03691678, 0.02803056; 0.02803056, 0.03965629]; %! assert (nlogL, 17.873477715879040, 3e-14); %! assert (avar, avar_out, 1e-7); %!test %! x = 0.01:0.02:0.99; %! [nlogL, avar] = betalike ([1, 4], x); %! avar_out = [0.02793282, 0.02717274; 0.02717274, 0.03993361]; %! assert (nlogL, 79.648061114839550, 1e-13); %! assert (avar, avar_out, 1e-7); %!test %! x = 0.00:0.02:1; %! [nlogL, avar] = betalike ([1, 4], x); %! avar_out = [0.00000801564765, 0.00000131397245; ... %! 0.00000131397245, 0.00070827639442]; %! assert (nlogL, 573.2008434477486, 1e-10); %! assert (avar, avar_out, 1e-14); ## Test input validation %!error ... %! betalike ([12, 15]); %!error betalike ([12, 15, 3], [1:50]); %!error ... %! betalike ([12, 15], ones (10, 1), ones (8,1)) %!error ... %! betalike ([12, 15], ones (1, 8), [1 1 1 1 1 1 1 -1]) %!error ... %! betalike ([12, 15], ones (1, 8), [1 1 1 1 1 1 1 1.5]) statistics-release-1.7.3/inst/dist_fit/binofit.m000066400000000000000000000143551475240274700217500ustar00rootroot00000000000000## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pshat} =} binofit (@var{x}, @var{n}) ## @deftypefnx {statistics} {[@var{pshat}, @var{psci}] =} binofit (@var{x}, @var{n}) ## @deftypefnx {statistics} {[@var{pshat}, @var{psci}] =} binofit (@var{x}, @var{n}, @var{alpha}) ## ## Estimate parameter and confidence intervals for the binomial distribution. ## ## @code{@var{pshat} = binofit (@var{x}, @var{n})} returns the maximum ## likelihood estimate (MLE) of the probability of success for the binomial ## distribution. @var{x} and @var{n} are scalars containing the number of ## successes and the number of trials, respectively. If @var{x} and @var{n} are ## vectors, @code{binofit} returns a vector of estimates whose @math{i}-th ## element is the parameter estimate for @var{x}(i) and @var{n}(i). A scalar ## value for @var{x} or @var{n} is expanded to the same size as the other input. ## ## @code{[@var{pshat}, @var{psci}] = binofit (@var{x}, @var{n}, @var{alpha})} ## also returns the @qcode{100 * (1 - @var{alpha})} percent confidence intervals ## of the estimated parameter. By default, the optional argument @var{alpha} ## is 0.05 corresponding to 95% confidence intervals. ## ## @code{binofit} treats a vector @var{x} as a collection of measurements from ## separate samples, and returns a vector of estimates. If you want to treat ## @var{x} as a single sample and compute a single parameter estimate and ## confidence interval, use @qcode{binofit (sum (@var{x}), sum (@var{n}))} when ## @var{n} is a vector, and ## @qcode{binofit (sum (@var{x}), @var{n} * length (@var{x}))} when @var{n} is a ## scalar. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binocdf, binoinv, binopdf, binornd, binolike, binostat} ## @end deftypefn function [pshat, psci] = binofit (x, n, alpha) ## Check input arguments if (nargin < 2) error ("binofit: function called with too few input arguments."); endif if (any (x < 0)) error ("binofit: X cannot have negative values."); endif if (! isvector (x)) error ("binofit: X must be a vector."); endif if (any (n < 0) || any (n != round (n)) || any (isinf (n))) error ("binofit: N must be a non-negative integer."); endif if (! (isscalar (n) || isequal (size (n), size (x)))) error ("binofit: N must be a scalar or the same size as X."); endif if (any (x > n)) error ("binofit: N must be at least as large as X."); endif if (nargin < 3 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("binofit: wrong value for ALPHA."); endif endif ## Compute pshat pshat = x ./ n; ## Compute lower confidence interval nu1 = 2 * x; nu2 = 2 * (n - x + 1); F = finv (alpha / 2, nu1, nu2); lb = (nu1 .* F) ./ (nu2 + nu1 .* F); x0 = find (x == 0); if (! isempty (x0)) lb(x0) = 0; endif ## Compute upper confidence interval nu1 = 2 * (x + 1); nu2 = 2 * (n - x); F = finv (1 - alpha / 2, nu1, nu2); ub = (nu1 .* F) ./ (nu2 + nu1 .* F); xn = find (x == n); if (! isempty (xn)) ub(xn) = 1; endif psci = [lb(:), ub(:)]; endfunction %!demo %! ## Sample 2 populations from different binomial distibutions %! rand ("seed", 1); # for reproducibility %! r1 = binornd (50, 0.15, 1000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = binornd (100, 0.5, 1000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, 23, 0.35); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their probability of success %! pshatA = binofit (r(:,1), 50); %! pshatB = binofit (r(:,2), 100); %! %! ## Plot their estimated PDFs %! x = [min(r(:,1)):max(r(:,1))]; %! y = binopdf (x, 50, mean (pshatA)); %! plot (x, y, "-pg"); %! x = [min(r(:,2)):max(r(:,2))]; %! y = binopdf (x, 100, mean (pshatB)); %! plot (x, y, "-sc"); %! ylim ([0, 0.2]) %! legend ({"Normalized HIST of sample 1 with ps=0.15", ... %! "Normalized HIST of sample 2 with ps=0.50", ... %! sprintf("PDF for sample 1 with estimated ps=%0.2f", ... %! mean (pshatA)), ... %! sprintf("PDF for sample 2 with estimated ps=%0.2f", ... %! mean (pshatB))}) %! title ("Two population samples from different binomial distibutions") %! hold off ## Test output %!test %! x = 0:3; %! [pshat, psci] = binofit (x, 3); %! assert (pshat, [0, 0.3333, 0.6667, 1], 1e-4); %! assert (psci(1,:), [0, 0.7076], 1e-4); %! assert (psci(2,:), [0.0084, 0.9057], 1e-4); %! assert (psci(3,:), [0.0943, 0.9916], 1e-4); %! assert (psci(4,:), [0.2924, 1.0000], 1e-4); ## Test input validation %!error ... %! binofit ([1 2 3 4]) %!error ... %! binofit ([-1, 4, 3, 2], [1, 2, 3, 3]) %!error binofit (ones(2), [1, 2, 3, 3]) %!error ... %! binofit ([1, 4, 3, 2], [1, 2, -1, 3]) %!error ... %! binofit ([1, 4, 3, 2], [5, 5, 5]) %!error ... %! binofit ([1, 4, 3, 2], [5, 3, 5, 5]) %!error binofit ([1, 2, 1], 3, 1.2); %!error binofit ([1, 2, 1], 3, 0); %!error binofit ([1, 2, 1], 3, "alpha"); statistics-release-1.7.3/inst/dist_fit/binolike.m000066400000000000000000000131621475240274700221050ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} binolike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} binolike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} binolike (@var{params}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the binomial distribution. ## ## @code{@var{nlogL} = binolike (@var{params}, @var{x})} returns the negative ## log likelihood of the binomial distribution with (1) parameter @var{n} and ## (2) parameter @var{ps}, given in the two-element vector @var{params}, where ## @var{n} is the number of trials and @var{ps} is the probability of success, ## given the number of successes in @var{x}. Unlike @code{binofit}, which ## handles each element in @var{x} independently, @code{binolike} returns the ## negative log likelihood of the entire vector @var{x}. ## ## @code{[@var{nlogL}, @var{acov}] = binolike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = binolike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binocdf, binoinv, binopdf, binornd, binofit, binostat} ## @end deftypefn function [nlogL, acov] = binolike (params, x, freq) ## Check input arguments if (nargin < 2) error ("binolike: function called with too few input arguments."); endif if (! isvector (x)) error ("binolike: X must be a vector."); endif if (length (params) != 2) error ("binolike: PARAMS must be a two-element vector."); endif if (params(1) < 0 || params(1) != round (params(1)) || isinf (params(1))) error (strcat (["binolike: number of trials, PARAMS(1), must be a"], ... [" finite non-negative integer."])); endif if (params(2) < 0 || params(2) > 1) error (strcat (["binolike: probability of success, PARAMS(2), must be"], ... [" in the range [0,1]."])); endif ## Parse FREQ argument or add default if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("binolike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("binolike: FREQ must not contain negative values."); endif ## Expand frequency vector (if necessary) if (! all (freq == 1)) ## Remove NaNs and zeros remove = isnan (freq) | freq == 0; x(remove) = []; freq(remove) = []; xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif if (any (x < 0)) error ("binolike: X cannot have negative values."); endif if (any (x > params(1))) error (strcat (["binolike: number of successes, X, must be at least"], ... [" as large as the number of trials, N."])); endif ## Compute negative log-likelihood and asymptotic covariance n = params(1); ps = params(2); numx = length (x); nlogL = -sum (log (binopdf (x, n, ps))); tmp = ps * (1 - ps) / (n * numx); acov = [0, 0; 0, tmp]; endfunction ## Test output %!assert (binolike ([3, 0.333], [0:3]), 6.8302, 1e-4) %!assert (binolike ([3, 0.333], 0), 1.2149, 1e-4) %!assert (binolike ([3, 0.333], 1), 0.8109, 1e-4) %!assert (binolike ([3, 0.333], 2), 1.5056, 1e-4) %!assert (binolike ([3, 0.333], 3), 3.2988, 1e-4) %!test %! [nlogL, acov] = binolike ([3, 0.333], 3); %! assert (acov(4), 0.0740, 1e-4) ## Test input validation %!error binolike (3.25) %!error binolike ([5, 0.2], ones (2)) %!error ... %! binolike ([1, 0.2, 3], [1, 3, 5, 7]) %!error binolike ([1.5, 0.2], 1) %!error binolike ([-1, 0.2], 1) %!error binolike ([Inf, 0.2], 1) %!error binolike ([5, 1.2], [3, 5]) %!error binolike ([5, -0.2], [3, 5]) %!error ... %! binolike ([5, 0.5], ones (10, 1), ones (8,1)) %!error ... %! binolike ([5, 0.5], ones (1, 8), [1 1 1 1 1 1 1 -1]) %!error binolike ([5, 0.2], [-1, 3]) %!error binolike ([5, 0.2], [3, 5, 7]) statistics-release-1.7.3/inst/dist_fit/bisafit.m000066400000000000000000000224551475240274700217370ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} bisafit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} bisafit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} bisafit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} bisafit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} bisafit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} bisafit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate mean and confidence intervals for the Birnbaum-Saunders distribution. ## ## @code{@var{muhat} = bisafit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Birnbaum-Saunders distribution given the ## data in @var{x}. @qcode{@var{paramhat}(1)} is the scale parameter, ## @var{beta}, and @qcode{@var{paramhat}(2)} is the shape parameter, ## @var{gamma}. ## ## @code{[@var{paramhat}, @var{paramci}] = bisafit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = bisafit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = bisafit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = bisafit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = bisafit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisacdf, bisainv, bisapdf, bisarnd, bisalike, bisastat} ## @end deftypefn function [paramhat, paramci] = bisafit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("bisafit: X must be a vector."); elseif (any (x <= 0)) error ("bisafit: X must contain only positive values."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("bisafit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("bisafit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("bisafit: X and FREQ vectors mismatch."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["bisafit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Starting points as suggested by Birnbaum and Saunders x_uncensored = x(censor==0); xubar = mean (x_uncensored); xuinv = mean (1 ./ x_uncensored); beta = sqrt (xubar ./ xuinv); gamma = 2 .* sqrt (sqrt (xubar .* xuinv) - 1); x0 = [beta, gamma]; ## Minimize negative log-likelihood to estimate parameters f = @(params) bisalike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, x0, options); ## Force positive parameter values paramhat = abs (paramhat); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning ("bisafit: maximum number of function evaluations are exceeded."); elseif (output.iterations >= options.MaxIter) warning ("bisafit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("bisafit: no solution."); endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = bisalike (paramhat, x, censor, freq); ## Get standard errors stderr = sqrt (diag (acov))'; stderr = stderr ./ paramhat; ## Apply log transform phatlog = log (paramhat); ## Compute normal quantiles z = norminv (alpha / 2); ## Compute CI paramci = [phatlog; phatlog] + [stderr; stderr] .* [z, z; -z, -z]; ## Inverse log transform paramci = exp (paramci); endif endfunction %!demo %! ## Sample 3 populations from different Birnbaum-Saunders distibutions %! rand ("seed", 5); # for reproducibility %! r1 = bisarnd (1, 0.5, 2000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = bisarnd (2, 0.3, 2000, 1); %! rand ("seed", 7); # for reproducibility %! r3 = bisarnd (4, 0.5, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 80, 4.2); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 1.1]); %! xlim ([0, 8]); %! hold on %! %! ## Estimate their α and β parameters %! beta_gammaA = bisafit (r(:,1)); %! beta_gammaB = bisafit (r(:,2)); %! beta_gammaC = bisafit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0:0.1:8]; %! y = bisapdf (x, beta_gammaA(1), beta_gammaA(2)); %! plot (x, y, "-pr"); %! y = bisapdf (x, beta_gammaB(1), beta_gammaB(2)); %! plot (x, y, "-sg"); %! y = bisapdf (x, beta_gammaC(1), beta_gammaC(2)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with β=1 and γ=0.5", ... %! "Normalized HIST of sample 2 with β=2 and γ=0.3", ... %! "Normalized HIST of sample 3 with β=4 and γ=0.5", ... %! sprintf("PDF for sample 1 with estimated β=%0.2f and γ=%0.2f", ... %! beta_gammaA(1), beta_gammaA(2)), ... %! sprintf("PDF for sample 2 with estimated β=%0.2f and γ=%0.2f", ... %! beta_gammaB(1), beta_gammaB(2)), ... %! sprintf("PDF for sample 3 with estimated β=%0.2f and γ=%0.2f", ... %! beta_gammaC(1), beta_gammaC(2))}) %! title ("Three population samples from different Birnbaum-Saunders distibutions") %! hold off ## Test output %!test %! paramhat = bisafit ([1:50]); %! paramhat_out = [16.2649, 1.0156]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = bisafit ([1:5]); %! paramhat_out = [2.5585, 0.5839]; %! assert (paramhat, paramhat_out, 1e-4); ## Test input validation %!error bisafit (ones (2,5)); %!error bisafit ([-1 2 3 4]); %!error bisafit ([1, 2, 3, 4, 5], 1.2); %!error bisafit ([1, 2, 3, 4, 5], 0); %!error bisafit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! bisafit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! bisafit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! bisafit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! bisafit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! bisafit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/bisalike.m000066400000000000000000000153661475240274700221040ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} bisalike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} bisalike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} bisalike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} bisalike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the Birnbaum-Saunders distribution. ## ## @code{@var{nlogL} = bisalike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Birnbaum-Saunders ## distribution with (1) scale parameter @var{beta} and (2) shape parameter ## @var{gamma} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = bisalike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = bisalike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = bisalike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisacdf, bisainv, bisapdf, bisarnd, bisafit, bisastat} ## @end deftypefn function [nlogL, acov] = bisalike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("bisalike: function called with too few input arguments."); endif if (! isvector (x)) error ("bisalike: X must be a vector."); endif if (any (x < 0)) error ("bisalike: X cannot have negative values."); endif if (length (params) != 2) error ("bisalike: PARAMS must be a two-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("bisalike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("bisalike: X and FREQ vector mismatch."); endif beta = params(1); gamma = params(2); z = (sqrt (x ./ beta) - sqrt (beta ./ x)) ./ gamma; w = (sqrt (x ./ beta) + sqrt (beta ./ x)) ./ gamma; L = -0.5 .* (z .^ 2 + log (2 .* pi)) + log (w) - log (2 .* x); n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); z_censored = z(censored); Scen = 0.5 * erfc (z_censored ./ sqrt(2)); L(censored) = log (Scen); endif nlogL = -sum (freq .* L); ## Compute asymptotic covariance if (nargout > 1) ## Compute first order central differences of the log-likelihood gradient dp = 0.0001 .* max (abs (params), 1); ngrad_p1 = bisa_ngrad (params + [dp(1), 0], x, censor, freq); ngrad_m1 = bisa_ngrad (params - [dp(1), 0], x, censor, freq); ngrad_p2 = bisa_ngrad (params + [0, dp(2)], x, censor, freq); ngrad_m2 = bisa_ngrad (params - [0, dp(2)], x, censor, freq); ## Compute negative Hessian by normalizing the differences by the increment nH = [(ngrad_p1(:) - ngrad_m1(:))./(2 * dp(1)), ... (ngrad_p2(:) - ngrad_m2(:))./(2 * dp(2))]; ## Force neg Hessian being symmetric nH = 0.5 .* (nH + nH'); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("bisalike: non positive definite Hessian matrix."); acov = NaN (2); return endif ## ACOV estimate is the negative inverse of the Hessian. Rinv = inv (R); acov = Rinv * Rinv; endif endfunction ## Helper function for computing negative gradient function ngrad = bisa_ngrad (params, x, censor, freq) beta = params(1); gamma = params(2); z = (sqrt (x ./ beta) - sqrt (beta ./ x)) ./ gamma; w = (sqrt (x ./ beta) + sqrt (beta ./ x)) ./ gamma; logphi = -0.5 .* (z .^ 2 + log (2 .* pi)); n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); z_censored = z(censored); Scen = 0.5 * erfc (z_censored ./ sqrt(2)); endif dL1 = (w .^ 2 - 1) .* 0.5 .* z ./ (w .* beta); dL2 = (z .^ 2 - 1) ./ gamma; if (n_censored > 0) phi_censored = exp (logphi(censored)); wcen = w(censored); d1Scen = phi_censored .* 0.5 .* wcen ./ beta; d2Scen = phi_censored .* z_censored ./ gamma; dL1(censored) = d1Scen ./ Scen; dL2(censored) = d2Scen ./ Scen; endif ngrad = -[sum(freq .* dL1), sum(freq .* dL2)]; endfunction ## Test results %!test %! nlogL = bisalike ([16.2649, 1.0156], [1:50]); %! assert (nlogL, 215.5905, 1e-4); %!test %! nlogL = bisalike ([2.5585, 0.5839], [1:5]); %! assert (nlogL, 8.9950, 1e-4); ## Test input validation %!error bisalike (3.25) %!error bisalike ([5, 0.2], ones (2)) %!error bisalike ([5, 0.2], [-1, 3]) %!error ... %! bisalike ([1, 0.2, 3], [1, 3, 5, 7]) %!error ... %! bisalike ([1.5, 0.2], [1:5], [0, 0, 0]) %!error ... %! bisalike ([1.5, 0.2], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! bisalike ([1.5, 0.2], [1:5], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/burrfit.m000066400000000000000000000352631475240274700217740ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} burrfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} burrfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} burrfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} burrfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} burrfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} burrfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate mean and confidence intervals for the Burr type XII distribution. ## ## @code{@var{muhat} = burrfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Burr type XII distribution given the data ## in @var{x}. @qcode{@var{paramhat}(1)} is the scale parameter, @var{lambda}, ## @qcode{@var{paramhat}(2)} is the first shape parameter, @var{c}, and ## @qcode{@var{paramhat}(3)} is the second shape parameter, @var{k} ## ## @code{[@var{paramhat}, @var{paramci}] = burrfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = burrfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = burrfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = burrfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = burrfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute the maximum likelihood ## estimates. @var{options} is a structure with the following field and its ## default value: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the Burr type XII distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{burrcdf, burrinv, burrpdf, burrrnd, burrlike, burrstat} ## @end deftypefn function [paramhat, paramci] = burrfit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("burrfit: X must be a vector."); elseif (any (x <= 0)) error ("burrfit: X must contain only positive values."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("burrfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("burrfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("burrfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("burrfit: FREQ must not contain negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["burrfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Force censoring vector into logical notc = ! censor; cens = ! notc; ## Check for identical data in X if (! isscalar (x) && max (abs (diff (x)) ./ x(2:end)) <= sqrt (eps)) warning ("burrfit: X must not contain identical data."); ## Return some sensical values for estimated parameters lambda = x(1); c = Inf; k = sum (notc .* freq) / sum (freq) / log (2); paramhat = [lambda, c, k]; if (nargout > 1) paramci = [paramhat; paramhat]; endif return endif ## Fit a Pareto distribution [paramhat_prt, nlogL_prt] = prtfit (x, cens, freq); ## Fit a Weibull distribution paramhat_wbl = wblfit (x, alpha, cens, freq); nlogL_wbl = wbllike (paramhat_wbl, x, cens, freq); ## Calculate the discriminator x_lambda = x ./ paramhat_wbl(1); x_lambdk = x_lambda .^ paramhat_wbl(2); discrimi = sum (freq .* (0.5 * x_lambdk .^ 2 - x_lambdk .* notc)); ## Compute Burr distribution if (discrimi > 0) ## Expand data (if necessary) if (any (freq != 1)) ## Preserve class x_expand = zeros (1, sum (freq), class (x)); id0 = 1; for idx = 1:numel (x) x_expand(id0:id0 + freq(idx) - 1) = x_lambda(idx); id0 += freq(idx); endfor else x_expand = x_lambda; endif ## Calculate median and 3rd quartile to estimate LAMBDA and C parameters Q = prctile (x_expand); xl_median = Q(3); xl_upperq = Q(4); ## Avoid median and upper quartile being too close together IRDdist = sqrt (eps (xl_median)) * xl_median; if ((xl_upperq - xl_median) < IRDdist) if (any (x_lambda > xl_upperq)) xl_upperq = min (x_lambda(x_lambda > xl_upperq)); elseif (any (x_lambda < xl_upperq)) xl_median = max (x_lambda(x_lambda < xl_median)); endif endif ## Compute starting LAMBDA and C, either directly or by minimization if (xl_median >= xl_upperq / xl_median) l0 = xl_median; c0 = log(3)/log(xl_upperq/xl_median); else l0 = 1; opts = optimset ("fzero"); opts = optimset (opts, "Display", "off"); cmax = log(realmax)/(2*log(xl_upperq/xl_median)); c0 = fzero (@(c)(xl_upperq/xl_median).^c-xl_median.^c-2, [0, cmax], opts); endif ## Calculate starting K from other starting parameters and scaled data k0 = exp (compute_logk (x_lambda, l0, c0, censor, freq)); ## Estimate parameters by minimizing the negative log-likelihood function f = @(params) burrlike (params, x_lambda, censor, freq); [paramhat, ~, err, output] = fminsearch (f, [l0, c0, k0], options); ## Force positive parameter values paramhat = abs (paramhat); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning (strcat (["burrfit: maximum number of function"], ... [" evaluations are exceeded."])); elseif (output.iterations >= options.MaxIter) warning ("burrfit: maximum number of iterations are exceeded."); endif endif ## Scale back LAMBDA parameter paramhat(1) = paramhat(1) * paramhat_wbl(1); ## Compute negative log-likelihood with estimated parameters nlogL_burr = burrlike (paramhat, x, censor, freq); ## Check if fitting a Burr distribution is better than fitting a Pareto ## according to step 5 of the algorithmic implementation in Shao, 2004 if (paramhat(3) > 1e-6 && nlogL_burr < nlogL_prt) ## Compute CIs using a log normal approximation for phat. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = burrlike (paramhat, x, censor, freq); ## Get standard errors stderr = sqrt (diag (acov))'; stderr = stderr ./ paramhat; ## Apply log transform phatlog = log (paramhat); ## Compute normal quantiles z = norminv (alpha / 2); ## Compute CI paramci = [phatlog; phatlog] + ... [stderr; stderr] .* [z, z, z; -z, -z, -z]; ## Inverse log transform paramci = exp (paramci); endif else if (nlogL_prt < nlogL_wbl) error ("burrfit: Pareto distribution fits better in X."); else error ("burrfit: Weibull distribution fits better in X."); endif endif else if (nlogL_prt < nlogL_wbl) error ("burrfit: Pareto distribution fits better in X."); else error ("burrfit: Weibull distribution fits better in X."); endif endif endfunction ## Helper function for fitting a Pareto distribution function [paramhat, nlogL] = prtfit (x, censor, freq) ## Force censoring vector into logical notc = ! censor; cens = ! notc; ## Compute MLE for x_m xm = x(notc); xm = min (xm); ## Handle case with all data censored if (all (cens)) paramhat = [max(x), NaN]; nlogL_prt = 0; return endif ## Compute some values logx = log (x); suml = sum (freq .* (logx - log (xm)) .* (x > xm)); sumf = sum (freq .* notc); ## Compute MLE for alpha a = sumf ./ suml; ## Add MLEs to returning vector paramhat = [xm, a]; ## Compute negative log-likelihood nlogL = a .* suml + sum (freq .* notc .* logx) - log (a) .* sumf; endfunction ## Helper function for computing K from X, LAMBDA, and C function logk = compute_logk (x, lambda, c, censor, freq) ## Force censoring vector into logical notc = ! censor; cens = ! notc; ## Precalculate some values xl = x ./ lambda; l1_xlc = log1p (xl .^ c); ## Avoid realmax overflow by approximation is_inf = isinf (l1_xlc); l1_xlc(is_inf) = c .* log (xl(is_inf)); if (sum (freq .* l1_xlc) < eps) lsxc = log (sum (freq .* (x .^ c))); if (isinf (lsxc)) [maxx, idx] = max (x); lsxc = c * log (freq(idx) * maxx); endif logk = log (sum (freq .* notc)) + c * log (lambda) - lsxc; else logk = log (sum (freq .* notc)) - log (sum (freq .* l1_xlc)); endif endfunction %!demo %! ## Sample 3 populations from different Burr type XII distibutions %! rand ("seed", 4); # for reproducibility %! r1 = burrrnd (3.5, 2, 2.5, 10000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = burrrnd (1, 3, 1, 10000, 1); %! rand ("seed", 9); # for reproducibility %! r3 = burrrnd (0.5, 2, 3, 10000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.1:0.2:20], [18, 5, 3]); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 3]); %! xlim ([0, 5]); %! hold on %! %! ## Estimate their α and β parameters %! lambda_c_kA = burrfit (r(:,1)); %! lambda_c_kB = burrfit (r(:,2)); %! lambda_c_kC = burrfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0.01:0.15:15]; %! y = burrpdf (x, lambda_c_kA(1), lambda_c_kA(2), lambda_c_kA(3)); %! plot (x, y, "-pr"); %! y = burrpdf (x, lambda_c_kB(1), lambda_c_kB(2), lambda_c_kB(3)); %! plot (x, y, "-sg"); %! y = burrpdf (x, lambda_c_kC(1), lambda_c_kC(2), lambda_c_kC(3)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with λ=3.5, c=2, and k=2.5", ... %! "Normalized HIST of sample 2 with λ=1, c=3, and k=1", ... %! "Normalized HIST of sample 3 with λ=0.5, c=2, and k=3", ... %! sprintf("PDF for sample 1 with estimated λ=%0.2f, c=%0.2f, and k=%0.2f", ... %! lambda_c_kA(1), lambda_c_kA(2), lambda_c_kA(3)), ... %! sprintf("PDF for sample 2 with estimated λ=%0.2f, c=%0.2f, and k=%0.2f", ... %! lambda_c_kB(1), lambda_c_kB(2), lambda_c_kB(3)), ... %! sprintf("PDF for sample 3 with estimated λ=%0.2f, c=%0.2f, and k=%0.2f", ... %! lambda_c_kC(1), lambda_c_kC(2), lambda_c_kC(3))}) %! title ("Three population samples from different Burr type XII distibutions") %! hold off ## Test output %!test %! l = 1; c = 2; k = 3; %! r = burrrnd (l, c, k, 100000, 1); %! lambda_c_kA = burrfit (r); %! assert (lambda_c_kA(1), l, 0.2); %! assert (lambda_c_kA(2), c, 0.2); %! assert (lambda_c_kA(3), k, 0.3); %!test %! l = 0.5; c = 1; k = 3; %! r = burrrnd (l, c, k, 100000, 1); %! lambda_c_kA = burrfit (r); %! assert (lambda_c_kA(1), l, 0.2); %! assert (lambda_c_kA(2), c, 0.2); %! assert (lambda_c_kA(3), k, 0.3); %!test %! l = 1; c = 3; k = 1; %! r = burrrnd (l, c, k, 100000, 1); %! lambda_c_kA = burrfit (r); %! assert (lambda_c_kA(1), l, 0.2); %! assert (lambda_c_kA(2), c, 0.2); %! assert (lambda_c_kA(3), k, 0.3); %!test %! l = 3; c = 2; k = 1; %! r = burrrnd (l, c, k, 100000, 1); %! lambda_c_kA = burrfit (r); %! assert (lambda_c_kA(1), l, 0.2); %! assert (lambda_c_kA(2), c, 0.2); %! assert (lambda_c_kA(3), k, 0.3); %!test %! l = 4; c = 2; k = 4; %! r = burrrnd (l, c, k, 100000, 1); %! lambda_c_kA = burrfit (r); %! assert (lambda_c_kA(1), l, 0.2); %! assert (lambda_c_kA(2), c, 0.2); %! assert (lambda_c_kA(3), k, 0.3); ## Test input validation %!error burrfit (ones (2,5)); %!error burrfit ([-1 2 3 4]); %!error burrfit ([1, 2, 3, 4, 5], 1.2); %!error burrfit ([1, 2, 3, 4, 5], 0); %!error burrfit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! burrfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! burrfit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error %! burrfit ([1, 2, 3, 4, 5], 0.05, [], [1, 1, 5]) %!error %! burrfit ([1, 2, 3, 4, 5], 0.05, [], [1, 5, 1, 1, -1]) %!error ... %! burrfit ([1:10], 0.05, [], [], 5) statistics-release-1.7.3/inst/dist_fit/burrlike.m000066400000000000000000000153731475240274700221360ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} burrlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} burrlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} burrlike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} burrlike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the Burr type XII distribution. ## ## @code{@var{nlogL} = burrlike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Burr type XII ## distribution with (1) scale parameter @var{lambda}, (2) first shape parameter ## @var{c}, and (3) second shape parameter @var{k} given in the three-element ## vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = burrlike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. ## ## @code{[@dots{}] = burrlike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = burrlike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Burr type XII distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{burrcdf, burrinv, burrpdf, burrrnd, burrfit, burrstat} ## @end deftypefn function [nlogL, acov] = burrlike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("burrlike: function called with too few input arguments."); endif if (! isvector (x)) error ("burrlike: X must be a vector."); endif if (any (x < 0)) error ("burrlike: X cannot have negative values."); endif if (length (params) != 3) error ("burrlike: PARAMS must be a three-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("burrlike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("burrlike: X and FREQ vector mismatch."); endif ## Get parameters lambda = params(1); c = params(2); k = params(3); ## Precalculate some values xl = x ./ lambda; log_xl = log (xl); l1_xlc = log1p (xl .^ c); ## Avoid realmax overflow by approximation is_inf = isinf (l1_xlc); l1_xlc(is_inf) = c .* log (xl(is_inf)); ## Force censoring vector into logical notc = ! censor; cens = ! notc; ## Compute neg-loglikelihood likeL = zeros (size (x)); likeL(notc) = (c - 1) .* log_xl(notc) - (k + 1) .* l1_xlc(notc); likeL(cens) = -k .* l1_xlc(cens); nlogL = sum (freq (notc)) * log (lambda / k / c) - sum (freq .* likeL); ## Compute asymptotic covariance if (nargout > 1) ## Preallocate variables nH = zeros (3); d2V1 = zeros (size (x)); d2V2 = d2V1; ## Precalculate some more values xlc = xl .^ c; log_xl = log (xl); xlc1 = (1 + xlc); xlc1sq = xlc1.^2; invxlc1sq = (1 + 1./xlc).^2; ## Find realmax overflow is_inf = isinf (xlc); is_fin = ! is_inf; ## Compute each element of the negative Hessian d2V1(is_fin) = -((1 + c) ./ xlc(is_fin) + 1) ./ invxlc1sq(is_fin); d2V1(is_inf) = -1; d2V2(notc) = d2V1(notc) .* (k + 1) + 1; d2V2(cens) = d2V1(cens) .* k; nH(1,1) = c ./ lambda .^ 2 .* sum (freq .* d2V2); d2V1(is_fin) = xlc(is_fin) .* (c .* log_xl(is_fin) + xlc1(is_fin)) ... ./ xlc1sq(is_fin); d2V1(is_inf) = 1; d2V2(notc) = (k + 1) .* d2V1(notc) - 1; d2V2(cens) = k .* d2V1(cens); nH(1,2) = sum (freq ./ lambda .* d2V2); nH(2,1) = nH(1,2); d2V1(is_fin) = xlc(is_fin) .* log_xl(is_fin) .^ 2 ./ xlc1sq(is_fin); d2V1(is_inf) = 0; d2V2(notc) = d2V1(notc) .* k + d2V1(notc); d2V2(cens) = d2V1(cens) .* k; nH(2,2) = -(sum (freq (notc))) ./ c .^ 2 - sum (freq .* d2V2); d2V1(is_fin) = xlc(is_fin) ./ xlc1(is_fin); d2V1(is_inf) = 1; nH(1,3) = (c ./ lambda) .* sum (freq .* d2V1); nH(3,1) = nH(1,3); nH(2,3) = -sum (freq .* d2V1 .* log_xl); nH(3,2) = nH(2,3); nH(3,3) = -(sum (freq (notc))) ./ k .^ 2; nH = -nH; ## Check negative Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("burrlike: non positive definite Hessian matrix."); acov = NaN (3); return endif ## ACOV estimate is the negative inverse of the Hessian. Rinv = inv (R); acov = Rinv * Rinv; endif endfunction ## Test output ## Test input validation %!error burrlike (3.25) %!error burrlike ([1, 2, 3], ones (2)) %!error burrlike ([1, 2, 3], [-1, 3]) %!error ... %! burrlike ([1, 2], [1, 3, 5, 7]) %!error ... %! burrlike ([1, 2, 3, 4], [1, 3, 5, 7]) %!error ... %! burrlike ([1, 2, 3], [1:5], [0, 0, 0]) %!error ... %! burrlike ([1, 2, 3], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! burrlike ([1, 2, 3], [1:5], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/evfit.m000066400000000000000000000337301475240274700214310ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## Copyright (C) 2022 Andrew Penn ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} evfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} evfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} evfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} evfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} evfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} evfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the extreme value distribution. ## ## @code{@var{paramhat} = evfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the extreme value distribution (also known as ## the Gumbel or the type I generalized extreme value distribution) given the ## data in @var{x}. @qcode{@var{paramhat}(1)} is the location parameter, ## @var{mu}, and @qcode{@var{paramhat}(2)} is the scale parameter, @var{sigma}. ## ## @code{[@var{paramhat}, @var{paramci}] = evfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = evfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = evfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = evfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = evfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute the maximum likelihood ## estimates. @var{options} is a structure with the following field and its ## default value: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling minima. For modeling maxima, use the alternative ## Gumbel fitting function, @code{gumbelfit}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evcdf, evinv, evpdf, evrnd, evlike, evstat, gumbelfit} ## @end deftypefn function [paramhat, paramci] = evfit (x, alpha, censor, freq, options) ## Check X for being a double precision vector if (! isvector (x) || ! isa (x, "double")) error ("evfit: X must be a double-precision vector."); endif ## Check that X does not contain missing values (NaNs) if (any (isnan (x))) error ("evfit: X must NOT contain missing values (NaNs)."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("evfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("evfit: X and CENSOR vectors mismatch."); endif ## Parse FREQ argument or add default if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("evfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("evfit: FREQ must not contain negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["evfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Remove zeros and NaNs from frequency vector (if necessary) if (! all (freq == 1)) remove = freq == 0 | isnan (freq); x(remove) = []; censor(remove) = []; freq(remove) = []; endif ## If X is a column vector, make X, CENSOR, and FREQ row vectors if (size (x, 1) > 1) x = x(:)'; censor = censor(:)'; freq = freq(:)'; endif ## Censor x and get number of samples sample_size = sum (freq); censored_sample_size = sum (freq .* censor); uncensored_sample_size = sample_size - censored_sample_size; x_range = range (x); x_max = max (x); ## Check cases that cannot make a fit. ## 1. All observations are censored if (sample_size == 0 || uncensored_sample_size == 0 || ! isfinite (x_range)) paramhat = NaN (1, 2); paramci = NaN (2, 2); return endif ## 2. Constant x in X if (censored_sample_size == 0 && x_range == 0) paramhat = [x(1), 0]; if (sample_size == 1) paramci = [-Inf, 0; Inf, Inf]; else paramci = [paramhat, paramhat]; endif return elseif (censored_sample_size == 0 && x_range != 0) ## Data can fit, so preprocess them to make likelihood eqn more stable. ## Shift x to max(x) == 0, min(x) = -1. x_0 = (x - x_max) ./ x_range; ## Get a rough initial estimate for scale parameter initial_sigma_parm = (sqrt (6) * std (x_0)) / pi; uncensored_weights = sum (freq .* x_0) ./ sample_size; endif ## 3. All uncensored observations are equal and greater than all censored ones uncensored_x_range = range (x(censor == 0)); uncensored_x = x(censor == 0); if (censored_sample_size > 0 && uncensored_x_range == 0 ... && uncensored_x(1) >= x_max) paramhat = [uncensored_x(1), 0]; if uncensored_sample_size == 1 paramci = [-Inf, 0; Inf, Inf]; else paramci = [paramhat; paramhat]; end return else ## Data can fit, so preprocess them to make likelihood eqn more stable. ## Shift x to max(x) == 0, min(x) = -1. x_0 = (x - x_max) ./ x_range; ## Get a rough initial estimate for scale parameter if (uncensored_x_range > 0) [F_y, y] = ecdf (x_0, "censoring", censor', "frequency", freq'); pmid = (F_y(1:(end-1)) + F_y(2:end)) / 2; linefit = polyfit (log (- log (1 - pmid)), y(2:end), 1); initial_sigma_parm = linefit(1); else initial_sigma_parm = 1; endif uncensored_weights = sum (freq .* x_0 .* (1 - censor)) ./ ... uncensored_sample_size; endif ## Find lower and upper boundaries for bracketing the likelihood equation for ## the extreme value scale parameter if (evscale_lkeq (initial_sigma_parm, x_0, freq, uncensored_weights) > 0) upper = initial_sigma_parm; lower = 0.5 * upper; while (evscale_lkeq (lower, x_0, freq, uncensored_weights) > 0) upper = lower; lower = 0.5 * upper; if (lower <= realmin ("double")) error ("evfit: no solution for maximum likelihood estimates."); endif endwhile boundaries = [lower, upper]; else lower = initial_sigma_parm; upper = 2 * lower; while (evscale_lkeq (upper, x_0, freq, uncensored_weights) < 0) lower = upper; upper = 2 * lower; if (upper > realmax ("double")) error ("evfit: no solution for maximum likelihood estimates."); endif endwhile boundaries = [lower, upper]; endif ## Compute maximum likelihood for scale parameter as the root of the equation ## Custom code for finding the value within the boundaries [lower, upper] that ## evscale_lkeq function returns zero ## First check that there is a root within the boundaries new_lo = boundaries(1); new_up = boundaries(2); v_lower = evscale_lkeq (new_lo, x_0, freq, uncensored_weights); v_upper = evscale_lkeq (new_up, x_0, freq, uncensored_weights); if (! (sign (v_lower) * sign (v_upper) <= 0)) error ("evfit: no solution for maximum likelihood estimates."); endif ## Get a value at mid boundary range old_sigma = new_lo; new_sigma = (new_lo + new_up) / 2; new_fzero = evscale_lkeq (new_sigma, x_0, freq, uncensored_weights); ## Start searching cur_iter = 0; max_iter = 1e+3; while (cur_iter < max_iter && abs (old_sigma - new_sigma) > options.TolX) cur_iter++; if (new_fzero < 0) old_sigma = new_sigma; new_lo = new_sigma; new_sigma = (new_lo + new_up) / 2; new_fzero = evscale_lkeq (new_sigma, x_0, freq, uncensored_weights); else old_sigma = new_sigma; new_up = new_sigma; new_sigma = (new_lo + new_up) / 2; new_fzero = evscale_lkeq (new_sigma, x_0, freq, uncensored_weights); endif endwhile ## Check for maximum number of iterations if (cur_iter == max_iter) warning (strcat (["evfit: maximum number of function "], ... [" evaluations (1e+4) has been reached."])); endif ## Compute MU muhat = new_sigma .* log (sum (freq .* exp (x_0 ./ new_sigma)) ./ ... uncensored_sample_size); ## Transform MU and SIGMA back to original location and scale paramhat = [(x_range*muhat)+x_max, x_range*new_sigma]; ## Compute the CI for MU and SIGMA if (nargout == 2) probs = [alpha/2; 1-alpha/2]; [~, acov] = evlike (paramhat, x, censor, freq); transfhat = [paramhat(1), log(paramhat(2))]; se = sqrt (diag (acov))'; se(2) = se(2) ./ paramhat(2); paramci = norminv ([probs, probs], [transfhat; transfhat], [se; se]); paramci(:,2) = exp (paramci(:,2)); endif endfunction ## Likelihood equation for the extreme value scale parameter. function v = evscale_lkeq (sigma, x, freq, x_weighted_uncensored) freq = freq .* exp (x ./ sigma); v = sigma + x_weighted_uncensored - sum (x .* freq) / sum (freq); endfunction %!demo %! ## Sample 3 populations from different extreme value distibutions %! rand ("seed", 1); # for reproducibility %! r1 = evrnd (2, 5, 400, 1); %! rand ("seed", 12); # for reproducibility %! r2 = evrnd (-5, 3, 400, 1); %! rand ("seed", 13); # for reproducibility %! r3 = evrnd (14, 8, 400, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 25, 0.4); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 0.28]) %! xlim ([-30, 30]); %! hold on %! %! ## Estimate their MU and SIGMA parameters %! mu_sigmaA = evfit (r(:,1)); %! mu_sigmaB = evfit (r(:,2)); %! mu_sigmaC = evfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [min(r(:)):max(r(:))]; %! y = evpdf (x, mu_sigmaA(1), mu_sigmaA(2)); %! plot (x, y, "-pr"); %! y = evpdf (x, mu_sigmaB(1), mu_sigmaB(2)); %! plot (x, y, "-sg"); %! y = evpdf (x, mu_sigmaC(1), mu_sigmaC(2)); %! plot (x, y, "-^c"); %! legend ({"Normalized HIST of sample 1 with μ=2 and σ=5", ... %! "Normalized HIST of sample 2 with μ=-5 and σ=3", ... %! "Normalized HIST of sample 3 with μ=14 and σ=8", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f and σ=%0.2f", ... %! mu_sigmaA(1), mu_sigmaA(2)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f and σ=%0.2f", ... %! mu_sigmaB(1), mu_sigmaB(2)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f and σ=%0.2f", ... %! mu_sigmaC(1), mu_sigmaC(2))}) %! title ("Three population samples from different extreme value distibutions") %! hold off ## Test output %!test %! x = 1:50; %! [paramhat, paramci] = evfit (x); %! paramhat_out = [32.6811, 13.0509]; %! paramci_out = [28.8504, 10.5294; 36.5118, 16.1763]; %! assert (paramhat, paramhat_out, 1e-4); %! assert (paramci, paramci_out, 1e-4); %!test %! x = 1:50; %! [paramhat, paramci] = evfit (x, 0.01); %! paramci_out = [27.6468, 9.8426; 37.7155, 17.3051]; %! assert (paramci, paramci_out, 1e-4); ## Test input validation %!error evfit (ones (2,5)); %!error evfit (single (ones (1,5))); %!error evfit ([1, 2, 3, 4, NaN]); %!error evfit ([1, 2, 3, 4, 5], 1.2); %!error %! evfit ([1 2 3], 0.05, [], [1 5]) %!error %! evfit ([1 2 3], 0.05, [], [1 5 -1]) %!error ... %! evfit ([1:10], 0.05, [], [], 5) statistics-release-1.7.3/inst/dist_fit/evlike.m000066400000000000000000000135761475240274700216010ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} evlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} evlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} evlike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} evlike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the extreme value distribution. ## ## @code{@var{nlogL} = evlike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the extreme value ## distribution (also known as the Gumbel or the type I generalized extreme ## value distribution) with (1) location parameter @var{mu} and (2) scale ## parameter @var{sigma} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = evlike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. ## ## @code{[@dots{}] = evlike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = evlike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling minima. For modeling maxima, use the alternative ## Gumbel likelihood function, @code{gumbellike}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evcdf, evinv, evpdf, evrnd, evfit, evstat, gumbellike} ## @end deftypefn function [nlogL, acov] = evlike (params, x, censor, freq) ## Check input arguments and add defaults if (nargin < 2) error ("evlike: function called with too few input arguments."); endif if (numel (params) != 2) error ("evlike: wrong parameters length."); endif if (! isvector (x)) error ("evlike: X must be a vector."); endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("evlike: X and CENSOR vectors mismatch."); endif if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("evlike: X and FREQ vectors mismatch."); endif ## Get mu and sigma values mu = params(1); sigma = params(2); ## sigma must be positive, otherwise make it NaN if (sigma <= 0) sigma = NaN; endif ## Compute the individual log-likelihood terms z = (x - mu) ./ sigma; expz = exp (z); L = (z - log (sigma)) .* (1 - censor) - expz; ## Force a log(0)==-Inf for X from extreme right tail L(z == Inf) = -Inf; ## Neg-log-likelihood is the sum of the individual contributions nlogL = -sum (freq .* L); ## Compute the negative hessian at the parameter values. ## Invert to get the observed information matrix. if (nargout == 2) unc = (1 - censor); nH11 = sum(freq .* expz); nH12 = sum(freq .* ((z + 1) .* expz - unc)); nH22 = sum(freq .* (z .* (z+2) .* expz - ((2 .* z + 1) .* unc))); acov = (sigma .^ 2) * ... [nH22 -nH12; -nH12 nH11] / (nH11 * nH22 - nH12 * nH12); endif endfunction ## Test output %!test %! x = 1:50; %! [nlogL, acov] = evlike ([2.3, 1.2], x); %! avar_out = [-1.2778e-13, 3.1859e-15; 3.1859e-15, -7.9430e-17]; %! assert (nlogL, 3.242264755689906e+17, 1e-14); %! assert (acov, avar_out, 1e-3); %!test %! x = 1:50; %! [nlogL, acov] = evlike ([2.3, 1.2], x * 0.5); %! avar_out = [-7.6094e-05, 3.9819e-06; 3.9819e-06, -2.0836e-07]; %! assert (nlogL, 481898704.0472211, 1e-6); %! assert (acov, avar_out, 1e-3); %!test %! x = 1:50; %! [nlogL, acov] = evlike ([21, 15], x); %! avar_out = [11.73913876598908, -5.9546128523121216; ... %! -5.954612852312121, 3.708060045170236]; %! assert (nlogL, 223.7612479380652, 1e-13); %! assert (acov, avar_out, 1e-14); ## Test input validation %!error evlike ([12, 15]) %!error evlike ([12, 15, 3], [1:50]) %!error evlike ([12, 3], ones (10, 2)) %!error ... %! evlike ([12, 15], [1:50], [1, 2, 3]) %!error ... %! evlike ([12, 15], [1:50], [], [1, 2, 3]) statistics-release-1.7.3/inst/dist_fit/expfit.m000066400000000000000000000315151475240274700216120ustar00rootroot00000000000000## Copyright (C) 2021 Nicholas R. Jankowski ## Copyright (C) 2023 Andreas Bertsatos ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{muhat} =} expfit (@var{x}) ## @deftypefnx {statistics} {[@var{muhat}, @var{muci}] =} expfit (@var{x}) ## @deftypefnx {statistics} {[@var{muhat}, @var{muci}] =} expfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} expfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} expfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## ## Estimate mean and confidence intervals for the exponential distribution. ## ## @code{@var{muhat} = expfit (@var{x})} returns the maximum likelihood estimate ## of the mean parameter, @var{muhat}, of the exponential distribution given the ## data in @var{x}. @var{x} is expected to be a non-negative vector. If @var{x} ## is an array, the mean will be computed for each column of @var{x}. If any ## elements of @var{x} are NaN, that vector's mean will be returned as NaN. ## ## @code{[@var{muhat}, @var{muci}] = expfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimate. If @var{x} is a vector, ## @var{muci} is a two element column vector. If @var{x} is an array, each ## column of data will have a confidence interval returned as a two-row array. ## ## @code{[@dots{}] = evfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. Any invalid values for @var{alpha} ## will return NaN for both CI bounds. ## ## @code{[@dots{}] = expfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## logical or numeric array, @var{censor}, of the same size as @var{x} with ## @qcode{1}s for observations that are right-censored and @qcode{0}s for ## observations that are observed exactly. Any non-zero elements are regarded ## as @qcode{1}s. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = expfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency array, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Matlab incompatibility: Matlab's @code{expfit} produces unpredictable results ## for some cases with higher dimensions (specifically 1 x m x n x ... arrays). ## Octave's implementation allows for @math{nxD} arrays, consistently performing ## calculations on individual column vectors. Additionally, @var{censor} and ## @var{freq} can be used with arrays of any size, whereas Matlab only allows ## their use when @var{x} is a vector. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expcdf, expinv, explpdf, exprnd, explike, expstat} ## @end deftypefn function [muhat, muci] = expfit (x, alpha = 0.05, censor = [], freq = []) ## Check arguments if (nargin == 0 || nargin > 4 || nargout > 2) print_usage (); endif if (! (isnumeric (x) || islogical (x))) x = double (x); endif ## Guarantee working with column vectors if (isvector (x)) x = x(:); endif if (any (x(:) < 0)) error ("expfit: X cannot be negative."); endif sz_s = size (x); if (isempty (alpha)) alpha = 0.05; elseif (! (isscalar (alpha))) error ("expfit: ALPHA must be a scalar quantity."); endif if (isempty (censor) && isempty (freq)) ## Simple case without freq or censor, shortcut other validations muhat = mean (x, 1); if (nargout == 2) X = sum (x, 1); muci = [2*X./chi2inv(1 - alpha / 2, 2 * sz_s(1));... 2*X./chi2inv(alpha / 2, 2 * sz_s(1))]; endif else ## Input validation for censor and freq if (isempty (censor)) ## Expand to full censor with values that don't affect results censor = zeros (sz_s); elseif (! (isnumeric (censor) || islogical (censor))) ## Check for incorrect freq type error ("expfit: CENSOR must be a numeric or logical array.") elseif (isvector (censor)) ## Guarantee working with a column vector censor = censor(:); endif if (isempty (freq)) ## Expand to full censor with values that don't affect results freq = ones (sz_s); elseif (! (isnumeric (freq) || islogical (freq))) ## Check for incorrect freq type error ("expfit: FREQ must be a numeric or logical array.") elseif (isvector (freq)) ## Guarantee working with a column vector freq = freq(:); endif ## Check that size of censor and freq match x if (! (isequal (size (censor), sz_s))) error("expfit: X and CENSOR vectors mismatch."); elseif (! isequal (size (freq), sz_s)) error("expfit: X and FREQ vectors mismatch."); endif ## Trivial case where censor and freq have no effect if (all (censor(:) == 0 & freq(:) == 1)) muhat = mean (x, 1); if (nargout == 2) X = sum (x, 1); muci = [2*X./chi2inv(1 - alpha / 2, 2 * sz_s(1));... 2*X./chi2inv(alpha / 2, 2 * sz_s(1))]; endif ## No censoring, just adjust sample counts for freq elseif (all (censor(:) == 0)) X = sum (x.*freq, 1); n = sum (freq, 1); muhat = X ./ n; if (nargout == 2) muci = [2*X./chi2inv(1 - alpha / 2, 2 * n);... 2*X./chi2inv(alpha / 2, 2 * n)]; endif ## Censoring, but no sample counts adjustment elseif (all (freq(:) == 1)) censor = logical(censor); # convert any numeric censor'x to 0s and 1s X = sum (x, 1); r = sz_s(1) - sum (censor, 1); muhat = X ./ r; if (nargout == 2) muci = [2*X./chi2inv(1 - alpha / 2, 2 * r);... 2*X./chi2inv(alpha / 2, 2 * r)]; endif ## Both censoring and sample count adjustment else censor = logical (censor); # convert any numeric censor'x to 0s and 1s X = sum (x .* freq , 1); r = sum (freq .* (! censor), 1); muhat = X ./ r; if (nargout == 2) muci = [2*X./chi2inv(1 - alpha / 2, 2 * r);... 2*X./chi2inv(alpha / 2, 2 * r)]; endif endif ## compatibility check, NaN for columns where all censor's or freq's remove ## all samples null_columns = all (censor) | ! all (freq); muhat(null_columns) = NaN; if (nargout == 2) muci(:,null_columns) = NaN; endif endif endfunction %!demo %! ## Sample 3 populations from 3 different exponential distibutions %! rande ("seed", 1); # for reproducibility %! r1 = exprnd (2, 4000, 1); %! rande ("seed", 2); # for reproducibility %! r2 = exprnd (5, 4000, 1); %! rande ("seed", 3); # for reproducibility %! r3 = exprnd (12, 4000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 48, 0.52); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! hold on %! %! ## Estimate their mu parameter %! muhat = expfit (r); %! %! ## Plot their estimated PDFs %! x = [0:max(r(:))]; %! y = exppdf (x, muhat(1)); %! plot (x, y, "-pr"); %! y = exppdf (x, muhat(2)); %! plot (x, y, "-sg"); %! y = exppdf (x, muhat(3)); %! plot (x, y, "-^c"); %! ylim ([0, 0.6]) %! xlim ([0, 40]) %! legend ({"Normalized HIST of sample 1 with μ=2", ... %! "Normalized HIST of sample 2 with μ=5", ... %! "Normalized HIST of sample 3 with μ=12", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f", muhat(1)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f", muhat(2)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f", muhat(3))}) %! title ("Three population samples from different exponential distibutions") %! hold off ## Tests for mean %!assert (expfit (1), 1) %!assert (expfit (1:3), 2) %!assert (expfit ([1:3]'), 2) %!assert (expfit (1:3, []), 2) %!assert (expfit (1:3, [], [], []), 2) %!assert (expfit (magic (3)), [5 5 5]) %!assert (expfit (cat (3, magic (3), 2*magic (3))), cat (3,[5 5 5], [10 10 10])) %!assert (expfit (1:3, 0.1, [0 0 0], [1 1 1]), 2) %!assert (expfit ([1:3]', 0.1, [0 0 0]', [1 1 1]'), 2) %!assert (expfit (1:3, 0.1, [0 0 0]', [1 1 1]'), 2) %!assert (expfit (1:3, 0.1, [1 0 0], [1 1 1]), 3) %!assert (expfit (1:3, 0.1, [0 0 0], [4 1 1]), 1.5) %!assert (expfit (1:3, 0.1, [1 0 0], [4 1 1]), 4.5) %!assert (expfit (1:3, 0.1, [1 0 1], [4 1 1]), 9) %!assert (expfit (1:3, 0.1, [], [-1 1 1]), 4) %!assert (expfit (1:3, 0.1, [], [0.5 1 1]), 2.2) %!assert (expfit (1:3, 0.1, [1 1 1]), NaN) %!assert (expfit (1:3, 0.1, [], [0 0 0]), NaN) %!assert (expfit (reshape (1:9, [3 3])), [2 5 8]) %!assert (expfit (reshape (1:9, [3 3]), [], eye(3)), [3 7.5 12]) %!assert (expfit (reshape (1:9, [3 3]), [], 2*eye(3)), [3 7.5 12]) %!assert (expfit (reshape (1:9, [3 3]), [], [], [2 2 2; 1 1 1; 1 1 1]), ... %! [1.75 4.75 7.75]) %!assert (expfit (reshape (1:9, [3 3]), [], [], [2 2 2; 1 1 1; 1 1 1]), ... %! [1.75 4.75 7.75]) %!assert (expfit (reshape (1:9, [3 3]), [], eye(3), [2 2 2; 1 1 1; 1 1 1]), ... %! [3.5 19/3 31/3]) ## Tests for confidence intervals %!assert ([~,muci] = expfit (1:3, 0), [0; Inf]) %!assert ([~,muci] = expfit (1:3, 2), [Inf; 0]) %!assert ([~,muci] = expfit (1:3, 0.1, [1 1 1]), [NaN; NaN]) %!assert ([~,muci] = expfit (1:3, 0.1, [], [0 0 0]), [NaN; NaN]) %!assert ([~,muci] = expfit (1:3, -1), [NaN; NaN]) %!assert ([~,muci] = expfit (1:3, 5), [NaN; NaN]) #!assert ([~,muci] = expfit ([1:3;1:3], -1), NaN(2, 3)] #!assert ([~,muci] = expfit ([1:3;1:3], 5), NaN(2, 3)] %!assert ([~,muci] = expfit (1:3), [0.830485728373393; 9.698190330474096], ... %! 1000*eps) %!assert ([~,muci] = expfit (1:3, 0.1), ... %! [0.953017262058213; 7.337731146400207], 1000*eps) %!assert ([~,muci] = expfit ([1:3;2:4]), ... %! [0.538440777613095, 0.897401296021825, 1.256361814430554; ... %! 12.385982973214016, 20.643304955356694, 28.900626937499371], ... %! 1000*eps) %!assert ([~,muci] = expfit ([1:3;2:4], [], [1 1 1; 0 0 0]), ... %! 100*[0.008132550920455, 0.013554251534091, 0.018975952147727; ... %! 1.184936706156216, 1.974894510260360, 2.764852314364504], ... %! 1000*eps) %!assert ([~,muci] = expfit ([1:3;2:4], [], [], [3 3 3; 1 1 1]), ... %! [0.570302756652583, 1.026544961974649, 1.482787167296715; ... %! 4.587722594914109, 8.257900670845396, 11.928078746776684], ... %! 1000*eps) %!assert ([~,muci] = expfit ([1:3;2:4], [], [0 0 0; 1 1 1], [3 3 3; 1 1 1]), ... %! [0.692071440311161, 1.245728592560089, 1.799385744809018; ... %! 8.081825275395081, 14.547285495711145, 21.012745716027212], ... %! 1000*eps) %!test %! x = reshape (1:8, [4 2]); %! x(4) = NaN; %! [muhat,muci] = expfit (x); %! assert ({muhat, muci}, {[NaN, 6.5], ... %! [NaN, 2.965574334593430;NaN, 23.856157493553368]}, 1000*eps); %!test %! x = magic (3); %! censor = [0 1 0; 0 1 0; 0 1 0]; %! freq = [1 1 0; 1 1 0; 1 1 0]; %! [muhat,muci] = expfit (x, [], censor, freq); %! assert ({muhat, muci}, {[5 NaN NaN], ... %! [[2.076214320933482; 24.245475826185242],NaN(2)]}, 1000*eps); ## Test input validation %!error expfit () %!error expfit (1,2,3,4,5) %!error [a b censor] = expfit (1) %!error expfit (1, [1 2]) %!error expfit ([-1 2 3 4 5]) %!error expfit ([1:5], [], "test") %!error expfit ([1:5], [], [], "test") %!error expfit ([1:5], [], [0 0 0 0]) %!error expfit ([1:5], [], [], [1 1 1 1]) statistics-release-1.7.3/inst/dist_fit/explike.m000066400000000000000000000123301475240274700217460ustar00rootroot00000000000000## Copyright (C) 2021 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} explike (@var{mu}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} explike (@var{mu}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} explike (@var{mu}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} explike (@var{mu}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the exponential distribution. ## ## @code{@var{nlogL} = explike (@var{mu}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the exponential ## distribution with mean parameter @var{mu}. @var{x} must be a vector of ## non-negative values, otherwise @qcode{NaN} is returned. ## ## @code{[@var{nlogL}, @var{avar}] = explike (@var{mu}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{avar}. If the input ## mean parameter, @var{mu}, is the maximum likelihood estimate, @var{avar} is ## its asymptotic variance. ## ## @code{[@dots{}] = explike (@var{mu}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = explike (@var{mu}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expcdf, expinv, exppdf, exprnd, expfit, expstat} ## @end deftypefn function [nlogL, avar] = explike (mu, x, censor, freq) ## Check input arguments if (nargin < 2) error ("explike: function called with too few input arguments."); endif if (! isvector (x)) error ("explike: X must be a vector."); endif if (numel (mu) != 1) error ("explike: MU must be a scalar."); endif ## Return NaNs for non-positive MU or negative values in X if (mu <= 0 || any (x(:) < 0)) nlogL = NaN; if (nargout > 1) avar = NaN; endif return endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("explike: X and CENSOR vectors mismatch."); endif if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("explike: X and FREQ vectors mismatch."); endif ## Start processing numx = numel (x); sumz = sum (x .* freq) / mu; numc = numx - sum (freq .* censor); ## Calculate negative log likelihood nlogL = sumz + numc * log (mu); ## Optionally calculate the inverse (reciprocal) of the second derivative ## of the negative log likelihood with respect to parameter if (nargout > 1) avar = (mu ^ 2) ./ (2 * sumz - numc); endif endfunction %!test %! x = 12; %! beta = 5; %! [L, V] = explike (beta, x); %! expected_L = 4.0094; %! expected_V = 6.5789; %! assert (L, expected_L, 0.001); %! assert (V, expected_V, 0.001); %!test %! x = 1:5; %! beta = 2; %! [L, V] = explike (beta, x); %! expected_L = 10.9657; %! expected_V = 0.4; %! assert (L, expected_L, 0.001); %! assert (V, expected_V, 0.001); ## Test input validation %!error explike () %!error explike (2) %!error explike ([12, 3], [1:50]) %!error explike (3, ones (10, 2)) %!error ... %! explike (3, [1:50], [1, 2, 3]) %!error ... %! explike (3, [1:50], [], [1, 2, 3]) statistics-release-1.7.3/inst/dist_fit/gamfit.m000066400000000000000000000401501475240274700215550ustar00rootroot00000000000000## Copyright (C) 2019 Nir Krakauer ## Copyright (C) 2023-2024 Andreas Bertsatos ## Based on previous work by Martijn van Oosterhout ## originally granted to the public domain. ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} gamfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gamfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gamfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} gamfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} gamfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} gamfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the Gamma distribution. ## ## @code{@var{paramhat} = gamfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Gamma distribution given the data in ## @var{x}. @qcode{@var{paramhat}(1)} is the shape parameter, @var{a}, and ## @qcode{@var{paramhat}(2)} is the scale parameter, @var{b}. ## ## @code{[@var{paramhat}, @var{paramci}] = gamfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = gamfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = gamfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = gamfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = gamfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute the maximum likelihood ## estimates. @var{options} is a structure with the following field and its ## default value: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gamcdf, gampdf, gaminv, gamrnd, gamlike} ## @end deftypefn function [paramhat, paramci] = gamfit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("gamfit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("gamfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("gamfit: X and CENSOR vectors mismatch."); endif ## Parse FREQ argument or add default if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("gamfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("gamfit: FREQ must not contain negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["gamfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Remove zeros and NaNs from frequency vector (if necessary) if (! all (freq == 1)) remove = freq == 0 | isnan (freq); x(remove) = []; censor(remove) = []; freq(remove) = []; endif ## Get sample size and data type cls = class (x); szx = sum (freq); ncen = sum (freq .* censor); nunc = szx - ncen; ## Check for illegal value in X if (ncen == 0 && any (x < 0)) error ("gamfit: X cannot contain negative values."); endif if (ncen > 0 && any (x <= 0)) error ("gamfit: X must contain positive values when censored."); endif ## Handle ill-conditioned cases: no data or all censored if (szx == 0 || nunc == 0 || any (! isfinite (x))) paramhat = nan (1, 2, cls); paramci = nan (2, cls); return endif ## Check for identical data in X if (! isscalar (x) && max (abs (diff (x)) ./ x(2:end)) <= sqrt (eps)) paramhat = cast ([Inf, 0], cls); paramci = cast ([Inf, 0; Inf, 0], cls); return endif ## When CENSOR and FREQ are default if (all (censor == 0) && all (freq == 1)) ## Optimize with respect to log(a), since both A and B must be positive meanx = mean (x); x0 = 0; ## Minimize negative log-likelihood to estimate parameters f = @(loga) gamfit_search (loga, meanx, x); [loga, ~, err, output] = fminsearch (f, x0, options); ## Inverse log(a) a = exp (loga); b = meanx / a; paramhat = [a, b]; ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning (strcat (["gamfit: maximum number of function"], ... [" evaluations are exceeded."])); elseif (output.iterations >= options.MaxIter) warning ("gamfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("gamfit: NoSolution."); endif endif ## No censoring if (all (censor == 0)) ## Scale data to allow parameter estimation ## for extremely large or small values scale = sum (freq .* x) / szx; ## Check for all data being ~zero if (scale < realmin (cls)) paramhat = cast ([NaN, 0], cls); paramci = cast ([NaN, 0; NaN, 0], cls); return endif scaledx = x / scale; ## Use Method of Moments for initial estimates meansqx = sum (freq .* (scaledx - 1) .^ 2) / szx; b = meansqx * szx / (szx - 1); a = 1 / b; ## Ensure that MLEs is possible, otherwise return initial estimates if (any (scaledx == 0)) paramhat = [a, b*scale]; paramci = nan (2, cls); warning ("gamfit: X contains zeros."); return ## Compute MLEs else ## Bracket the root of the scale parameter likelihood equation sumlogx = sum (freq .* log (scaledx)); bracket = sumlogx / szx; if (lkeqn (a, bracket) > 0) upper = a; lower = 0.5 * upper; while (lkeqn (lower, bracket) > 0) upper = lower; lower = 0.5 * upper; if (lower < realmin (cls)) error ("gamfit: no solution"); endif endwhile else lower = a; upper = 2 * lower; while (lkeqn (upper, bracket) < 0) lower = upper; upper = 2 * lower; if (upper > realmax (cls)) error ("gamfit: no solution"); endif endwhile endif bounds = [lower upper]; ## Find the root of the likelihood equation. opts = optimset ("fzero"); opts = optimset (opts, "Display", "off"); f = @(a) lkeqn (a, bracket); [a, lkeqnval, err] = fzero (f, bounds, opts); ## Rescale B paramhat = [a, (1/a)*scale]; endif ## With censoring else ## Get uncensored data notc = ! censor; xunc = x(notc); freq_notc = freq(notc); ## Ensure that MLEs is possible and get initial estimates xuncbar = sum (freq_notc .* xunc) / nunc; s2unc = sum (freq_notc .* (xunc - xuncbar) .^ 2) / nunc; if s2unc <= 100.*eps(xuncbar.^2) ## When all uncensored observations are equal and greater than all ## the censored observations, the likelihood surface becomes infinite if (max (xunc) == max (x)) paramhat = cast ([Inf, 0], cls); if (nunc > 1) paramci = cast ([Inf, 0; Inf, 0], cls); else paramci = cast ([0, 0; Inf, Inf], cls); endif return endif ## Set some default parameter estimates. x0 = [2, xuncbar./2]; else ## Fit a Weibull distribution and equate the parameter estimates ## into a Gamma distribution wblphat = wblfit (x, alpha, censor, freq); [m, v] = wblstat (wblphat(1), wblphat(2)); x0 = [m.*m./v, v./m]; endif ## Minimize negative log-likelihood to estimate parameters f = @(params) gamlike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, x0, options); ## Force positive parameter values paramhat = abs (paramhat); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning (strcat (["gamfit: maximum number of function"], ... [" evaluations are exceeded."])); elseif (output.iterations >= options.MaxIter) warning ("gamfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("gamfit: no solution."); endif endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = gamlike (paramhat, x, censor, freq); ## Get standard errors stderr = sqrt (diag (acov))'; stderr = stderr ./ paramhat; ## Apply log transform phatlog = log (paramhat); ## Compute normal quantiles z = probit (alpha / 2); ## Compute CI paramci = [phatlog; phatlog] + [stderr; stderr] .* [z, z; -z, -z]; ## Inverse log transform paramci = exp (paramci); endif endfunction ## Helper function so we only have to minimize for one variable. function nlogL = gamfit_search (loga, meanx, x) a = exp (loga); b = meanx / a; nlogL = gamlike ([a, b], x); endfunction ## Helper function for MLE with no censoring function v = lkeqn (a, bracket) v = -bracket - log (a) + psi (a); endfunction %!demo %! ## Sample 3 populations from different Gamma distibutions %! randg ("seed", 5); # for reproducibility %! r1 = gamrnd (1, 2, 2000, 1); %! randg ("seed", 2); # for reproducibility %! r2 = gamrnd (2, 2, 2000, 1); %! randg ("seed", 7); # for reproducibility %! r3 = gamrnd (7.5, 1, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 75, 4); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 0.62]); %! xlim ([0, 12]); %! hold on %! %! ## Estimate their α and β parameters %! a_bA = gamfit (r(:,1)); %! a_bB = gamfit (r(:,2)); %! a_bC = gamfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0.01,0.1:0.2:18]; %! y = gampdf (x, a_bA(1), a_bA(2)); %! plot (x, y, "-pr"); %! y = gampdf (x, a_bB(1), a_bB(2)); %! plot (x, y, "-sg"); %! y = gampdf (x, a_bC(1), a_bC(2)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with α=1 and β=2", ... %! "Normalized HIST of sample 2 with α=2 and β=2", ... %! "Normalized HIST of sample 3 with α=7.5 and β=1", ... %! sprintf("PDF for sample 1 with estimated α=%0.2f and β=%0.2f", ... %! a_bA(1), a_bA(2)), ... %! sprintf("PDF for sample 2 with estimated α=%0.2f and β=%0.2f", ... %! a_bB(1), a_bB(2)), ... %! sprintf("PDF for sample 3 with estimated α=%0.2f and β=%0.2f", ... %! a_bC(1), a_bC(2))}) %! title ("Three population samples from different Gamma distibutions") %! hold off ## Test output %!shared x %! x = [1.2 1.6 1.7 1.8 1.9 2.0 2.2 2.6 3.0 3.5 4.0 4.8 5.6 6.6 7.6]; %!test %! [paramhat, paramci] = gamfit (x); %! assert (paramhat, [3.4248, 0.9752], 1e-4); %! assert (paramci, [1.7287, 0.4670; 6.7852, 2.0366], 1e-4); %!test %! [paramhat, paramci] = gamfit (x, 0.01); %! assert (paramhat, [3.4248, 0.9752], 1e-4); %! assert (paramci, [1.3945, 0.3705; 8.4113, 2.5668], 1e-4); %!test %! freq = [1 1 1 1 2 1 1 1 1 2 1 1 1 1 2]; %! [paramhat, paramci] = gamfit (x, [], [], freq); %! assert (paramhat, [3.3025, 1.0615], 1e-4); %! assert (paramci, [1.7710, 0.5415; 6.1584, 2.0806], 1e-4); %!test %! [paramhat, paramci] = gamfit (x, [], [], [1:15]); %! assert (paramhat, [4.4484, 0.9689], 1e-4); %! assert (paramci, [3.4848, 0.7482; 5.6785, 1.2546], 1e-4); %!test %! [paramhat, paramci] = gamfit (x, 0.01, [], [1:15]); %! assert (paramhat, [4.4484, 0.9689], 1e-4); %! assert (paramci, [3.2275, 0.6899; 6.1312, 1.3608], 1e-4); %!test %! cens = [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0]; %! [paramhat, paramci] = gamfit (x, [], cens, [1:15]); %! assert (paramhat, [4.7537, 0.9308], 1e-4); %! assert (paramci, [3.7123, 0.7162; 6.0872, 1.2097], 1e-4); %!test %! cens = [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0]; %! freq = [1 1 1 1 2 1 1 1 1 2 1 1 1 1 2]; %! [paramhat, paramci] = gamfit (x, [], cens, freq); %! assert (paramhat, [3.4736, 1.0847], 1e-4); %! assert (paramci, [1.8286, 0.5359; 6.5982, 2.1956], 1e-4); ## Test edge cases %!test %! [paramhat, paramci] = gamfit ([1 1 1 1 1 1]); %! assert (paramhat, [Inf, 0]); %! assert (paramci, [Inf, 0; Inf, 0]); %!test %! [paramhat, paramci] = gamfit ([1 1 1 1 1 1], [], [1 1 1 1 1 1]); %! assert (paramhat, [NaN, NaN]); %! assert (paramci, [NaN, NaN; NaN, NaN]); %!test %! [paramhat, paramci] = gamfit ([1 1 1 1 1 1], [], [], [1 1 1 1 1 1]); %! assert (paramhat, [Inf, 0]); %! assert (paramci, [Inf, 0; Inf, 0]); ## Test class of input preserved %!assert (class (gamfit (single (x))), "single") ## Test input validation %!error gamfit (ones (2)) %!error gamfit (x, 1) %!error gamfit (x, -1) %!error gamfit (x, {0.05}) %!error gamfit (x, "a") %!error gamfit (x, i) %!error gamfit (x, [0.01 0.02]) %!error %! gamfit ([1 2 3], 0.05, [], [1 5]) %!error %! gamfit ([1 2 3], 0.05, [], [1 5 -1]) %!error ... %! gamfit ([1:10], 0.05, [], [], 5) %!error gamfit ([1 2 3 -4]) %!error ... %! gamfit ([1 2 0], [], [1 0 0]) statistics-release-1.7.3/inst/dist_fit/gamlike.m000066400000000000000000000246231475240274700217260ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## Based on previous work by Martijn van Oosterhout ## originally granted to the public domain. ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} gamlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} gamlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} gamlike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} gamlike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the Gamma distribution. ## ## @code{@var{nlogL} = gamlike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Gamma distribution ## with (1) shape parameter @var{a} and (2) scale parameter @var{b} given in the ## two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = gamlike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. ## ## @code{[@dots{}] = gamlike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = gamlike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gamcdf, gampdf, gaminv, gamrnd, gamfit} ## @end deftypefn function [nlogL, acov] = gamlike (params, x, censor, freq) ## Check input arguments and add defaults if (nargin < 2) error ("gamlike: function called with too few input arguments."); endif if (numel (params) != 2) error ("gamlike: wrong parameters length."); endif if (! isvector (x)) error ("gamlike: X must be a vector."); endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("gamlike: X and CENSOR vectors mismatch."); endif if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("gamlike: X and FREQ vectors mismatch."); endif ## Get K and THETA values a = params(1); b = params(2); ## Parameters K and THETA must be positive, otherwise make them NaN a(a <= 0) = NaN; b(b <= 0) = NaN; ## Data in X must be positive, otherwise make it NaN x(x <= 0) = NaN; ## Compute the individual log-likelihood terms z = x ./ b; L = (a - 1) .* log (z) - z - gammaln (a) - log (b); n_censored = sum (freq .* censor); if (n_censored > 0) z_censored = z(logical (censor)); Scen = gammainc (z_censored, a, "upper"); L(logical (censor)) = log (Scen); endif ## Force a log(0)==-Inf for X from extreme right tail L(z == Inf) = -Inf; ## Neg-log-likelihood is the sum of the individual contributions nlogL = -sum (freq .* L); ## Compute the negative hessian at the parameter values. ## Invert to get the observed information matrix. if (nargout == 2) ## Calculate all data dL11 = -psi (1, a) * ones (size (z), "like", z); dL12 = -(1 ./ b) * ones (size (z), "like", z); dL22 = -(2 .* z - a) ./ (b .^ 2); ## Calculate censored data if (n_censored > 0) ## Compute derivatives [y, dy, d2y] = dgammainc (z_censored, a); dlnS = dy ./ y; d2lnS = d2y ./ y - dlnS.*dlnS; #[dlnS,d2lnS] = dlngamsf(z_censored,a); logzcen = log(z_censored); tmp = exp(a .* logzcen - z_censored - gammaln(a) - log(b)) ./ Scen; dL11(logical (censor)) = d2lnS; dL12(logical (censor)) = tmp .* (logzcen - dlnS - psi(0,a)); dL22(logical (censor)) = tmp .* ((z_censored-1-a)./b - tmp); endif nH11 = -sum(freq .* dL11); nH12 = -sum(freq .* dL12); nH22 = -sum(freq .* dL22); nH = [nH11 nH12; nH12 nH22]; if (any (isnan (nH(:)))) acov = nan (2, "like", nH); else acov = inv (nH); endif endif endfunction ## Compute the incomplete Gamma function with its 1st and 2nd derivatives function [y, dy, d2y] = dgammainc (x, a) ## Initialize return variables y = nan (size (x)); dy = y; d2y = y; ## Use approximation for K > 2^20 ulim = 2^20; is_lim = find (a > ulim); if (! isempty (is_lim)) x(is_lim) = max (ulim - 1/3 + sqrt (ulim ./ a(is_lim)) .* ... (x(is_lim) - (a(is_lim) - 1/3)), 0); a(is_lim) = ulim; endif ## For x < a+1 is_lo = find(x < a + 1 & x != 0); if (! isempty (is_lo)) x_lo = x(is_lo); k_lo = a(is_lo); k_1 = k_lo; step = 1; d1st = 0; d2st = 0; stsum = step; d1sum = d1st; d2sum = d2st; while norm (step, "inf") >= 100 * eps (norm (stsum, "inf")) k_1 += 1; step = step .* x_lo ./ k_1; d1st = (d1st .* x_lo - step) ./ k_1; d2st = (d2st .* x_lo - 2 .* d1st) ./ k_1; stsum = stsum + step; d1sum = d1sum + d1st; d2sum = d2sum + d2st; endwhile fklo = exp (-x_lo + k_lo .* log (x_lo) - gammaln (k_lo + 1)); y_lo = fklo .* stsum; ## Fix very small a y_lo(x_lo > 0 & y_lo > 1) = 1; ## Compute 1st derivative dlogfklo = (log (x_lo) - psi (k_lo + 1)); d1fklo = fklo .* dlogfklo; d1y_lo = d1fklo .* stsum + fklo .* d1sum; ## Compute 2nd derivative d2fklo = d1fklo .* dlogfklo - fklo .* psi (1, k_lo + 1); d2y_lo = d2fklo .* stsum + 2 .* d1fklo .* d1sum + fklo .* d2sum; ## Considering the upper tail y(is_lo) = 1 - y_lo; dy(is_lo) = -d1y_lo; d2y(is_lo) = -d2y_lo; endif ## For x >= a+1 is_hi = find(x >= a+1); if (! isempty (is_hi)) x_hi = x(is_hi); k_hi = a(is_hi); zc = 0; k0 = 0; k1 = k_hi; x0 = 1; x1 = x_hi; d1k0 = 0; d1k1 = 1; d1x0 = 0; d1x1 = 0; d2k0 = 0; d2k1 = 0; d2x0 = 0; d2x2 = 0; kx = k_hi ./ x_hi; d1kx = 1 ./ x_hi; d2kx = 0; start = 1; while norm (d2kx - start, "Inf") > 100 * eps (norm (d2kx, "Inf")) rescale = 1 ./ x1; zc += 1; n_k = zc - k_hi; d2k0 = (d2k1 + d2k0 .* n_k - 2 .* d1k0) .* rescale; d2x0 = (d2x2 + d2x0 .* n_k - 2 .* d1x0) .* rescale; d1k0 = (d1k1 + d1k0 .* n_k - k0) .* rescale; d1x0 = (d1x1 + d1x0 .* n_k - x0) .* rescale; k0 = (k1 + k0 .* n_k) .* rescale; x0 = 1 + (x0 .* n_k) .* rescale; nrescale = zc .* rescale; d2k1 = d2k0 .* x_hi + d2k1 .* nrescale; d2x2 = d2x0 .* x_hi + d2x2 .* nrescale; d1k1 = d1k0 .* x_hi + d1k1 .* nrescale; d1x1 = d1x0 .* x_hi + d1x1 .* nrescale; k1 = k0 .* x_hi + k1 .* nrescale; x1 = x0 .* x_hi + zc; start = d2kx; kx = k1 ./ x1; d1kx = (d1k1 - kx .* d1x1) ./ x1; d2kx = (d2k1 - d1kx .* d1x1 - kx .* d2x2 - d1kx .* d1x1) ./ x1; endwhile fkhi = exp (-x_hi + k_hi .* log (x_hi) - gammaln (k_hi + 1)); y_hi = fkhi .* kx; ## Compute 1st derivative dlogfkhi = (log (x_hi) - psi (k_hi + 1)); d1fkhi = fkhi .* dlogfkhi; d1y_hi = d1fkhi .* kx + fkhi .* d1kx; ## Compute 2nd derivative d2fkhi = d1fkhi .* dlogfkhi - fkhi .* psi (1, k_hi + 1); d2y_hi = d2fkhi .* kx + 2 .* d1fkhi .* d1kx + fkhi .* d2kx; ## Considering the upper tail y(is_hi) = y_hi; dy(is_hi) = d1y_hi; d2y(is_hi) = d2y_hi; endif ## Handle x == 0 is_x0 = find (x == 0); if (! isempty (is_x0)) ## Considering the upper tail y(is_x0) = 1; dy(is_x0) = 0; d2y(is_x0) = 0; endif ## Handle a == 0 is_k0 = find (a == 0); if (! isempty (is_k0)) is_k0x0 = find (a == 0 & x == 0); ## Considering the upper tail y(is_k0) = 0; dy(is_k0x0) = Inf; d2y(is_k0x0) = -Inf; endif endfunction ## Test output %!test %! [nlogL, acov] = gamlike([2, 3], [2, 3, 4, 5, 6, 7, 8, 9]); %! assert (nlogL, 19.4426, 1e-4); %! assert (acov, [2.7819, -5.0073; -5.0073, 9.6882], 1e-4); %!test %! [nlogL, acov] = gamlike([2, 3], [5:45]); %! assert (nlogL, 305.8070, 1e-4); %! assert (acov, [0.0423, -0.0087; -0.0087, 0.0167], 1e-4); %!test %! [nlogL, acov] = gamlike([2, 13], [5:45]); %! assert (nlogL, 163.2261, 1e-4); %! assert (acov, [0.2362, -1.6631; -1.6631, 13.9440], 1e-4); ## Test input validation %!error ... %! gamlike ([12, 15]) %!error gamlike ([12, 15, 3], [1:50]) %!error gamlike ([12, 3], ones (10, 2)) %!error ... %! gamlike ([12, 15], [1:50], [1, 2, 3]) %!error ... %! gamlike ([12, 15], [1:50], [], [1, 2, 3]) statistics-release-1.7.3/inst/dist_fit/geofit.m000066400000000000000000000132241475240274700215650ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pshat} =} geofit (@var{x}) ## @deftypefnx {statistics} {[@var{pshat}, @var{psci}] =} geofit (@var{x}) ## @deftypefnx {statistics} {[@var{pshat}, @var{psci}] =} geofit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{pshat}, @var{psci}] =} geofit (@var{x}, @var{alpha}, @var{freq}) ## ## Estimate parameter and confidence intervals for the geometric distribution. ## ## @code{@var{pshat} = geofit (@var{x})} returns the maximum likelihood estimate ## (MLE) of the probability of success for the geometric distribution. @var{x} ## must be a vector. ## ## @code{[@var{pshat}, @var{psci}] = geofit (@var{x}, @var{alpha})} also returns ## the @qcode{100 * (1 - @var{alpha})} percent confidence intervals of the ## estimated parameter. By default, the optional argument @var{alpha} is 0.05 ## corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = geofit (@var{x}, @var{alpha}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## The geometric distribution models the number of failures (@var{x}) of a ## Bernoulli trial with probability @var{ps} before the first success. ## ## Further information about the geometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Geometric_distribution} ## ## @seealso{geocdf, geoinv, geopdf, geornd, geostat} ## @end deftypefn function [pshat, psci] = geofit (x, alpha, freq) ## Check input arguments if (nargin < 1) error ("geofit: function called with too few input arguments."); endif ## Check data inX if (any (x < 0)) error ("geofit: X cannot have negative values."); endif if (! isvector (x)) error ("geofit: X must be a vector."); endif ## Check ALPHA if (nargin < 2 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("geofit: wrong value for ALPHA."); endif ## Check frequency vector if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("geofit: X and FREQ vector mismatch."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); endif ## Compute PS estimate pshat = 1 ./ (1 + mean (x)); ## Compute confidence interval of PS if (nargout > 1) sz = numel (x); serr = pshat .* sqrt ((1 - pshat) ./ sz); psci = norminv ([alpha/2; 1-alpha/2], [pshat; pshat], [serr; serr]); endif endfunction %!demo %! ## Sample 2 populations from different geometric distibutions %! rande ("seed", 1); # for reproducibility %! r1 = geornd (0.15, 1000, 1); %! rande ("seed", 2); # for reproducibility %! r2 = geornd (0.5, 1000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, 0:0.5:20.5, 1); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their probability of success %! pshatA = geofit (r(:,1)); %! pshatB = geofit (r(:,2)); %! %! ## Plot their estimated PDFs %! x = [0:15]; %! y = geopdf (x, pshatA); %! plot (x, y, "-pg"); %! y = geopdf (x, pshatB); %! plot (x, y, "-sc"); %! xlim ([0, 15]) %! ylim ([0, 0.6]) %! legend ({"Normalized HIST of sample 1 with ps=0.15", ... %! "Normalized HIST of sample 2 with ps=0.50", ... %! sprintf("PDF for sample 1 with estimated ps=%0.2f", ... %! mean (pshatA)), ... %! sprintf("PDF for sample 2 with estimated ps=%0.2f", ... %! mean (pshatB))}) %! title ("Two population samples from different geometric distibutions") %! hold off ## Test output %!test %! x = 0:5; %! [pshat, psci] = geofit (x); %! assert (pshat, 0.2857, 1e-4); %! assert (psci, [0.092499; 0.478929], 1e-5); %!test %! x = 0:5; %! [pshat, psci] = geofit (x, [], [1 1 1 1 1 1]); %! assert (pshat, 0.2857, 1e-4); %! assert (psci, [0.092499; 0.478929], 1e-5); %!assert (geofit ([1 1 2 3]), geofit ([1 2 3], [] ,[2 1 1])) ## Test input validation %!error geofit () %!error geofit (-1, [1 2 3 3]) %!error geofit (1, 0) %!error geofit (1, 1.2) %!error geofit (1, [0.02 0.05]) %!error ... %! geofit ([1.5, 0.2], [], [0, 0, 0, 0, 0]) %!error ... %! geofit ([1.5, 0.2], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/gevfit.m000066400000000000000000000275051475240274700216030ustar00rootroot00000000000000## Copyright (C) 2012-2021 Nir Krakauer ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} gevfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gevfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gevfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gevfit (@var{x}, @var{alpha}, @var{freq}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gevfit (@var{x}, @var{alpha}, @var{options}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gevfit (@var{x}, @var{alpha}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the generalized extreme ## value (GEV) distribution. ## ## @code{@var{paramhat} = gevfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the GEV distribution given the data in ## @var{x}. @qcode{@var{paramhat}(1)} is the shape parameter, @var{k}, and ## @qcode{@var{paramhat}(2)} is the scale parameter, @var{sigma}, and ## @qcode{@var{paramhat}(3)} is the location parameter, @var{mu}. ## ## @code{[@var{paramhat}, @var{paramci}] = gevfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = gevfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = gevfit (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@var{paramhat}, @var{paramci}] = gevfit (@var{x}, @var{alpha}, ## @var{options})} specifies control parameters for the iterative algorithm used ## to compute ML estimates with the @code{fminsearch} function. @var{options} ## is a structure with the following fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## When @qcode{@var{k} < 0}, the GEV is the type III extreme value distribution. ## When @qcode{@var{k} > 0}, the GEV distribution is the type II, or Frechet, ## extreme value distribution. If @var{W} has a Weibull distribution as ## computed by the @code{wblcdf} function, then @qcode{-@var{W}} has a type III ## extreme value distribution and @qcode{1/@var{W}} has a type II extreme value ## distribution. In the limit as @var{k} approaches @qcode{0}, the GEV is the ## mirror image of the type I extreme value distribution as computed by the ## @code{evcdf} function. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevcdf, gevinv, gevpdf, gevrnd, gevlike, gevstat} ## @end deftypefn function [paramhat, paramci] = gevfit (x, alpha, varargin) ## Check X is vector if (! isvector (x)) error ("gevfit: X must be a vector."); endif ## Get X type and convert to double for computation is_type = class (x); if (strcmpi (is_type, "single")) x = double (x); endif ## Check that X is not constant and does not contain NaNs sample_size = length (x); if (sample_size == 0 || any (isnan (x))) paramhat = NaN (1,3, is_type); paramci = NaN (2,3, is_type); warning ("gevfit: X contains NaNs."); return elseif (numel (unique (x)) == 1) paramhat = cast ([0, 0, unique(x)], is_type); if (length (x) == 1) paramci = cast ([-Inf, 0, -Inf; Inf, Inf, Inf], is_type); else paramci = [paramhat; paramhat]; endif warning ("gevfit: X is a constant vector."); return endif ## Check ALPHA if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("gevfit: wrong value for ALPHA."); endif endif ## Add defaults freq = []; options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; ## Check extra arguments for FREQ vector and/or 'options' structure if (nargin > 2) if (numel (varargin) == 1 && isstruct (varargin{1})) options = varargin{1}; elseif (numel (varargin) == 1 && isnumeric (varargin{1})) freq = varargin{1}; elseif (numel (varargin) == 2) freq = varargin{1}; options = varargin{2}; endif if (isempty (freq)) freq = ones (size (x)); endif ## Check for valid freq vector if (! isequal (size (x), size (freq))) error ("gevfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("gevfit: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("gevfit: FREQ must contain integer values."); endif ## Check for valid options structure if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["gevfit: 'options' argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Expand frequency if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Force to column vector x = x(:); ## Compute initial parameters F = (0.5:1:(sample_size - 0.5))' ./ sample_size; k_0 = fminsearch (@(k) 1 - corr (x, gevinv (F, k, 1, 0)), 0); paramguess = [k_0, polyfit(gevinv(F,k_0,1,0),x',1)]; ## Check if x support initial parameters or fall back to unbounded evfit if (k_0 < 0 && (max (x) > - paramguess(2) / k_0 + paramguess(3)) || ... k_0 > 0 && (min (x) < - paramguess(2) / k_0 + paramguess(3))) paramguess = [evfit(x), 0]; paramguess = flip (paramguess); endif ## Minimize the negative log-likelihood according to initial parameters paramguess(2) = log (paramguess(2)); fhandle = @(paramguess) nll (paramguess, x); [paramhat, ~, exitflag, output] = fminsearch (fhandle, paramguess, options); paramhat(2) = exp (paramhat(2)); ## Display errors and warnings if any if (exitflag == 0) if (output.funcCount >= output.iterations) warning ("gevfit: maximum number of evaluations reached"); else warning ("gevfit: reached iteration limit"); endif elseif (exitflag == -1) error ("gevfit: No solution"); endif ## Return a row vector for Matlab compatibility paramhat = paramhat(:)'; ## Check for second output argument if (nargout > 1) [~, acov] = gevlike (paramhat, x); param_se = sqrt (diag (acov))'; if (any (iscomplex (param_se))) warning (["gevfit: Fisher information matrix not positive definite;", ... " parameter optimization likely did not converge"]); paramci = NaN (2, 3, is_type); else p_vals = [alpha/2; 1-alpha/2]; k_ci = norminv (p_vals, paramhat(1), param_se(1)); s_ci = exp (norminv (p_vals, log (paramhat(2)), param_se(2) ./ paramhat(2))); m_ci = norminv (p_vals, paramhat(3), param_se(3)); paramci = [k_ci, s_ci, m_ci]; endif endif endfunction ## Negative log-likelihood for the GEV (log(sigma) parameterization) function out = nll (parms, x) k_0 = parms(1); log_sigma = parms(2); sigma = exp (log_sigma); mu = parms(3); n = numel (x); z = (x - mu) ./ sigma; if abs(k_0) > eps u = 1 + k_0.*z; if min(u) > 0 lnu = log1p (k_0 .* z); out = n * log_sigma + sum (exp ((-1 / k_0) * lnu)) + ... (1 + 1 / k_0) * sum (lnu); else out = Inf; endif else out = n * log_sigma + sum (exp (-z) + z); endif endfunction %!demo %! ## Sample 2 populations from 2 different exponential distibutions %! rand ("seed", 1); # for reproducibility %! r1 = gevrnd (-0.5, 1, 2, 5000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = gevrnd (0, 1, -4, 5000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, 50, 5); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their k, sigma, and mu parameters %! k_sigma_muA = gevfit (r(:,1)); %! k_sigma_muB = gevfit (r(:,2)); %! %! ## Plot their estimated PDFs %! x = [-10:0.5:20]; %! y = gevpdf (x, k_sigma_muA(1), k_sigma_muA(2), k_sigma_muA(3)); %! plot (x, y, "-pr"); %! y = gevpdf (x, k_sigma_muB(1), k_sigma_muB(2), k_sigma_muB(3)); %! plot (x, y, "-sg"); %! ylim ([0, 0.7]) %! xlim ([-7, 5]) %! legend ({"Normalized HIST of sample 1 with k=-0.5, σ=1, μ=2", ... %! "Normalized HIST of sample 2 with k=0, σ=1, μ=-4", %! sprintf("PDF for sample 1 with estimated k=%0.2f, σ=%0.2f, μ=%0.2f", ... %! k_sigma_muA(1), k_sigma_muA(2), k_sigma_muA(3)), ... %! sprintf("PDF for sample 3 with estimated k=%0.2f, σ=%0.2f, μ=%0.2f", ... %! k_sigma_muB(1), k_sigma_muB(2), k_sigma_muB(3))}) %! title ("Two population samples from different exponential distibutions") %! hold off ## Test output %!test %! x = 1:50; %! [pfit, pci] = gevfit (x); %! pfit_out = [-0.4407, 15.1923, 21.5309]; %! pci_out = [-0.7532, 11.5878, 16.5686; -0.1282, 19.9183, 26.4926]; %! assert (pfit, pfit_out, 1e-3); %! assert (pci, pci_out, 1e-3); %!test %! x = 1:2:50; %! [pfit, pci] = gevfit (x); %! pfit_out = [-0.4434, 15.2024, 21.0532]; %! pci_out = [-0.8904, 10.3439, 14.0168; 0.0035, 22.3429, 28.0896]; %! assert (pfit, pfit_out, 1e-3); %! assert (pci, pci_out, 1e-3); ## Test input validation %!error gevfit (ones (2,5)); %!error gevfit ([1, 2, 3, 4, 5], 1.2); %!error gevfit ([1, 2, 3, 4, 5], 0); %!error gevfit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! gevfit ([1, 2, 3, 4, 5], 0.05, [1, 2, 3, 2]); %!error ... %! gevfit ([1, 2, 3, 4, 5], 0.05, [1, 2, 3, 2, -1]); %!error ... %! gevfit ([1, 2, 3, 4, 5], 0.05, [1, 2, 3, 2, 1.5]); %!error ... %! gevfit ([1, 2, 3, 4, 5], 0.05, struct ("option", 234)); %!error ... %! gevfit ([1, 2, 3, 4, 5], 0.05, ones (1,5), struct ("option", 234)); statistics-release-1.7.3/inst/dist_fit/gevfit_lmom.m000066400000000000000000000066701475240274700226270ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{paramhat}, @var{paramci}] =} gevfit_lmom (@var{data}) ## ## Find an estimator (@var{paramhat}) of the generalized extreme value (GEV) ## distribution fitting @var{data} using the method of L-moments. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{data} is the vector of given values. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{parmhat} is the 3-parameter maximum-likelihood parameter vector ## [@var{k}; @var{sigma}; @var{mu}], where @var{k} is the shape parameter of the ## GEV distribution, @var{sigma} is the scale parameter of the GEV distribution, ## and @var{mu} is the location parameter of the GEV distribution. ## @item ## @var{paramci} has the approximate 95% confidence intervals of the parameter ## values (currently not implemented). ## ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## data = gevrnd (0.1, 1, 0, 100, 1); ## [pfit, pci] = gevfit_lmom (data); ## p1 = gevcdf (data,pfit(1),pfit(2),pfit(3)); ## [f, x] = ecdf (data); ## plot(data, p1, 's', x, f) ## @end group ## @end example ## @seealso{gevfit} ## @subheading References ## ## @enumerate ## @item ## Ailliot, P.; Thompson, C. & Thomson, P. Mixed methods for fitting the GEV ## distribution, Water Resources Research, 2011, 47, W05551 ## ## @end enumerate ## @end deftypefn function [paramhat, paramci] = gevfit_lmom (data) # Check arguments if (nargin < 1) print_usage; endif # find the L-moments data = sort (data(:))'; n = numel(data); L1 = mean(data); L2 = sum(data .* (2*(1:n) - n - 1)) / (2*nchoosek(n, 2)); # or mean(triu(data' - data, 1, 'pack')) / 2; b = bincoeff((1:n) - 1, 2); L3 = sum(data .* (b - 2 * ((1:n) - 1) .* (n - (1:n)) + fliplr(b))) / (3*nchoosek(n, 3)); #match the moments to the GEV distribution #first find k based on L3/L2 f = @(k) (L3/L2 + 3)/2 - limdiv((1 - 3^(k)), (1 - 2^(k))); k = fzero(f, 0); #next find sigma and mu given k if abs(k) < 1E-8 sigma = L2 / log(2); eg = 0.57721566490153286; %Euler-Mascheroni constant mu = L1 - sigma * eg; else sigma = -k*L2 / (gamma(1 - k) * (1 - 2^(k))); mu = L1 - sigma * ((gamma(1 - k) - 1) / k); endif paramhat = [k; sigma; mu]; if nargout > 1 paramci = NaN; endif endfunction #internal function to accurately evaluate (1 - 3^k)/(1 - 2^k) in the limit as k --> 0 function c = limdiv(a, b) # c = ifelse (abs(b) < 1E-8, log(3)/log(2), a ./ b); if abs(b) < 1E-8 c = log(3)/log(2); else c = a / b; endif endfunction %!xtest <31070> %! data = 1:50; %! [pfit, pci] = gevfit_lmom (data); %! expected_p = [-0.28 15.01 20.22]'; %! assert (pfit, expected_p, 0.1); statistics-release-1.7.3/inst/dist_fit/gevlike.m000066400000000000000000000327631475240274700217470ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} gevlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} gevlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} gevlike (@var{params}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the generalized extreme value (GEV) distribution. ## ## @code{@var{nlogL} = gevlike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the GEV distribution ## with (1) shape parameter @var{k}, (2) scale parameter @var{sigma}, and (3) ## location parameter @var{mu} given in the three-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = gevlike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. ## ## @code{[@dots{}] = gevlike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## When @qcode{@var{k} < 0}, the GEV is the type III extreme value distribution. ## When @qcode{@var{k} > 0}, the GEV distribution is the type II, or Frechet, ## extreme value distribution. If @var{W} has a Weibull distribution as ## computed by the @code{wblcdf} function, then @qcode{-@var{W}} has a type III ## extreme value distribution and @qcode{1/@var{W}} has a type II extreme value ## distribution. In the limit as @var{k} approaches @qcode{0}, the GEV is the ## mirror image of the type I extreme value distribution as computed by the ## @code{evcdf} function. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevcdf, gevinv, gevpdf, gevrnd, gevfit, gevstat} ## @end deftypefn function [nlogL, acov] = gevlike (params, x, freq) ## Check input arguments if (nargin < 2) error ("gevlike: function called with too few input arguments."); endif if (! isvector (x)) error ("gevlike: X must be a vector."); endif if (length (params) != 3) error ("gevlike: PARAMS must be a three-element vector."); endif if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("gevlike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("gevlike: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("gevlike: FREQ must contain integer values."); endif ## Expand frequency if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif k = params(1); sigma = params(2); mu = params(3); ## Calculate negative log likelihood [nll, k_terms] = gevnll (x, k, sigma, mu); nlogL = sum (nll(:)); ## Optionally calculate the first and second derivatives of the negative log ## likelihood with respect to parameters if (nargout > 1) [Grad, kk_terms] = gevgrad (x, k, sigma, mu, k_terms); FIM = gevfim (x, k, sigma, mu, k_terms, kk_terms); acov = inv (FIM); endif endfunction ## Internal function to calculate negative log likelihood for gevlike function [nlogL, k_terms] = gevnll (x, k, sigma, mu) k_terms = []; a = (x - mu) ./ sigma; if (all (k == 0)) nlogL = exp(-a) + a + log(sigma); else aa = k .* a; ## Use a series expansion to find the log likelihood more accurately ## when k is small if (min (abs (aa)) < 1E-3 && max (abs (aa)) < 0.5) k_terms = 1; sgn = 1; i = 0; while 1 sgn = -sgn; i++; newterm = (sgn / (i + 1)) * (aa .^ i); k_terms = k_terms + newterm; if (max (abs (newterm)) <= eps) break endif endwhile nlogL = exp (-a .* k_terms) + a .* (k + 1) .* k_terms + log (sigma); else b = 1 + aa; nlogL = b .^ (-1 ./ k) + (1 + 1 ./ k) .* log (b) + log (sigma); nlogL(b <= 0) = Inf; endif endif endfunction ## Calculate the gradient of the negative log likelihood of x x with respect ## to the parameters of the generalized extreme value distribution for gevlike function [G, kk_terms] = gevgrad (x, k, sigma, mu, k_terms) kk_terms = []; G = ones(3, 1); ## Use the expressions for first derivatives that are the limits as k --> 0 if (k == 0) a = (x - mu) ./ sigma; f = exp(-a) - 1; ## k g = a .* (1 + a .* f / 2); G(1) = sum(g(:)); ## sigma g = (a .* f + 1) ./ sigma; G(2) = sum(g(:)); ## mu g = f ./ sigma; G(3) = sum(g(:)); return endif a = (x - mu) ./ sigma; b = 1 + k .* a; ## Negative log likelihood is locally infinite if (any (b <= 0)) G(:) = 0; return endif ## k c = log(b); d = 1 ./ k + 1; ## Use a series expansion to find the gradient more accurately when k is small if (nargin > 4 && ! isempty (k_terms)) aa = k .* a; f = exp (-a .* k_terms); kk_terms = 0.5; sgn = 1; i = 0; while 1 sgn = -sgn; i++; newterm = (sgn * (i + 1) / (i + 2)) * (aa .^ i); kk_terms = kk_terms + newterm; if (max (abs (newterm)) <= eps) break endif endwhile g = a .* ((a .* kk_terms) .* (f - 1 - k) + k_terms); else g = (c ./ k - a ./ b) ./ (k .* b .^ (1/k)) - c ./ (k .^ 2) + a .* d ./ b; endif G(1) = sum(g(:)); ## sigma ## Use a series expansion to find the gradient more accurately when k is small if nargin > 4 && ~isempty(k_terms) g = (1 - a .* (a .* k .* kk_terms - k_terms) .* (f - k - 1)) ./ sigma; else g = (a .* b .^ (-d) - (k + 1) .* a ./ b + 1) ./ sigma; endif G(2) = sum(g(:)); ## mu ## Use a series expansion to find the gradient more accurately when k is small if (nargin > 4 && ! isempty (k_terms)) g = - (a .* k .* kk_terms - k_terms) .* (f - k - 1) ./ sigma; else g = (b .^ (-d) - (k + 1) ./ b) ./ sigma; end G(3) = sum(g(:)); endfunction ## Internal function to calculate the Fisher information matrix for gevlike function ACOV = gevfim (x, k, sigma, mu, k_terms, kk_terms) ACOV = ones(3); ## Use the expressions for second derivatives that are the limits as k --> 0 if (k == 0) ## k, k a = (x - mu) ./ sigma; f = exp(-a); der = (a .^ 2) .* (a .* (a/4 - 2/3) .* f + 2/3 * a - 1); ACOV(1, 1) = sum(der(:)); ## sigma, sigma der = (sigma .^ -2) .* (a .* ((a - 2) .* f + 2) - 1); ACOV(2, 2) = sum(der(:)); ## mu, mu der = (sigma .^ -2) .* f; ACOV(3, 3) = sum(der(:)); ## k, sigma der = (-a ./ sigma) .* (a .* (1 - a/2) .* f - a + 1); ACOV(1, 2) = ACOV(2, 1) = sum(der(:)); ## k, mu der = (-1 ./ sigma) .* (a .* (1 - a/2) .* f - a + 1); ACOV(1, 3) = ACOV(3, 1) = sum(der(:)); ## sigma, mu der = (1 + (a - 1) .* f) ./ (sigma .^ 2); ACOV(2, 3) = ACOV(3, 2) = sum(der(:)); return endif ## General case z = 1 + k .* (x - mu) ./ sigma; ## k, k a = (x - mu) ./ sigma; b = k .* a + 1; c = log(b); d = 1 ./ k + 1; ## Use a series expansion to find the derivatives more accurately ## when k is small if (nargin > 5 && ! isempty (kk_terms)) aa = k .* a; f = exp (-a .* k_terms); kkk_terms = 2/3; sgn = 1; i = 0; while 1 sgn = -sgn; i++; newterm = (sgn * (i + 1) * (i + 2) / (i + 3)) * (aa .^ i); kkk_terms = kkk_terms + newterm; if (max (abs (newterm)) <= eps) break endif endwhile der = (a .^ 2) .* (a .* (a .* kk_terms .^ 2 - kkk_terms) .* ... f + a .* (1 + k) .* kkk_terms - 2 * kk_terms); else der = ((((c ./ k.^2) - (a ./ (k .* b))) .^ 2) ./ (b .^ (1 ./ k))) + ... ((-2*c ./ k.^3) + (2*a ./ (k.^2 .* b)) + ((a ./ b) .^ 2 ./ k)) ./ ... (b .^ (1 ./ k)) + 2*c ./ k.^3 - (2*a ./ (k.^2 .* b)) - (d .* (a ./ b) .^ 2); endif der(z <= 0) = 0; # no probability mass in this region ACOV(1, 1) = sum (der(:)); ## sigma, sigma ## Use a series expansion to find the derivatives more accurately ## when k is small if (nargin > 5 && ! isempty (kk_terms)) der = ((-2*a .* k_terms + 4 * a .^ 2 .* k .* kk_terms - a .^ 3 .* ... (k .^ 2) .* kkk_terms) .* (f - k - 1) + f .* ((a .* ... (k_terms - a .* k .* kk_terms)) .^ 2) - 1) ./ (sigma .^ 2); else der = (sigma .^ -2) .* (-2 * a .* b .^ (-d) + d .* k .* a .^ 2 .* ... (b .^ (-d-1)) + 2 .* d .* k .* a ./ b - d .* (k .* a ./ b) .^ 2 - 1); end der(z <= 0) = 0; # no probability mass in this region ACOV(2, 2) = sum (der(:)); ## mu, mu ## Use a series expansion to find the derivatives more accurately ## when k is small if (nargin > 5 && ! isempty (kk_terms)) der = (f .* (a .* k .* kk_terms - k_terms) .^ 2 - a .* k .^ 2 .* ... kkk_terms .* (f - k - 1)) ./ (sigma .^ 2); else der = (d .* (sigma .^ -2)) .* (k .* (b .^ (-d-1)) - (k ./ b) .^ 2); endif der(z <= 0) = 0; # no probability mass in this region ACOV(3, 3) = sum (der(:)); ## k, mu ## Use a series expansion to find the derivatives more accurately ## when k is small if (nargin > 5 && ! isempty (kk_terms)) der = 2 * a .* kk_terms .* (f - 1 - k) - a .^ 2 .* k_terms .* ... kk_terms .* f + k_terms; der = -der ./ sigma; else der = ((b .^ (-d)) .* (c ./ k - a ./ b) ./ k - a .* (b .^ (-d-1)) + ... ((1 ./ k) - d) ./ b + a .* k .* d ./ (b .^ 2)) ./ sigma; endif der(z <= 0) = 0; # no probability mass in this region ACOV(1, 3) = ACOV(3, 1) = sum (der(:)); ## k, sigma der = a .* der; der(z <= 0) = 0; # no probability mass in this region ACOV(1, 2) = ACOV(2, 1) = sum (der(:)); ## sigma, mu ## Use a series expansion to find the derivatives more accurately ## when k is small if (nargin > 5 && ! isempty (kk_terms)) der = ((-k_terms + 3 * a .* k .* kk_terms - (a .* k) .^ 2 .* ... kkk_terms) .* (f - k - 1) + a .* (k_terms - a .* k .* ... kk_terms) .^ 2 .* f) ./ (sigma .^ 2); else der = (-(b .^ (-d)) + a .* k .* d .* (b .^ (-d-1)) + ... (d .* k ./ b) - a .* (k./b).^2 .* d) ./ (sigma .^ 2); end der(z <= 0) = 0; # no probability mass in this region ACOV(2, 3) = ACOV(3, 2) = sum (der(:)); endfunction ## Test output %!test %! x = 1; %! k = 0.2; %! sigma = 0.3; %! mu = 0.5; %! [L, C] = gevlike ([k sigma mu], x); %! expected_L = 0.75942; %! expected_C = [-0.12547 1.77884 1.06731; 1.77884 16.40761 8.48877; 1.06731 8.48877 0.27979]; %! assert (L, expected_L, 0.001); %! assert (C, inv (expected_C), 0.001); %!test %! x = 1; %! k = 0; %! sigma = 0.3; %! mu = 0.5; %! [L, C] = gevlike ([k sigma mu], x); %! expected_L = 0.65157; %! expected_C = [0.090036 3.41229 2.047337; 3.412229 24.760027 12.510190; 2.047337 12.510190 2.098618]; %! assert (L, expected_L, 0.001); %! assert (C, inv (expected_C), 0.001); %!test %! x = -5:-1; %! k = -0.2; %! sigma = 0.3; %! mu = 0.5; %! [L, C] = gevlike ([k sigma mu], x); %! expected_L = 3786.4; %! expected_C = [1.6802e-07, 4.6110e-06, 8.7297e-05; ... %! 4.6110e-06, 7.5693e-06, 1.2034e-05; ... %! 8.7297e-05, 1.2034e-05, -0.0019125]; %! assert (L, expected_L, -0.001); %! assert (C, expected_C, -0.001); %!test %! x = -5:0; %! k = -0.2; %! sigma = 0.3; %! mu = 0.5; %! [L, C] = gevlike ([k sigma mu], x, [1, 1, 1, 1, 1, 0]); %! expected_L = 3786.4; %! expected_C = [1.6802e-07, 4.6110e-06, 8.7297e-05; ... %! 4.6110e-06, 7.5693e-06, 1.2034e-05; ... %! 8.7297e-05, 1.2034e-05, -0.0019125]; %! assert (L, expected_L, -0.001); %! assert (C, expected_C, -0.001); ## Test input validation %!error gevlike (3.25) %!error gevlike ([1, 2, 3], ones (2)) %!error ... %! gevlike ([1, 2], [1, 3, 5, 7]) %!error ... %! gevlike ([1, 2, 3, 4], [1, 3, 5, 7]) %!error ... %! gevlike ([5, 0.2, 1], ones (10, 1), ones (8,1)) %!error ... %! gevlike ([5, 0.2, 1], ones (1, 8), [1 1 1 1 1 1 1 -1]) %!error ... %! gevlike ([5, 0.2, 1], ones (1, 8), [1 1 1 1 1 1 1 1.5]) statistics-release-1.7.3/inst/dist_fit/gpfit.m000066400000000000000000000276121475240274700214270ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} gpfit (@var{x}, @var{theta}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gpfit (@var{x}, @var{theta}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gpfit (@var{x}, @var{theta}, @var{alpha}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gpfit (@var{x}, @var{theta}, @var{alpha}, @var{options}) ## ## Estimate parameters and confidence intervals for the generalized Pareto ## distribution. ## ## @code{@var{paramhat} = gpfit (@var{x}, @var{theta})} returns the maximum ## likelihood estimates of the parameters of the generalized Pareto distribution ## given the data in @var{x} and the location parameter @var{theta}. ## @qcode{@var{paramhat}(1)} is the shape parameter, @var{k}, ## @qcode{@var{paramhat}(2)} is the scale parameter, @var{sigma}, and ## @qcode{@var{paramhat}(3)} is the location parameter, @var{theta}. Although ## @var{theta} is returned in the estimated @var{paramhat}, @code{gpfit} does ## not estimate the location parameter @var{theta}, and it must be assumed to be ## known, given as a fixed parameter in input argument @var{theta}. ## ## @code{[@var{paramhat}, @var{paramci}] = gpfit (@var{x}, @var{theta})} returns ## the 95% confidence intervals for the estimated parameter @var{k} and ## @var{sigma}. The third colummn of @var{paramci} includes the location ## parameter @var{theta} without any confidence bounds. ## ## @code{[@dots{}] = gpfit (@var{x}, @var{theta}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = gpfit (@var{x}, @var{theta}, @var{alpha}, @var{options})} ## specifies control parameters for the iterative algorithm used to compute ML ## estimates with the @code{fminsearch} function. @var{options} is a structure ## with the following fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## When @qcode{@var{k} = 0} and @qcode{@var{theta} = 0}, the Generalized Pareto ## is equivalent to the exponential distribution. When @qcode{@var{k} > 0} and ## @code{@var{theta} = @var{k} / @var{k}} the Generalized Pareto is equivalent ## to the Pareto distribution. The mean of the Generalized Pareto is not finite ## when @qcode{@var{k} >= 1} and the variance is not finite when ## @qcode{@var{k} >= 1/2}. When @qcode{@var{k} >= 0}, the Generalized Pareto ## has positive density for @qcode{@var{x} > @var{theta}}, or, when ## @qcode{@var{theta} < 0}, for ## @qcode{0 <= (@var{x} - @var{theta}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpcdf, gpinv, gppdf, gprnd, gplike, gpstat} ## @end deftypefn function [paramhat, paramci] = gpfit (x, theta, alpha, freq, options) ## Check for valid number of input arguments if (nargin < 2) error ("gpfit: function called with too few input arguments."); endif ## Check X for being a vector if (isempty (x)) phat = nan (1, 3, class (x)); pci = nan (3, 3, class (x)); return elseif (! isvector (x) || ! isreal (x)) error ("gpfit: X must be a vector of real values."); endif ## Check for THETA being a scalar real value if (! isscalar (theta) || ! isreal (theta)) error ("gpfit: THETA must be a real scalar value."); endif ## Check X >= THETA if (any (x < theta)) error ("gpfit: X cannot contain values less than THETA."); endif ## Parse ALPHA argument or add default if (nargin < 3 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("gpfit: wrong value for ALPHA."); endif ## Parse FREQ argument or add default if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("gpfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("gpfit: FREQ must not contain negative values."); endif ## Expand frequency vector (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["gpfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Remove NaN from data and make a warning if (any (isnan (x))) x(isnan(x)) = []; warning ("gpfit: X contains NaN values, which are ignored."); endif ## Remove Inf from data and make a warning if (any (isinf (x))) x(isnan(x)) = []; warning ("gpfit: X contains Inf values, which are ignored."); endif ## Get sample size, max and range of X x = x - theta; x_max = max (x); x_size = length (x); x_range = range (x); ## Check for appropriate sample size or all observations being equal if (x_size == 0) paramhat = [NaN(1, 2), theta]; paramci = [NaN(2, 2), [theta; theta]]; warning ("gpfit: X contains no data."); return elseif (x_range < realmin (class (x))) paramhat = cast ([NaN, 0, theta], class (x)); paramci = [paramhat; paramhat]; warning ("gpfit: X contains constant data."); return endif ## Make an initial guess x_mean = mean (x); x_var = var (x); k0 = -0.5 .* (x_mean .^ 2 ./ x_var - 1); s0 = 0.5 .* x_mean .* (x_mean .^ 2 ./ x_var + 1); ## If initial guess fails, start with an exponential fit if (k0 < 0 && (x_max >= -s0 / k0)) k0 = 0; s0 = x_mean; endif paramhat = [k0, log(s0)]; ## Maximize the log-likelihood with respect to shape and log_scale. f = @(paramhat) negloglike (paramhat, x); [paramhat, ~, err, output] = fminsearch (f, paramhat, options); paramhat(2) = exp (paramhat(2)); paramhat = [paramhat, theta]; ## Check output of fminsearch and produce warnings or errors if applicable if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning ("gpfit: reached evaluation limit."); else warning ("gpfit: reached iteration limit."); endif elseif (err < 0) error ("gpfit: no solution."); endif ## Check if converged to boundaries if ((paramhat(1) < 0) && (x_max > -paramhat(2)/paramhat(1) - options.TolX)) warning ("gpfit: converged to boundary 1."); reachedBnd = true; elseif (paramhat(1) <= -1 / 2) warning ("gpfit: converged to boundary 2."); reachedBnd = true; else reachedBnd = false; endif ## If second output argument is requested if (nargout > 1) if (! reachedBnd) probs = [alpha/2; 1-alpha/2]; [~, acov] = gplike (paramhat, x + theta); se = sqrt (diag (acov))'; ## Compute the CI for shape using a normal distribution for khat. kci = norminv (probs, paramhat(1), se(1)); ## Compute the CI for scale using a normal approximation for ## log(sigmahat), and transform back to the original scale. lnsigci = norminv (probs, log (paramhat(2)), se(2) ./ paramhat(2)); paramci = [kci, exp(lnsigci), [theta; theta]]; else paramci = [NaN, NaN, theta; NaN, NaN, theta]; endif endif endfunction ## Negative log-likelihood for the GP function nll = negloglike (paramhat, data) shape = paramhat(1); log_scale = paramhat(2); scale = exp (log_scale); sample_size = numel (data); z = data ./ scale; if (abs (shape) > eps) if (shape > 0 || max (z) < -1 / shape) nll = sample_size * log_scale + (1 + 1/shape) * sum (log1p (shape .* z)); else nll = Inf; endif else nll = sample_size * log_scale + sum (z); endif endfunction %!demo %! ## Sample 2 populations from different generalized Pareto distibutions %! ## Assume location parameter θ is known %! theta = 0; %! rand ("seed", 5); # for reproducibility %! r1 = gprnd (1, 2, theta, 20000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = gprnd (3, 1, theta, 20000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.1:0.2:100], 5); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "r"); %! set (h(2), "facecolor", "c"); %! ylim ([0, 1]); %! xlim ([0, 5]); %! hold on %! %! ## Estimate their α and β parameters %! k_sigmaA = gpfit (r(:,1), theta); %! k_sigmaB = gpfit (r(:,2), theta); %! %! ## Plot their estimated PDFs %! x = [0.01, 0.1:0.2:18]; %! y = gppdf (x, k_sigmaA(1), k_sigmaA(2), theta); %! plot (x, y, "-pc"); %! y = gppdf (x, k_sigmaB(1), k_sigmaB(2), theta); %! plot (x, y, "-sr"); %! hold off %! legend ({"Normalized HIST of sample 1 with k=1 and σ=2", ... %! "Normalized HIST of sample 2 with k=2 and σ=2", ... %! sprintf("PDF for sample 1 with estimated k=%0.2f and σ=%0.2f", ... %! k_sigmaA(1), k_sigmaA(2)), ... %! sprintf("PDF for sample 3 with estimated k=%0.2f and σ=%0.2f", ... %! k_sigmaB(1), k_sigmaB(2))}) %! title ("Three population samples from different generalized Pareto distibutions") %! text (2, 0.7, "Known location parameter θ = 0") %! hold off ## Test output %!test %! k = 0.8937; sigma = 1.3230; theta = 1; %! x = [2.2196, 11.9301, 4.3673, 1.0949, 6.5626, ... %! 1.2109, 1.8576, 1.0039, 12.7917, 2.2590]; %! [hat, ci] = gpfit (x, theta); %! assert (hat, [k, sigma, theta], 1e-4); %! assert (ci, [-0.7750, 0.2437, 1; 2.5624, 7.1820, 1], 1e-4); ## Test input validation %!error gpfit () %!error gpfit (1) %!error gpfit ([0.2, 0.5+i], 0); %!error gpfit (ones (2,2) * 0.5, 0); %!error ... %! gpfit ([0.5, 1.2], [0, 1]); %!error ... %! gpfit ([0.5, 1.2], 5+i); %!error ... %! gpfit ([1:5], 2); %!error gpfit ([0.01:0.1:0.99], 0, 1.2); %!error gpfit ([0.01:0.1:0.99], 0, i); %!error gpfit ([0.01:0.1:0.99], 0, -1); %!error gpfit ([0.01:0.1:0.99], 0, [0.05, 0.01]); %!error %! gpfit ([1 2 3], 0, [], [1 5]) %!error %! gpfit ([1 2 3], 0, [], [1 5 -1]) %!error ... %! gpfit ([1:10], 1, 0.05, [], 5) statistics-release-1.7.3/inst/dist_fit/gplike.m000066400000000000000000000156231475240274700215700ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} gplike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} gplike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} gplike (@var{params}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the generalized Pareto distribution. ## ## @code{@var{nlogL} = gplike (@var{params}, @var{x})} returns the negative ## log-likelihood of the data in @var{x} corresponding to the generalized Pareto ## distribution with (1) shape parameter @var{k}, (2) scale parameter ## @var{sigma}, and (3) location parameter @var{theta} given in the ## three-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = gplike (@var{params}, @var{x})} returns ## the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. @var{acov} ## is based on the observed Fisher's information, not the expected information. ## ## @code{[@dots{}] = gplike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## When @qcode{@var{k} = 0} and @qcode{@var{mu} = 0}, the Generalized Pareto CDF ## is equivalent to the exponential distribution. When @qcode{@var{k} > 0} and ## @code{@var{mu} = @var{k} / @var{k}} the Generalized Pareto is equivalent to ## the Pareto distribution. The mean of the Generalized Pareto is not finite ## when @qcode{@var{k} >= 1} and the variance is not finite when ## @qcode{@var{k} >= 1/2}. When @qcode{@var{k} >= 0}, the Generalized Pareto ## has positive density for @qcode{@var{x} > @var{mu}}, or, when ## @qcode{@var{mu} < 0}, for ## @qcode{0 <= (@var{x} - @var{mu}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpcdf, gpinv, gppdf, gprnd, gpfit, gpstat} ## @end deftypefn function [nlogL, acov] = gplike (params, x, freq) ## Check input arguments if (nargin < 2) error ("gplike: function called with too few input arguments."); endif if (! isvector (x)) error ("gplike: X must be a vector."); endif if (numel (params) != 3) error ("gplike: PARAMS must be a three-element vector."); endif ## Parse FREQ argument or add default if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("gplike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("gplike: FREQ must not contain negative values."); endif ## Expand frequency vector (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Get K, SIGMA, and THETA parameters k = params(1); sigma = params(2); theta = params(3); ## Get sample size and sigma x sz = numel (x); z = (x - theta) ./ sigma; ## For K > 0 if (abs (k) > eps) if (k > 0 || max (z) < -1 / k) sumLn = sum (log1p (k .* z)); nlogL = sz * log (sigma) + (1 + 1 / k) .* sumLn; if (nargout > 1) z_kz = z ./ (1 + k .* z); sumv = sum (z_kz); sumvsq = sum (z_kz .^ 2); nH11 = 2 * sumLn ./ k ^ 3 - ... 2 * sumv ./ k ^ 2 - (1 + 1 / k) .* sumvsq; nH12 = (-sumv + (k + 1) .* sumvsq) ./ sigma; nH22 = (-sz + 2 * (k + 1) .* sumv - ... k * (k + 1) .* sumvsq) ./ sigma ^ 2; acov = [nH22, -nH12; -nH12, nH11] / (nH11 * nH22 - nH12 * nH12); acov = [acov, [0; 0]; 0, 0, 0]; endif else ## The support of the GP when k<0 is 0 < y < abs(sigma/k) nlogL = Inf; if (nargout > 1) acov = [NaN, NaN, 0; NaN, NaN, 0; 0, 0, 0]; endif endif else # For k = 0 nlogL = sz * log (sigma) + sum (z); if (nargout > 1) sumz = sum (z); sumzsq = sum (z .^ 2); sumzcb = sum (z .^ 3); nH11 = (2 / 3) * sumzcb - sumzsq; nH12 = (-sumz + sumzsq) ./ sigma; nH22 = (-sz + 2 * sumz) ./ sigma ^ 2; acov = [nH22, -nH12; -nH12, nH11] / (nH11 * nH22 - nH12 * nH12); acov = [acov, [0; 0]; 0, 0, 0]; endif endif endfunction ## Test output %!test %! k = 0.8937; sigma = 1.3230; theta = 1; %! x = [2.2196, 11.9301, 4.3673, 1.0949, 6.5626, ... %! 1.2109, 1.8576, 1.0039, 12.7917, 2.2590]; %! [nlogL, acov] = gplike ([k, sigma, theta], x); %! assert (nlogL, 21.736, 1e-3); %! assert (acov, [0.7249, -0.7351, 0; -0.7351, 1.3040, 0; 0, 0, 0], 1e-4); %!assert (gplike ([2, 3, 0], 4), 3.047536764863501, 1e-14) %!assert (gplike ([2, 3, 4], 8), 3.047536764863501, 1e-14) %!assert (gplike ([1, 2, 0], 4), 2.890371757896165, 1e-14) %!assert (gplike ([1, 2, 4], 8), 2.890371757896165, 1e-14) %!assert (gplike ([2, 3, 0], [1:10]), 32.57864322725392, 1e-14) %!assert (gplike ([2, 3, 2], [1:10] + 2), 32.57864322725392, 1e-14) %!assert (gplike ([2, 3, 0], [1:10], ones (1,10)), 32.57864322725392, 1e-14) %!assert (gplike ([1, 2, 0], [1:10]), 31.65666282460443, 1e-14) %!assert (gplike ([1, 2, 3], [1:10] + 3), 31.65666282460443, 1e-14) %!assert (gplike ([1, 2, 0], [1:10], ones (1,10)), 31.65666282460443, 1e-14) %!assert (gplike ([1, NaN, 0], [1:10]), NaN) ## Test input validation %!error gplike () %!error gplike (1) %!error gplike ([1, 2, 0], []) %!error gplike ([1, 2, 0], ones (2)) %!error gplike (2, [1:10]) %!error gplike ([2, 3], [1:10]) %!error ... %! gplike ([1, 2, 0], ones (10, 1), ones (8,1)) %!error ... %! gplike ([1, 2, 0], ones (1, 8), [1 1 1 1 1 1 1 -1]) statistics-release-1.7.3/inst/dist_fit/gumbelfit.m000066400000000000000000000223501475240274700222660ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} gumbelfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gumbelfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} gumbelfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} gumbelfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} gumbelfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} gumbelfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for Gumbel distribution. ## ## @code{@var{paramhat} = gumbelfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Gumbel distribution (also known as ## the extreme value or the type I generalized extreme value distribution) given ## in @var{x}. @qcode{@var{paramhat}(1)} is the location parameter, @var{mu}, ## and @qcode{@var{paramhat}(2)} is the scale parameter, @var{beta}. ## ## @code{[@var{paramhat}, @var{paramci}] = gumbelfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = gumbelfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = gumbelfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = gumbelfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = gumbelfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute the maximum likelihood ## estimates. @var{options} is a structure with the following field and its ## default value: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling maxima. For modeling minima, use the alternative ## extreme value fitting function, @code{evfit}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{gumbelcdf, gumbelinv, gumbelpdf, gumbelrnd, gumbellike, gumbelstat, ## evfit} ## @end deftypefn function [paramhat, paramci] = gumbelfit (x, alpha, censor, freq, options) ## Check X for being a double precision vector if (! isvector (x) || ! isa (x, "double")) error ("gumbelfit: X must be a double-precision vector."); endif ## Check that X does not contain missing values (NaNs) if (any (isnan (x))) error ("gumbelfit: X must NOT contain missing values (NaNs)."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("gumbelfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("gumbelfit: X and CENSOR vectors mismatch."); endif ## Parse FREQ argument or add default if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("gumbelfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("gumbelfit: FREQ must not contain negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["gumbelfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Remove zeros and NaNs from frequency vector (if necessary) if (! all (freq == 1)) remove = freq == 0 | isnan (freq); x(remove) = []; censor(remove) = []; freq(remove) = []; endif ## If X is a column vector, make X, CENSOR, and FREQ row vectors if (size (x, 1) > 1) x = x(:)'; censor = censor(:)'; freq = freq(:)'; endif ## Call evfit to do the actual computation on the negative X try [paramhat, paramci] = evfit (-x, alpha, censor, freq, options); catch error ("gumbelfit: no solution for maximum likelihood estimates."); end_try_catch ## Flip sign on estimated parameter MU paramhat(1) = -paramhat(1); ## Flip sign on confidence intervals of parameter MU paramci(:,1) = -flip (paramci(:,1)); endfunction %!demo %! ## Sample 3 populations from different Gumbel distibutions %! rand ("seed", 1); # for reproducibility %! r1 = gumbelrnd (2, 5, 400, 1); %! rand ("seed", 11); # for reproducibility %! r2 = gumbelrnd (-5, 3, 400, 1); %! rand ("seed", 16); # for reproducibility %! r3 = gumbelrnd (14, 8, 400, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 25, 0.32); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 0.28]) %! xlim ([-11, 50]); %! hold on %! %! ## Estimate their MU and BETA parameters %! mu_betaA = gumbelfit (r(:,1)); %! mu_betaB = gumbelfit (r(:,2)); %! mu_betaC = gumbelfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [min(r(:)):max(r(:))]; %! y = gumbelpdf (x, mu_betaA(1), mu_betaA(2)); %! plot (x, y, "-pr"); %! y = gumbelpdf (x, mu_betaB(1), mu_betaB(2)); %! plot (x, y, "-sg"); %! y = gumbelpdf (x, mu_betaC(1), mu_betaC(2)); %! plot (x, y, "-^c"); %! legend ({"Normalized HIST of sample 1 with μ=2 and β=5", ... %! "Normalized HIST of sample 2 with μ=-5 and β=3", ... %! "Normalized HIST of sample 3 with μ=14 and β=8", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f and β=%0.2f", ... %! mu_betaA(1), mu_betaA(2)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f and β=%0.2f", ... %! mu_betaB(1), mu_betaB(2)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f and β=%0.2f", ... %! mu_betaC(1), mu_betaC(2))}) %! title ("Three population samples from different Gumbel distibutions") %! hold off ## Test output %!test %! x = 1:50; %! [paramhat, paramci] = gumbelfit (x); %! paramhat_out = [18.3188, 13.0509]; %! paramci_out = [14.4882, 10.5294; 22.1495, 16.1763]; %! assert (paramhat, paramhat_out, 1e-4); %! assert (paramci, paramci_out, 1e-4); %!test %! x = 1:50; %! [paramhat, paramci] = gumbelfit (x, 0.01); %! paramci_out = [13.2845, 9.8426; 23.3532, 17.3051]; %! assert (paramci, paramci_out, 1e-4); ## Test input validation %!error gumbelfit (ones (2,5)); %!error ... %! gumbelfit (single (ones (1,5))); %!error ... %! gumbelfit ([1, 2, 3, 4, NaN]); %!error gumbelfit ([1, 2, 3, 4, 5], 1.2); %!error ... %! gumbelfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! gumbelfit ([1, 2, 3, 4, 5], 0.05, [], [1 1 0]); %!error %! gamfit ([1, 2, 3], 0.05, [], [1 5 -1]) %!error ... %! gumbelfit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/gumbellike.m000066400000000000000000000137071475240274700224360ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} gumbellike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} gumbellike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} gumbellike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} gumbellike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the extreme value distribution. ## ## @code{@var{nlogL} = gumbellike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Gumbel ## distribution (also known as the extreme value or the type I generalized ## extreme value distribution) with (1) location parameter @var{mu} and (2) ## scale parameter @var{beta} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = gumbellike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. ## ## @code{[@dots{}] = gumbellike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = gumbellike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling maxima. For modeling minima, use the alternative ## extreme value likelihood function, @code{evlike}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{gumbelcdf, gumbelinv, gumbelpdf, gumbelrnd, gumbelfit, gumbelstat, ## evlike} ## @end deftypefn function [nlogL, avar] = gumbellike (params, x, censor, freq) ## Check input arguments and add defaults if (nargin < 2) error ("gumbellike: too few input arguments."); endif if (numel (params) != 2) error ("gumbellike: wrong parameters length."); endif if (! isvector (x)) error ("gumbellike: X must be a vector."); endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("gumbellike: X and CENSOR vectors mismatch."); endif if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("gumbellike: X and FREQ vectors mismatch."); endif ## Get mu and sigma values mu = params(1); sigma = params(2); ## sigma must be positive, otherwise make it NaN if (sigma <= 0) sigma = NaN; endif ## Compute the individual log-likelihood terms. Force a log(0)==-Inf for ## x from extreme right tail, instead of getting exp(Inf-Inf)==NaN. z = (x - mu) ./ sigma; expz = exp (z); L = (z - log (sigma)) .* (1 - censor) - expz; L(z == Inf) = -Inf; ## Neg-log-like is the sum of the individual contributions nlogL = -sum (freq .* L); ## Compute the negative hessian at the parameter values. ## Invert to get the observed information matrix. if (nargout == 2) unc = (1-censor); nH11 = sum(freq .* expz); nH12 = sum(freq .* ((z + 1) .* expz - unc)); nH22 = sum(freq .* (z .* (z+2) .* expz - ((2 .* z + 1) .* unc))); avar = (sigma .^ 2) * ... [nH22 -nH12; -nH12 nH11] / (nH11 * nH22 - nH12 * nH12); endif endfunction ## Test output %!test %! x = 1:50; %! [nlogL, avar] = gumbellike ([2.3, 1.2], x); %! avar_out = [-1.2778e-13, 3.1859e-15; 3.1859e-15, -7.9430e-17]; %! assert (nlogL, 3.242264755689906e+17, 1e-14); %! assert (avar, avar_out, 1e-3); %!test %! x = 1:50; %! [nlogL, avar] = gumbellike ([2.3, 1.2], x * 0.5); %! avar_out = [-7.6094e-05, 3.9819e-06; 3.9819e-06, -2.0836e-07]; %! assert (nlogL, 481898704.0472211, 1e-6); %! assert (avar, avar_out, 1e-3); %!test %! x = 1:50; %! [nlogL, avar] = gumbellike ([21, 15], x); %! avar_out = [11.73913876598908, -5.9546128523121216; ... %! -5.954612852312121, 3.708060045170236]; %! assert (nlogL, 223.7612479380652, 1e-13); %! assert (avar, avar_out, 1e-14); ## Test input validation %!error gumbellike ([12, 15]); %!error gumbellike ([12, 15, 3], [1:50]); %!error gumbellike ([12, 3], ones (10, 2)); %!error gumbellike ([12, 15], [1:50], [1, 2, 3]); %!error gumbellike ([12, 15], [1:50], [], [1, 2, 3]); statistics-release-1.7.3/inst/dist_fit/hnfit.m000066400000000000000000000162501475240274700214220ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{paramhat}, @var{paramci}] =} hnfit (@var{x}, @var{mu}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} hnfit (@var{x}, @var{mu}, @var{alpha}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} hnfit (@var{x}, @var{mu}, @var{alpha}, @var{freq}) ## ## Estimate parameters and confidence intervals for the half-normal distribution. ## ## @code{@var{paramhat} = hnfit (@var{x}, @var{mu})} returns the maximum ## likelihood estimates of the parameters of the half-normal distribution given ## the data in vector @var{x} and the location parameter @var{mu}. ## @qcode{@var{paramhat}(1)} is the location parameter, @var{mu}, and ## @qcode{@var{paramhat}(2)} is the scale parameter, @var{sigma}. Although ## @var{mu} is returned in the estimated @var{paramhat}, @code{hnfit} does not ## estimate the location parameter @var{mu}, and it must be assumed to be known, ## given as a fixed parameter in input argument @var{mu}. ## ## @code{[@var{paramhat}, @var{paramci}] = hnfit (@var{x}, @var{mu})} returns ## the 95% confidence intervals for the estimated scale parameter @var{sigma}. ## The first colummn of @var{paramci} includes the location parameter @var{mu} ## without any confidence bounds. ## ## @code{[@dots{}] = hnfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals of the estimated ## scale parameter. By default, the optional argument @var{alpha} is 0.05 ## corresponding to 95% confidence intervals. ## ## @code{[@dots{}] = hnfit (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## The half-normal CDF is only defined for @qcode{@var{x} >= @var{mu}}. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{hncdf, hninv, hnpdf, hnrnd, hnlike, hnstat} ## @end deftypefn function [paramhat, paramci] = hnfit (x, mu, alpha, freq) ## Check for valid number of input arguments if (nargin < 2) error ("hnfit: function called with too few input arguments."); endif ## Check X for being a vector if (isempty (x)) phat = nan (1, 2, class (x)); pci = nan (2, 2, class (x)); return elseif (! isvector (x) || ! isreal (x)) error ("hnfit: X must be a vector of real values."); endif ## Check for MU being a scalar real value if (! isscalar (mu) || ! isreal (mu)) error ("hnfit: MU must be a real scalar value."); endif ## Check X >= MU if (any (x < mu)) error ("hnfit: X cannot contain values less than MU."); endif ## Parse ALPHA argument or add default if (nargin < 3 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("hnfit: wrong value for ALPHA."); endif ## Parse FREQ argument or add default if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("hnfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("hnfit: FREQ must not contain negative values."); endif ## Expand frequency vector (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Estimate parameters sz = numel (x); x = x - mu; sigmahat = sqrt (sum (x .* x) ./ sz); paramhat = [mu, sigmahat]; ## Compute confidence intervals if (nargout == 2) chi2cr = chi2inv ([alpha/2, 1-alpha/2], sz); shatlo = sigmahat * sqrt (sz / chi2inv (1 - alpha / 2, sz)); shathi = sigmahat * sqrt (sz / chi2inv (alpha / 2, sz)); paramci = [mu, shatlo; mu, shathi]; endif endfunction %!demo %! ## Sample 2 populations from different half-normal distibutions %! rand ("seed", 1); # for reproducibility %! r1 = hnrnd (0, 5, 5000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = hnrnd (0, 2, 5000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.5:20], 1); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their shape parameters %! mu_sigmaA = hnfit (r(:,1), 0); %! mu_sigmaB = hnfit (r(:,2), 0); %! %! ## Plot their estimated PDFs %! x = [0:0.2:10]; %! y = hnpdf (x, mu_sigmaA(1), mu_sigmaA(2)); %! plot (x, y, "-pr"); %! y = hnpdf (x, mu_sigmaB(1), mu_sigmaB(2)); %! plot (x, y, "-sg"); %! xlim ([0, 10]) %! ylim ([0, 0.5]) %! legend ({"Normalized HIST of sample 1 with μ=0 and σ=5", ... %! "Normalized HIST of sample 2 with μ=0 and σ=2", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f and σ=%0.2f", ... %! mu_sigmaA(1), mu_sigmaA(2)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f and σ=%0.2f", ... %! mu_sigmaB(1), mu_sigmaB(2))}) %! title ("Two population samples from different half-normal distibutions") %! hold off ## Test output %!test %! x = 1:20; %! [paramhat, paramci] = hnfit (x, 0); %! assert (paramhat, [0, 11.9791], 1e-4); %! assert (paramci, [0, 9.1648; 0, 17.2987], 1e-4); %!test %! x = 1:20; %! [paramhat, paramci] = hnfit (x, 0, 0.01); %! assert (paramci, [0, 8.4709; 0, 19.6487], 1e-4); ## Test input validation %!error hnfit () %!error hnfit (1) %!error hnfit ([0.2, 0.5+i], 0); %!error hnfit (ones (2,2) * 0.5, 0); %!error ... %! hnfit ([0.5, 1.2], [0, 1]); %!error ... %! hnfit ([0.5, 1.2], 5+i); %!error ... %! hnfit ([1:5], 2); %!error hnfit ([0.01:0.1:0.99], 0, 1.2); %!error hnfit ([0.01:0.1:0.99], 0, i); %!error hnfit ([0.01:0.1:0.99], 0, -1); %!error hnfit ([0.01:0.1:0.99], 0, [0.05, 0.01]); %!error %! hnfit ([1 2 3], 0, [], [1 5]) %!error %! hnfit ([1 2 3], 0, [], [1 5 -1]) statistics-release-1.7.3/inst/dist_fit/hnlike.m000066400000000000000000000113721475240274700215640ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} hnlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} hnlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} hnlike (@var{params}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the half-normal distribution. ## ## @code{@var{nlogL} = hnlike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the half-normal ## distribution with (1) location parameter @var{mu} and (2) scale parameter ## @var{sigma} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = hnlike (@var{params}, @var{x})} returns ## the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = hnlike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## The half-normal CDF is only defined for @qcode{@var{x} >= @var{mu}}. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{hncdf, hninv, hnpdf, hnrnd, hnfit, hnstat} ## @end deftypefn function [nlogL, acov] = hnlike (params, x, freq) ## Check input arguments and add defaults if (nargin < 2) error ("hnlike: function called with too few input arguments."); endif if (numel (params) != 2) error ("hnlike: wrong parameters length."); endif ## Check X for being a vector if (isempty (x)) phat = nan (1, 2, class (x)); pci = nan (2, 2, class (x)); return elseif (! isvector (x) || ! isreal (x)) error ("hnlike: X must be a vector of real values."); endif ## Parse FREQ argument or add default if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("hnlike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("hnlike: FREQ must not contain negative values."); endif ## Expand frequency vector (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Get MU and SIGMA parameters mu = params(1); sigma = params(2); ## Force X to column vector x = x(:); ## Return NaN for out of range parameters or data. sigma(sigma <= 0) = NaN; x(x < mu) = NaN; z = (x - mu) ./ sigma; ## Sum up the individual log-likelihood terms nlogL = -sum (-0.5 .* z .* z - log (sqrt (pi ./ 2) .* sigma)); ## Compute asymptotic covariance (if requested) if (nargout == 2) nH = -sum (1 - 3 .* z .* z); avar = (sigma .^ 2) ./ nH; acov = [0, 0; 0, avar]; endif endfunction ## Test output %!test %! x = 1:20; %! paramhat = hnfit (x, 0); %! [nlogL, acov] = hnlike (paramhat, x); %! assert (nlogL, 64.179177404891300, 1e-14); %!test %! x = 1:20; %! paramhat = hnfit (x, 0); %! [nlogL, acov] = hnlike (paramhat, x, ones (1, 20)); %! assert (nlogL, 64.179177404891300, 1e-14); ## Test input validation %!error ... %! hnlike ([12, 15]); %!error hnlike ([12, 15, 3], [1:50]); %!error hnlike ([3], [1:50]); %!error ... %! hnlike ([0, 3], ones (2)); %!error ... %! hnlike ([0, 3], [1, 2, 3, 4, 5+i]); %!error ... %! hnlike ([1, 2], ones (10, 1), ones (8,1)) %!error ... %! hnlike ([1, 2], ones (1, 8), [1 1 1 1 1 1 1 -1]) statistics-release-1.7.3/inst/dist_fit/invgfit.m000066400000000000000000000230501475240274700217540ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} invgfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} invgfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} invgfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} invgfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} invgfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} invgfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate mean and confidence intervals for the inverse Gaussian distribution. ## ## @code{@var{mu0} = invgfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the inverse Gaussian distribution given the ## data in @var{x}. @qcode{@var{paramhat}(1)} is the scale parameter, @var{mu}, ## and @qcode{@var{paramhat}(2)} is the shape parameter, @var{lambda}. ## ## @code{[@var{paramhat}, @var{paramci}] = invgfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = invgfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = invgfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = invgfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = invgfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invgcdf, invginv, invgpdf, invgrnd, invglike, invgstat} ## @end deftypefn function [paramhat, paramci] = invgfit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("invgfit: X must be a vector."); elseif (any (x <= 0)) error ("invgfit: X must contain only positive values."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("invgfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("invgfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("invgfit: X and FREQ vectors mismatch."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["invgfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif n_censored = sum (freq .* censor); ## Compute parameters for uncensored data if (n_censored == 0) ## Expand frequency vector (MATLAB does not do this, in R2018 at least) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor xbar = mean (xf); paramhat = [xbar, (1 ./ mean (1 ./ x - 1 ./ xbar))]; else ## Use MLEs of the uncensored data as initial searching values x_uncensored = x(censor == 0); mu0 = mean (x_uncensored); lambda0 = 1 ./ mean (1 ./ x_uncensored - 1 ./ mu0); x0 = [mu0, lambda0]; ## Minimize negative log-likelihood to estimate parameters f = @(params) invglike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, x0, options); ## Force positive parameter values paramhat = abs (paramhat); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) msg = "invgfit: maximum number of function evaluations are exceeded."; warning (msg); elseif (output.iterations >= options.MaxIter) warning ("invgfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("invgfit: no solution."); endif endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = invglike (paramhat, x, censor, freq); ## Get standard errors stderr = sqrt (diag (acov))'; ## Get normal quantiles probs = [alpha/2; 1-alpha/2]; ## Compute CI paramci = norminv([probs, probs], [paramhat; paramhat], [stderr; stderr]); endif endfunction %!demo %! ## Sample 3 populations from different inverse Gaussian distibutions %! rand ("seed", 5); randn ("seed", 5); # for reproducibility %! r1 = invgrnd (1, 0.2, 2000, 1); %! rand ("seed", 2); randn ("seed", 2); # for reproducibility %! r2 = invgrnd (1, 3, 2000, 1); %! rand ("seed", 7); randn ("seed", 7); # for reproducibility %! r3 = invgrnd (3, 1, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.1:0.1:3.2], 9); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 3]); %! xlim ([0, 3]); %! hold on %! %! ## Estimate their MU and LAMBDA parameters %! mu_lambdaA = invgfit (r(:,1)); %! mu_lambdaB = invgfit (r(:,2)); %! mu_lambdaC = invgfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0:0.1:3]; %! y = invgpdf (x, mu_lambdaA(1), mu_lambdaA(2)); %! plot (x, y, "-pr"); %! y = invgpdf (x, mu_lambdaB(1), mu_lambdaB(2)); %! plot (x, y, "-sg"); %! y = invgpdf (x, mu_lambdaC(1), mu_lambdaC(2)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with μ=1 and λ=0.5", ... %! "Normalized HIST of sample 2 with μ=2 and λ=0.3", ... %! "Normalized HIST of sample 3 with μ=4 and λ=0.5", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f and λ=%0.2f", ... %! mu_lambdaA(1), mu_lambdaA(2)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f and λ=%0.2f", ... %! mu_lambdaB(1), mu_lambdaB(2)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f and λ=%0.2f", ... %! mu_lambdaC(1), mu_lambdaC(2))}) %! title ("Three population samples from different inverse Gaussian distibutions") %! hold off ## Test output %!test %! paramhat = invgfit ([1:50]); %! paramhat_out = [25.5, 19.6973]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = invgfit ([1:5]); %! paramhat_out = [3, 8.1081]; %! assert (paramhat, paramhat_out, 1e-4); ## Test input validation %!error invgfit (ones (2,5)); %!error invgfit ([-1 2 3 4]); %!error invgfit ([1, 2, 3, 4, 5], 1.2); %!error invgfit ([1, 2, 3, 4, 5], 0); %!error invgfit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! invgfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! invgfit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! invgfit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! invgfit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! invgfit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/invglike.m000066400000000000000000000163451475240274700221270ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} invglike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} invglike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} invglike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} invglike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the inverse Gaussian distribution. ## ## @code{@var{nlogL} = invglike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the inverse Gaussian ## distribution with (1) scale parameter @var{mu} and (2) shape parameter ## @var{lambda} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = invglike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = invglike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = invglike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invgcdf, invginv, invgpdf, invgrnd, invgfit, invgstat} ## @end deftypefn function [nlogL, acov] = invglike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("invglike: function called with too few input arguments."); endif if (! isvector (x)) error ("invglike: X must be a vector."); endif if (any (x < 0)) error ("invglike: X must have positive values."); endif if (length (params) != 2) error ("invglike: PARAMS must be a two-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("invglike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("invglike: X and FREQ vector mismatch."); endif ## Get parameters mu = params(1); lambda = params(2); L = 0.5 .* log (lambda ./ (2 * pi)) - 1.5 .* log (x) ... -lambda .* (x ./ mu - 1) .^ 2 ./ (2 .* x); n_censored = sum (freq .* censor); ## Handle censored data if (n_censored > 0) censored = (censor == 1); x_censored = x(censored); sqrt_lx = sqrt (lambda ./ x_censored); z_censored = -(x_censored ./ mu - 1) .* sqrt_lx; w_censored = -(x_censored ./ mu + 1) .* sqrt_lx; Fz = 0.5 .* erfc (-z_censored ./ sqrt (2)); Fw = 0.5 .* erfc (-w_censored ./ sqrt (2)); S_censored = Fz - exp (2 .* lambda ./ mu) .* Fw; L(censored) = log (S_censored); endif ## Sum up the neg log likelihood nlogL = -sum (freq .* L); ## Compute asymptotic covariance if (nargout > 1) ## Compute first order central differences of the log-likelihood gradient dp = 0.0001 .* max (abs (params), 1); ngrad_p1 = invg_grad (params + [dp(1), 0], x, censor, freq); ngrad_m1 = invg_grad (params - [dp(1), 0], x, censor, freq); ngrad_p2 = invg_grad (params + [0, dp(2)], x, censor, freq); ngrad_m2 = invg_grad (params - [0, dp(2)], x, censor, freq); ## Compute negative Hessian by normalizing the differences by the increment nH = [(ngrad_p1(:) - ngrad_m1(:))./(2 * dp(1)), ... (ngrad_p2(:) - ngrad_m2(:))./(2 * dp(2))]; ## Force neg Hessian being symmetric nH = 0.5 .* (nH + nH'); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("invglike: non positive definite Hessian matrix."); acov = NaN (2); return endif ## ACOV estimate is the negative inverse of the Hessian. Rinv = inv (R); acov = Rinv * Rinv; endif endfunction ## Helper function for computing negative gradient function ngrad = invg_grad (params, x, censor, freq) mu = params(1); lambda = params(2); dL1 = lambda .* (x - mu) ./ mu .^ 3; dL2 = 1 ./ (2 .* lambda) - (x ./ mu - 1) .^ 2 ./ (2 .* x); n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); x_censored = x(censored); sqrt_lx = sqrt (lambda ./ x_censored); exp_lmu = exp (2 .* lambda ./ mu); z_censored = -(x_censored ./ mu - 1) .* sqrt_lx; w_censored = -(x_censored ./ mu + 1) .* sqrt_lx; Fw = 0.5 .* erfc (-w_censored ./ sqrt (2)); fz = exp (-0.5 .* z_censored .^ 2) ./ sqrt (2 .* pi); fw = exp (-0.5 .* w_censored .^ 2) ./ sqrt (2 .* pi); dS1cen = (fz - exp_lmu .* fw) .* (x_censored ./ mu .^ 2) .* sqrt_lx ... + 2 .* Fw .* exp_lmu .* lambda ./ mu .^ 2; dS2cen = 0.5 .* (fz .* z_censored - exp_lmu .* fw .* w_censored) ... ./ lambda - 2 .* Fw .* exp_lmu ./ mu; dL1(censored) = dS1cen ./ Scen; dL2(censored) = dS2cen ./ Scen; endif ngrad = -[sum(freq .* dL1), sum(freq .* dL2)]; endfunction ## Test results %!test %! nlogL = invglike ([25.5, 19.6973], [1:50]); %! assert (nlogL, 219.1516, 1e-4); %!test %! nlogL = invglike ([3, 8.1081], [1:5]); %! assert (nlogL, 9.0438, 1e-4); ## Test input validation %!error invglike (3.25) %!error invglike ([5, 0.2], ones (2)) %!error invglike ([5, 0.2], [-1, 3]) %!error ... %! invglike ([1, 0.2, 3], [1, 3, 5, 7]) %!error ... %! invglike ([1.5, 0.2], [1:5], [0, 0, 0]) %!error ... %! invglike ([1.5, 0.2], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! invglike ([1.5, 0.2], [1:5], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/logifit.m000066400000000000000000000226621475240274700217530ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} logifit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} logifit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} logifit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} logifit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} logifit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} logifit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate mean and confidence intervals for the logistic distribution. ## ## @code{@var{mu0} = logifit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the logistic distribution given the data in ## @var{x}. @qcode{@var{paramhat}(1)} is the scale parameter, @var{mu}, and ## @qcode{@var{paramhat}(2)} is the shape parameter, @var{s}. ## ## @code{[@var{paramhat}, @var{paramci}] = logifit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = logifit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = logifit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = logifit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = logifit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logicdf, logiinv, logipdf, logirnd, logilike, logistat} ## @end deftypefn function [paramhat, paramci] = logifit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("logifit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("logifit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("logifit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("logifit: X and FREQ vectors mismatch."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["logifit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Use MLEs of the uncensored data as initial searching values x_uncensored = x(censor == 0); mu0 = mean (x_uncensored); s0 = std (x_uncensored) .* sqrt (3) ./ pi; x0 = [mu0, s0]; ## Minimize negative log-likelihood to estimate parameters f = @(params) logilike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, x0, options); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) msg = "logifit: maximum number of function evaluations are exceeded."; warning (msg); elseif (output.iterations >= options.MaxIter) warning ("logifit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("logifit: no solution."); endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = logilike (paramhat, x, censor, freq); ## Get standard errors se = sqrt (diag (acov))'; ## Get normal quantiles probs = [alpha/2; 1-alpha/2]; ## Compute muci using a normal approximation paramci(:,1) = norminv (probs, paramhat(1), se(1)); ## Compute sci using a normal approximation for log (s) and transform back paramci(:,2) = exp (norminv (probs, log (paramhat(2)), log (se(2)))); endif endfunction %!demo %! ## Sample 3 populations from different logistic distibutions %! rand ("seed", 5) # for reproducibility %! r1 = logirnd (2, 1, 2000, 1); %! rand ("seed", 2) # for reproducibility %! r2 = logirnd (5, 2, 2000, 1); %! rand ("seed", 7) # for reproducibility %! r3 = logirnd (9, 4, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [-6:20], 1); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 0.3]); %! xlim ([-5, 20]); %! hold on %! %! ## Estimate their MU and LAMBDA parameters %! mu_sA = logifit (r(:,1)); %! mu_sB = logifit (r(:,2)); %! mu_sC = logifit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [-5:0.5:20]; %! y = logipdf (x, mu_sA(1), mu_sA(2)); %! plot (x, y, "-pr"); %! y = logipdf (x, mu_sB(1), mu_sB(2)); %! plot (x, y, "-sg"); %! y = logipdf (x, mu_sC(1), mu_sC(2)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with μ=1 and s=0.5", ... %! "Normalized HIST of sample 2 with μ=2 and s=0.3", ... %! "Normalized HIST of sample 3 with μ=4 and s=0.5", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f and s=%0.2f", ... %! mu_sA(1), mu_sA(2)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f and s=%0.2f", ... %! mu_sB(1), mu_sB(2)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f and s=%0.2f", ... %! mu_sC(1), mu_sC(2))}) %! title ("Three population samples from different logistic distibutions") %! hold off ## Test output %!test %! paramhat = logifit ([1:50]); %! paramhat_out = [25.5, 8.7724]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = logifit ([1:5]); %! paramhat_out = [3, 0.8645]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = logifit ([1:6], [], [], [1 1 1 1 1 0]); %! paramhat_out = [3, 0.8645]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = logifit ([1:5], [], [], [1 1 1 1 2]); %! paramhat_out = logifit ([1:5, 5]); %! assert (paramhat, paramhat_out, 1e-4); ## Test input validation %!error logifit (ones (2,5)); %!error logifit ([1, 2, 3, 4, 5], 1.2); %!error logifit ([1, 2, 3, 4, 5], 0); %!error logifit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! logifit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! logifit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! logifit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! logifit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! logifit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/logilike.m000066400000000000000000000153611475240274700221130ustar00rootroot00000000000000## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} logilike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} logilike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} logilike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} logilike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the logistic distribution. ## ## @code{@var{nlogL} = logilike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the logistic ## distribution with (1) location parameter @var{mu} and (2) scale parameter ## @var{sigma} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = logilike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = logilike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = logilike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logicdf, logiinv, logipdf, logirnd, logifit, logistat} ## @end deftypefn function [nlogL, acov] = logilike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("logilike: function called with too few input arguments."); endif if (! isvector (x)) error ("logilike: X must be a vector."); endif if (length (params) != 2) error ("logilike: PARAMS must be a two-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("logilike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("logilike: X and FREQ vector mismatch."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Compute the negative loglikelihood nlogL = loginll (params, x, censor, freq); ## Compute the negative hessian and invert to get the information matrix if (nargout > 1) ei = zeros (1, 2); ej = zeros (1, 2); nH = zeros (2, 2); dp = (eps ^ (1/4)) .* max (abs (params), 1); for i = 1:2 ei(i) = dp(i); for j = 1:(i-1) ej(j) = dp(j); ## Four-point central difference for mixed second partials nH(i,j) = loginll (params+ei+ej, x, censor, freq) ... - loginll (params+ei-ej, x, censor, freq) ... - loginll (params-ei+ej, x, censor, freq) ... + loginll (params-ei-ej, x, censor, freq); ej(j) = 0; endfor ## Five-point central difference for pure second partial nH(i,i) = - loginll (params+2*ei, x, censor, freq) ... + 16 * loginll (params+ei, x, censor, freq) - 30 * nlogL ... + 16 * loginll (params-ei, x, censor, freq) ... - loginll (params-2*ei, x, censor, freq); ei(i) = 0; endfor ## Fill in the upper triangle nH = nH + triu (nH', 1); ## Normalize the second differences to get derivative estimates nH = nH ./ (4 .* dp(:) * dp(:)' + diag (8 * dp(:) .^ 2)); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("logilike: non positive definite Hessian matrix."); acov = NaN (2); return endif ## ACOV estimate is the negative inverse of the Hessian Rinv = inv (R); acov = Rinv * Rinv'; endif endfunction ## Helper function for computing negative loglikelihood function nlogL = loginll (params, x, censor, freq) ## Get parameters mu = params(1); sigma = params(2); ## Compute intermediate values z = (x - mu) ./ sigma; logclogitz = log (1 ./ (1 + exp (z))); k = (z > 700); if (any (k)) logclogitz(k) = z(k); endif L = z + 2 .* logclogitz - log (sigma); n_censored = sum (freq .* censor); ## Handle censored data if (n_censored > 0) censored = (censor == 1); L(censored) = logclogitz(censored); endif ## Sum up the neg log likelihood if (sigma < 0) nlogL = Inf; else nlogL = -sum (freq .* L); endif endfunction ## Test results %!test %! nlogL = logilike ([25.5, 8.7725], [1:50]); %! assert (nlogL, 206.6769, 1e-4); %!test %! nlogL = logilike ([3, 0.8645], [1:5]); %! assert (nlogL, 9.0699, 1e-4); ## Test input validation %!error logilike (3.25) %!error logilike ([5, 0.2], ones (2)) %!error ... %! logilike ([1, 0.2, 3], [1, 3, 5, 7]) %!error ... %! logilike ([1.5, 0.2], [1:5], [0, 0, 0]) %!error ... %! logilike ([1.5, 0.2], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! logilike ([1.5, 0.2], [1:5], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/loglfit.m000066400000000000000000000240361475240274700217530ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} loglfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} loglfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} loglfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} loglfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} loglfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} loglfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate mean and confidence intervals for the log-logistic distribution. ## ## @code{@var{mu0} = loglfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the log-logistic distribution given the data ## in @var{x}. @qcode{@var{paramhat}(1)} is the mean parameter, @var{mu}, and ## @qcode{@var{paramhat}(2)} is the scale parameter, @var{sigma}. ## ## @code{[@var{paramhat}, @var{paramci}] = loglfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = loglfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = loglfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = loglfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = loglfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{loglcdf, loglinv, loglpdf, loglrnd, logllike, loglstat} ## @end deftypefn function [paramhat, paramci] = loglfit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("loglfit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("loglfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("loglfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("loglfit: X and FREQ vectors mismatch."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["loglfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Use MLEs of the uncensored data as initial searching values logx_uncensored = log (x(censor == 0)); a0 = mean (logx_uncensored); b0 = 1 ./ (std (logx_uncensored) .* sqrt (3) ./ pi); x0 = [a0, b0]; ## Minimize negative log-likelihood to estimate parameters f = @(params) logllike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, x0, options); ## Force positive parameter values paramhat = abs (paramhat); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) msg = "loglfit: maximum number of function evaluations are exceeded."; warning (msg); elseif (output.iterations >= options.MaxIter) warning ("loglfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("loglfit: no solution."); endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = logllike (paramhat, x, censor, freq); ## Get standard errors se = sqrt (diag (acov))'; ## Get normal quantiles probs = [alpha/2; 1-alpha/2]; ## Compute muci using a normal approximation paramci(:,1) = norminv (probs, paramhat(1), se(1)); ## Compute sci using a normal approximation for log (s) and transform back paramci(:,2) = exp (norminv (probs, log (paramhat(2)), se(2)/paramhat(2))); endif endfunction %!demo %! ## Sample 3 populations from different log-logistic distibutions %! rand ("seed", 5) # for reproducibility %! r1 = loglrnd (0, 1, 2000, 1); %! rand ("seed", 2) # for reproducibility %! r2 = loglrnd (0, 0.5, 2000, 1); %! rand ("seed", 7) # for reproducibility %! r3 = loglrnd (0, 0.125, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.05:0.1:2.5], 10); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 3.5]); %! xlim ([0, 2.0]); %! hold on %! %! ## Estimate their MU and LAMBDA parameters %! a_bA = loglfit (r(:,1)); %! a_bB = loglfit (r(:,2)); %! a_bC = loglfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0.01:0.1:2.01]; %! y = loglpdf (x, a_bA(1), a_bA(2)); %! plot (x, y, "-pr"); %! y = loglpdf (x, a_bB(1), a_bB(2)); %! plot (x, y, "-sg"); %! y = loglpdf (x, a_bC(1), a_bC(2)); %! plot (x, y, "-^c"); %! legend ({"Normalized HIST of sample 1 with α=1 and β=1", ... %! "Normalized HIST of sample 2 with α=1 and β=2", ... %! "Normalized HIST of sample 3 with α=1 and β=8", ... %! sprintf("PDF for sample 1 with estimated α=%0.2f and β=%0.2f", ... %! a_bA(1), a_bA(2)), ... %! sprintf("PDF for sample 2 with estimated α=%0.2f and β=%0.2f", ... %! a_bB(1), a_bB(2)), ... %! sprintf("PDF for sample 3 with estimated α=%0.2f and β=%0.2f", ... %! a_bC(1), a_bC(2))}) %! title ("Three population samples from different log-logistic distibutions") %! hold off ## Test output %!test %! [paramhat, paramci] = loglfit ([1:50]); %! paramhat_out = [3.09717, 0.468525]; %! paramci_out = [2.87261, 0.370616; 3.32174, 0.5923]; %! assert (paramhat, paramhat_out, 1e-5); %! assert (paramci, paramci_out, 1e-5); %!test %! paramhat = loglfit ([1:5]); %! paramhat_out = [1.01124, 0.336449]; %! assert (paramhat, paramhat_out, 1e-5); %!test %! paramhat = loglfit ([1:6], [], [], [1 1 1 1 1 0]); %! paramhat_out = [1.01124, 0.336449]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = loglfit ([1:5], [], [], [1 1 1 1 2]); %! paramhat_out = loglfit ([1:5, 5]); %! assert (paramhat, paramhat_out, 1e-4); ## Test input validation %!error loglfit (ones (2,5)); %!error loglfit ([1, 2, 3, 4, 5], 1.2); %!error loglfit ([1, 2, 3, 4, 5], 0); %!error loglfit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! loglfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! loglfit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! loglfit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! loglfit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! loglfit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/logllike.m000066400000000000000000000163371475240274700221220ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} logllike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} logllike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} logllike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} logllike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the log-logistic distribution. ## ## @code{@var{nlogL} = logllike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the log-logistic ## distribution with (1) scale parameter @var{a} and (2) shape parameter @var{b} ## given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = logllike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = logllike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = logllike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{loglcdf, loglinv, loglpdf, loglrnd, loglfit, loglstat} ## @end deftypefn function [nlogL, acov] = logllike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("logllike: function called with too few input arguments."); endif if (! isvector (x)) error ("logllike: X must be a vector."); endif if (length (params) != 2) error ("logllike: PARAMS must be a two-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("logllike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("logllike: X and FREQ vector mismatch."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Compute the negative loglikelihood nlogL = loglnll (params, x, censor, freq); ## Compute the negative hessian and invert to get the information matrix if (nargout > 1) ei = zeros (1, 2); ej = zeros (1, 2); nH = zeros (2, 2); dp = (eps ^ (1/4)) .* max (abs (params), 1); for i = 1:2 ei(i) = dp(i); for j = 1:(i-1) ej(j) = dp(j); ## Four-point central difference for mixed second partials nH(i,j) = loglnll (params+ei+ej, x, censor, freq) ... - loglnll (params+ei-ej, x, censor, freq) ... - loglnll (params-ei+ej, x, censor, freq) ... + loglnll (params-ei-ej, x, censor, freq); ej(j) = 0; endfor ## Five-point central difference for pure second partial nH(i,i) = - loglnll (params+2*ei, x, censor, freq) ... + 16 * loglnll (params+ei, x, censor, freq) - 30 * nlogL ... + 16 * loglnll (params-ei, x, censor, freq) ... - loglnll (params-2*ei, x, censor, freq); ei(i) = 0; endfor ## Fill in the upper triangle nH = nH + triu (nH', 1); ## Normalize the second differences to get derivative estimates nH = nH ./ (4 .* dp(:) * dp(:)' + diag (8 * dp(:) .^ 2)); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("logllike: non positive definite Hessian matrix."); acov = NaN (2); return endif ## ACOV estimate is the negative inverse of the Hessian Rinv = inv (R); acov = Rinv * Rinv'; endif endfunction ## Helper function for computing negative loglikelihood function nlogL = loglnll (params, x, censor, freq) ## Get parameters mu = params(1); sigma = params(2); ## Compute intermediate values z = (log (x) - mu) ./ sigma; logclogitz = log (1 ./ (1 + exp (z))); k = (z > 708); if (any (k)) logclogitz(k) = z(k); endif L = z + 2 .* logclogitz - log (sigma) - log (x); n_censored = sum (freq .* censor); ## Handle censored data if (n_censored > 0) censored = (censor == 1); L(censored) = logclogitz(censored); endif ## Sum up the neg log likelihood nlogL = -sum (freq .* L); endfunction ## Test output %!test %! [nlogL, acov] = logllike ([3.09717, 0.468525], [1:50]); %! assert (nlogL, 211.2965, 1e-4); %! assert (acov, [0.0131, -0.0007; -0.0007, 0.0031], 1e-4); %!test %! [nlogL, acov] = logllike ([1.01124, 0.336449], [1:5]); %! assert (nlogL, 9.2206, 1e-4); %! assert (acov, [0.0712, -0.0032; -0.0032, 0.0153], 1e-4); ## Test input validation %!error logllike (3.25) %!error logllike ([5, 0.2], ones (2)) %!error ... %! logllike ([1, 0.2, 3], [1, 3, 5, 7]) %!error ... %! logllike ([1.5, 0.2], [1:5], [0, 0, 0]) %!error ... %! logllike ([1.5, 0.2], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! logllike ([1.5, 0.2], [1:5], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/lognfit.m000066400000000000000000000211171475240274700217520ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} lognfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} lognfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} lognfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} lognfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} lognfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} lognfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the lognormal distribution. ## ## @code{@var{paramhat} = lognfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the lognormal distribution given the data in ## vector @var{x}. @qcode{@var{paramhat}([1, 2])} corresponds to the mean and ## standard deviation, respectively, of the associated normal distribution. ## ## If a random variable follows this distribution, its logarithm is normally ## distributed with mean @var{mu} and standard deviation @var{sigma}. ## ## @code{[@var{paramhat}, @var{paramci}] = lognfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = lognfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = lognfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = lognfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = lognfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## With no censor, the estimate of the standard deviation, ## @qcode{@var{paramhat}(2)}, is the square root of the unbiased estimate of the ## variance of @qcode{log (@var{x})}. With censored data, the maximum ## likelihood estimate is returned. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logncdf, logninv, lognpdf, lognrnd, lognlike, lognstat} ## @end deftypefn function [paramhat, paramci] = lognfit (x, alpha, censor, freq, options) ## Check X for valid data if (! isvector (x) || ! isnumeric (x) || any (x <= 0)) error ("lognfit: X must be a numeric vector of positive values."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("lognfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = []; elseif (! isequal (size (x), size (censor))) error ("lognfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = []; elseif (! isequal (size(x), size(freq))) error ("lognfit: X and FREQ vectors mismatch."); endif ## Check options structure or add defaults if (nargin > 4 && ! isempty (options)) if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["lognfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif else options = []; endif ## Fit a normal distribution to the logged data if (nargout <= 1) [muhat, sigmahat] = normfit (log (x), alpha, censor, freq, options); paramhat = [muhat, sigmahat]; else [muhat, sigmahat, muci, sigmaci] = normfit (log (x), alpha, ... censor, freq, options); paramhat = [muhat, sigmahat]; paramci = [muci, sigmaci]; endif endfunction %!demo %! ## Sample 3 populations from 3 different log-normal distibutions %! randn ("seed", 1); # for reproducibility %! r1 = lognrnd (0, 0.25, 1000, 1); %! randn ("seed", 2); # for reproducibility %! r2 = lognrnd (0, 0.5, 1000, 1); %! randn ("seed", 3); # for reproducibility %! r3 = lognrnd (0, 1, 1000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 30, 2); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! hold on %! %! ## Estimate their mu and sigma parameters %! mu_sigmaA = lognfit (r(:,1)); %! mu_sigmaB = lognfit (r(:,2)); %! mu_sigmaC = lognfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0:0.1:6]; %! y = lognpdf (x, mu_sigmaA(1), mu_sigmaA(2)); %! plot (x, y, "-pr"); %! y = lognpdf (x, mu_sigmaB(1), mu_sigmaB(2)); %! plot (x, y, "-sg"); %! y = lognpdf (x, mu_sigmaC(1), mu_sigmaC(2)); %! plot (x, y, "-^c"); %! ylim ([0, 2]) %! xlim ([0, 6]) %! hold off %! legend ({"Normalized HIST of sample 1 with mu=0, σ=0.25", ... %! "Normalized HIST of sample 2 with mu=0, σ=0.5", ... %! "Normalized HIST of sample 3 with mu=0, σ=1", ... %! sprintf("PDF for sample 1 with estimated mu=%0.2f and σ=%0.2f", ... %! mu_sigmaA(1), mu_sigmaA(2)), ... %! sprintf("PDF for sample 2 with estimated mu=%0.2f and σ=%0.2f", ... %! mu_sigmaB(1), mu_sigmaB(2)), ... %! sprintf("PDF for sample 3 with estimated mu=%0.2f and σ=%0.2f", ... %! mu_sigmaC(1), mu_sigmaC(2))}, "location", "northeast") %! title ("Three population samples from different log-normal distibutions") %! hold off ## Test output %!test %! randn ("seed", 1); %! x = lognrnd (3, 5, [1000, 1]); %! [paramhat, paramci] = lognfit (x, 0.01); %! assert (paramci(1,1) < 3); %! assert (paramci(1,2) > 3); %! assert (paramci(2,1) < 5); %! assert (paramci(2,2) > 5); ## Test input validation %!error ... %! lognfit (ones (20,3)) %!error ... %! lognfit ({1, 2, 3, 4, 5}) %!error ... %! lognfit ([-1, 2, 3, 4, 5]) %!error lognfit (ones (20,1), 0) %!error lognfit (ones (20,1), -0.3) %!error lognfit (ones (20,1), 1.2) %!error lognfit (ones (20,1), [0.05, 0.1]) %!error lognfit (ones (20,1), 0.02+i) %!error ... %! lognfit (ones (20,1), [], zeros(15,1)) %!error ... %! lognfit (ones (20,1), [], zeros(20,1), ones(25,1)) %!error lognfit (ones (20,1), [], zeros(20,1), ones(20,1), "options") statistics-release-1.7.3/inst/dist_fit/lognlike.m000066400000000000000000000141361475240274700221170ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} lognlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} lognlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} lognlike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} lognlike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the lognormal distribution. ## ## @code{@var{nlogL} = lognlike (@var{params}, @var{x})} returns the negative ## log-likelihood of the data in @var{x} corresponding to the lognormal ## distribution with (1) location parameter @var{mu} and (2) scale parameter ## @var{sigma} given in the two-element vector @var{params}, which correspond to ## the mean and standard deviation of the associated normal distribution. ## Missing values, @qcode{NaNs}, are ignored. Negative values of @var{x} are ## treated as missing values. ## ## If a random variable follows this distribution, its logarithm is normally ## distributed with mean @var{mu} and standard deviation @var{sigma}. ## ## @code{[@var{nlogL}, @var{avar}] = lognlike (@var{params}, @var{x})} ## returns the inverse of Fisher's information matrix, @var{avar}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{avar} are their asymptotic variances. @var{avar} ## is based on the observed Fisher's information, not the expected information. ## ## @code{[@dots{}] = lognlike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = lognlike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logncdf, logninv, lognpdf, lognrnd, lognfit, lognstat} ## @end deftypefn function [nlogL, avar] = lognlike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("lognlike: function called with too few input arguments."); endif if (! isvector (x)) error ("lognlike: X must be a vector."); endif if (numel (params) != 2) error ("lognlike: PARAMS must be a two-element vector."); endif if (nargin < 3 || isempty (censor)) censor = []; elseif (! isequal (size (x), size (censor))) error ("lognlike: X and CENSOR vectors mismatch."); endif if nargin < 4 || isempty (freq) freq = []; elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; if (numel (censor) == numel (freq)) censor(nulls) = []; endif freq(nulls) = []; endif else error ("lognlike: X and FREQ vectors mismatch."); endif ## Treat negative data in X as missing values x(x < 0) = NaN; ## Calculate on log data logx = log(x); if (nargout <= 1) nlogL = normlike (params, logx, censor, freq); else [nlogL, avar] = normlike (params, logx, censor, freq); endif ## Compute censored and frequency if (isempty (freq)) freq = 1; endif if (isempty (censor)) censor = 0; endif nlogL = nlogL + sum (freq .* logx .* (1 - censor)); endfunction ## Test output %!test %! x = 1:50; %! [nlogL, avar] = lognlike ([0, 0.25], x); %! avar_out = [-5.4749e-03, 2.8308e-04; 2.8308e-04, -1.1916e-05]; %! assert (nlogL, 3962.330333301793, 1e-10); %! assert (avar, avar_out, 1e-7); %!test %! x = 1:50; %! [nlogL, avar] = lognlike ([0, 0.25], x * 0.5); %! avar_out = [-7.6229e-03, 4.8722e-04; 4.8722e-04, -2.6754e-05]; %! assert (nlogL, 2473.183051225747, 1e-10); %! assert (avar, avar_out, 1e-7); %!test %! x = 1:50; %! [nlogL, avar] = lognlike ([0, 0.5], x); %! avar_out = [-2.1152e-02, 2.2017e-03; 2.2017e-03, -1.8535e-04]; %! assert (nlogL, 1119.072424020455, 1e-12); %! assert (avar, avar_out, 1e-6); %!test %! x = 1:50; %! censor = ones (1, 50); %! censor([2, 4, 6, 8, 12, 14]) = 0; %! [nlogL, avar] = lognlike ([0, 0.5], x, censor); %! avar_out = [-1.9823e-02, 2.0370e-03; 2.0370e-03, -1.6618e-04]; %! assert (nlogL, 1091.746371145497, 1e-12); %! assert (avar, avar_out, 1e-6); %!test %! x = 1:50; %! censor = ones (1, 50); %! censor([2, 4, 6, 8, 12, 14]) = 0; %! [nlogL, avar] = lognlike ([0, 1], x, censor); %! avar_out = [-6.8634e-02, 1.3968e-02; 1.3968e-02, -2.1664e-03]; %! assert (nlogL, 349.3969104144271, 1e-12); %! assert (avar, avar_out, 1e-6); ## Test input validation %!error ... %! lognlike ([12, 15]); %!error lognlike ([12, 15], ones (2)); %!error ... %! lognlike ([12, 15, 3], [1:50]); %!error ... %! lognlike ([12, 15], [1:50], [1, 2, 3]); %!error ... %! lognlike ([12, 15], [1:50], [], [1, 2, 3]); statistics-release-1.7.3/inst/dist_fit/nakafit.m000066400000000000000000000224761475240274700217360ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} nakafit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nakafit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nakafit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} nakafit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} nakafit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} nakafit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate mean and confidence intervals for the Nakagami distribution. ## ## @code{@var{mu0} = nakafit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Nakagami distribution given the data in ## @var{x}. @qcode{@var{paramhat}(1)} is the shape parameter, @var{mu}, and ## @qcode{@var{paramhat}(2)} is the spread parameter, @var{omega}. ## ## @code{[@var{paramhat}, @var{paramci}] = nakafit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = nakafit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = nakafit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = nakafit (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} must contain non-negative integer frequencies for the ## corresponding elements in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = nakafit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{nakacdf, nakainv, nakapdf, nakarnd, nakalike, nakastat} ## @end deftypefn function [paramhat, paramci] = nakafit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("nakafit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("nakafit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("nakafit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("nakafit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("nakafit: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("nakafit: FREQ must contain integer values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["nakafit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Get parameter estimates from the Gamma distribution paramhat = gamfit (x .^ 2, alpha, censor, freq, options); ## Transform back to Nakagami parameters paramhat(2) = paramhat(1) .* paramhat(2); ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = nakalike (paramhat, x, censor, freq); ## Get standard errors se = sqrt (diag (acov))'; ## Get normal quantiles probs = [alpha/2; 1-alpha/2]; ## Compute muci using a normal approximation paramci(:,1) = norminv (probs, paramhat(1), se(1)); ## Compute omegaci using a normal approximation for log (omega) paramci(:,2) = exp (norminv (probs, log (paramhat(2)), log (se(2)))); endif endfunction %!demo %! ## Sample 3 populations from different Nakagami distibutions %! randg ("seed", 5) # for reproducibility %! r1 = nakarnd (0.5, 1, 2000, 1); %! randg ("seed", 2) # for reproducibility %! r2 = nakarnd (5, 1, 2000, 1); %! randg ("seed", 7) # for reproducibility %! r3 = nakarnd (2, 2, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.05:0.1:3.5], 10); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 2.5]); %! xlim ([0, 3.0]); %! hold on %! %! ## Estimate their MU and LAMBDA parameters %! mu_omegaA = nakafit (r(:,1)); %! mu_omegaB = nakafit (r(:,2)); %! mu_omegaC = nakafit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0.01:0.1:3.01]; %! y = nakapdf (x, mu_omegaA(1), mu_omegaA(2)); %! plot (x, y, "-pr"); %! y = nakapdf (x, mu_omegaB(1), mu_omegaB(2)); %! plot (x, y, "-sg"); %! y = nakapdf (x, mu_omegaC(1), mu_omegaC(2)); %! plot (x, y, "-^c"); %! legend ({"Normalized HIST of sample 1 with μ=0.5 and ω=1", ... %! "Normalized HIST of sample 2 with μ=5 and ω=1", ... %! "Normalized HIST of sample 3 with μ=2 and ω=2", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f and ω=%0.2f", ... %! mu_omegaA(1), mu_omegaA(2)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f and ω=%0.2f", ... %! mu_omegaB(1), mu_omegaB(2)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f and ω=%0.2f", ... %! mu_omegaC(1), mu_omegaC(2))}) %! title ("Three population samples from different Nakagami distibutions") %! hold off ## Test output %!test %! paramhat = nakafit ([1:50]); %! paramhat_out = [0.7355, 858.5]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = nakafit ([1:5]); %! paramhat_out = [1.1740, 11]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = nakafit ([1:6], [], [], [1 1 1 1 1 0]); %! paramhat_out = [1.1740, 11]; %! assert (paramhat, paramhat_out, 1e-4); %!test %! paramhat = nakafit ([1:5], [], [], [1 1 1 1 2]); %! paramhat_out = nakafit ([1:5, 5]); %! assert (paramhat, paramhat_out, 1e-4); ## Test input validation %!error nakafit (ones (2,5)); %!error nakafit ([1, 2, 3, 4, 5], 1.2); %!error nakafit ([1, 2, 3, 4, 5], 0); %!error nakafit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! nakafit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! nakafit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! nakafit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! nakafit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! nakafit ([1, 2, 3, 4, 5], [], [], [1 1 -1 1 1]); %!error ... %! nakafit ([1, 2, 3, 4, 5], [], [], [1 1 1.5 1 1]); %!error ... %! nakafit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/nakalike.m000066400000000000000000000256421475240274700220760ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} nakalike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} nakalike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} nakalike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} nakalike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the Nakagami distribution. ## ## @code{@var{nlogL} = nakalike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Nakagami ## distribution with (1) shape parameter @var{mu} and (2) spread parameter ## @var{omega} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = nakalike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = nakalike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = nakalike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} must contain non-negative integer frequencies for the ## corresponding elements in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{nakacdf, nakainv, nakapdf, nakarnd, nakafit, nakastat} ## @end deftypefn function [nlogL, acov] = nakalike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("nakalike: function called with too few input arguments."); endif if (! isvector (x)) error ("nakalike: X must be a vector."); endif if (length (params) != 2) error ("nakalike: PARAMS must be a two-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("nakalike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("nakalike: X and FREQ vector mismatch."); elseif (any (freq < 0)) error ("nakalike: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("nakafit: FREQ must contain integer values."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Get parameters mu = params(1); omega = params(2); log_a = gammaln (mu); log_b = log (omega / mu); z = x .^ 2 ./ (omega / mu); log_z = log (z); L = (mu - 1) .* log_z - z - log_a - log_b + log (2 .* x); ## Handle censored data n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); z_censored = z(censored); [S, dS] = dgammainc (z_censored, mu); L(censored) = log (S); endif ## Sum up the neg log likelihood nlogL = -sum (freq .* L); ## Compute asymptotic covariance if (nargout > 1) ## Compute first order central differences of the log-likelihood gradient dp = 0.0001 .* max (abs (params), 1); ngrad_p1 = logl_grad (params + [dp(1), 0], x, censor, freq); ngrad_m1 = logl_grad (params - [dp(1), 0], x, censor, freq); ngrad_p2 = logl_grad (params + [0, dp(2)], x, censor, freq); ngrad_m2 = logl_grad (params - [0, dp(2)], x, censor, freq); ## Compute negative Hessian by normalizing the differences by the increment nH = [(ngrad_p1(:) - ngrad_m1(:))./(2 * dp(1)), ... (ngrad_p2(:) - ngrad_m2(:))./(2 * dp(2))]; ## Force neg Hessian being symmetric nH = 0.5 .* (nH + nH'); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("nakalike: non positive definite Hessian matrix."); acov = NaN (2); return endif ## ACOV estimate is the negative inverse of the Hessian. Rinv = inv (R); acov = Rinv * Rinv; endif endfunction ## Helper function for computing negative gradient function ngrad = logl_grad (params, x, censor, freq) mu = params(1); omega = params(2); ## Transform to Gamma parameters log_a = gammaln (mu); log_b = log (omega / mu); z = x .^ 2 ./ (omega / mu); log_z = log (z); dL1 = log_z - psi (mu); dL2 = (z - mu) ./ (omega / mu); ## Handle censored data n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); z_censored = z(censored); [S, dS] = dgammainc (z_censored, a); dL1(censored) = dS ./ S; tmp = mu .* log_z(censored) - log_b - z_censored - log_a; dL2(censored) = exp (tmp) ./ S; endif ngrad = -[sum(freq .* dL1), sum(freq .* dL2)]; ## Transform back to Nakagami parameters ngrad = ngrad * [1, 0; (-omega ./ (mu .^ 2)), (1 ./ mu)]; endfunction ## Compute the incomplete Gamma function with its 1st and 2nd derivatives function [y, dy, d2y] = dgammainc (x, k) ## Initialize return variables y = nan (size (x)); dy = y; d2y = y; ## Use approximation for K > 2^20 ulim = 2^20; is_lim = find (k > ulim); if (! isempty (is_lim)) x(is_lim) = max (ulim - 1/3 + sqrt (ulim ./ k(is_lim)) .* ... (x(is_lim) - (k(is_lim) - 1/3)), 0); k(is_lim) = ulim; endif ## For x < k+1 is_lo = find(x < k + 1 & x != 0); if (! isempty (is_lo)) x_lo = x(is_lo); k_lo = k(is_lo); k_1 = k_lo; step = 1; d1st = 0; d2st = 0; stsum = step; d1sum = d1st; d2sum = d2st; while norm (step, "inf") >= 100 * eps (norm (stsum, "inf")) k_1 += 1; step = step .* x_lo ./ k_1; d1st = (d1st .* x_lo - step) ./ k_1; d2st = (d2st .* x_lo - 2 .* d1st) ./ k_1; stsum = stsum + step; d1sum = d1sum + d1st; d2sum = d2sum + d2st; endwhile fklo = exp (-x_lo + k_lo .* log (x_lo) - gammaln (k_lo + 1)); y_lo = fklo .* stsum; ## Fix very small k y_lo(x_lo > 0 & y_lo > 1) = 1; ## Compute 1st derivative dlogfklo = (log (x_lo) - psi (k_lo + 1)); d1fklo = fklo .* dlogfklo; d1y_lo = d1fklo .* stsum + fklo .* d1sum; ## Compute 2nd derivative d2fklo = d1fklo .* dlogfklo - fklo .* psi (1, k_lo + 1); d2y_lo = d2fklo .* stsum + 2 .* d1fklo .* d1sum + fklo .* d2sum; ## Considering the upper tail y(is_lo) = 1 - y_lo; dy(is_lo) = -d1y_lo; d2y(is_lo) = -d2y_lo; endif ## For x >= k+1 is_hi = find(x >= k+1); if (! isempty (is_hi)) x_hi = x(is_hi); k_hi = k(is_hi); zc = 0; k0 = 0; k1 = k_hi; x0 = 1; x1 = x_hi; d1k0 = 0; d1k1 = 1; d1x0 = 0; d1x1 = 0; d2k0 = 0; d2k1 = 0; d2x0 = 0; d2x2 = 0; kx = k_hi ./ x_hi; d1kx = 1 ./ x_hi; d2kx = 0; start = 1; while norm (d2kx - start, "Inf") > 100 * eps (norm (d2kx, "Inf")) rescale = 1 ./ x1; zc += 1; n_k = zc - k_hi; d2k0 = (d2k1 + d2k0 .* n_k - 2 .* d1k0) .* rescale; d2x0 = (d2x2 + d2x0 .* n_k - 2 .* d1x0) .* rescale; d1k0 = (d1k1 + d1k0 .* n_k - k0) .* rescale; d1x0 = (d1x1 + d1x0 .* n_k - x0) .* rescale; k0 = (k1 + k0 .* n_k) .* rescale; x0 = 1 + (x0 .* n_k) .* rescale; nrescale = zc .* rescale; d2k1 = d2k0 .* x_hi + d2k1 .* nrescale; d2x2 = d2x0 .* x_hi + d2x2 .* nrescale; d1k1 = d1k0 .* x_hi + d1k1 .* nrescale; d1x1 = d1x0 .* x_hi + d1x1 .* nrescale; k1 = k0 .* x_hi + k1 .* nrescale; x1 = x0 .* x_hi + zc; start = d2kx; kx = k1 ./ x1; d1kx = (d1k1 - kx .* d1x1) ./ x1; d2kx = (d2k1 - d1kx .* d1x1 - kx .* d2x2 - d1kx .* d1x1) ./ x1; endwhile fkhi = exp (-x_hi + k_hi .* log (x_hi) - gammaln (k_hi + 1)); y_hi = fkhi .* kx; ## Compute 1st derivative dlogfkhi = (log (x_hi) - psi (k_hi + 1)); d1fkhi = fkhi .* dlogfkhi; d1y_hi = d1fkhi .* kx + fkhi .* d1kx; ## Compute 2nd derivative d2fkhi = d1fkhi .* dlogfkhi - fkhi .* psi (1, k_hi + 1); d2y_hi = d2fkhi .* kx + 2 .* d1fkhi .* d1kx + fkhi .* d2kx; ## Considering the upper tail y(is_hi) = y_hi; dy(is_hi) = d1y_hi; d2y(is_hi) = d2y_hi; endif ## Handle x == 0 is_x0 = find (x == 0); if (! isempty (is_x0)) ## Considering the upper tail y(is_x0) = 1; dy(is_x0) = 0; d2y(is_x0) = 0; endif ## Handle k == 0 is_k0 = find (k == 0); if (! isempty (is_k0)) is_k0x0 = find (k == 0 & x == 0); ## Considering the upper tail y(is_k0) = 0; dy(is_k0x0) = Inf; d2y(is_k0x0) = -Inf; endif endfunction ## Test output %!test %! nlogL = nakalike ([0.735504, 858.5], [1:50]); %! assert (nlogL, 202.8689, 1e-4); %!test %! nlogL = nakalike ([1.17404, 11], [1:5]); %! assert (nlogL, 8.6976, 1e-4); %!test %! nlogL = nakalike ([1.17404, 11], [1:5], [], [1, 1, 1, 1, 1]); %! assert (nlogL, 8.6976, 1e-4); %!test %! nlogL = nakalike ([1.17404, 11], [1:6], [], [1, 1, 1, 1, 1, 0]); %! assert (nlogL, 8.6976, 1e-4); ## Test input validation %!error nakalike (3.25) %!error nakalike ([5, 0.2], ones (2)) %!error ... %! nakalike ([1, 0.2, 3], [1, 3, 5, 7]) %!error ... %! nakalike ([1.5, 0.2], [1:5], [0, 0, 0]) %!error ... %! nakalike ([1.5, 0.2], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! nakalike ([1.5, 0.2], [1:5], [], [1, 1, 1]) %!error ... %! nakalike ([1.5, 0.2], [1:5], [], [1, 1, 1, 1, -1]) statistics-release-1.7.3/inst/dist_fit/nbinfit.m000066400000000000000000000274561475240274700217550ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} nbinfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nbinfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nbinfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nbinfit (@var{x}, @var{alpha}, @var{freq}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nbinfit (@var{x}, @var{alpha}, @var{options}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} nbinfit (@var{x}, @var{alpha}, @var{freq}, @var{options}) ## ## Estimate parameter and confidence intervals for the negative binomial ## distribution. ## ## @code{@var{paramhat} = nbinfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the negative binomial distribution given the ## data in vector @var{x}. @qcode{@var{paramhat}(1)} is the number of successes ## until the experiment is stopped, @var{r}, and @qcode{@var{paramhat}(2)} is ## the probability of success in each experiment, @var{ps}. ## ## @code{[@var{paramhat}, @var{paramci}] = nbinfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@var{paramhat}, @var{paramci}] = nbinfit (@var{x}, @var{alpha})} also ## returns the @qcode{100 * (1 - @var{alpha})} percent confidence intervals of ## the estimated parameter. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. ## ## @code{[@dots{}] = nbinlike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@var{paramhat}, @var{paramci}] = nbinfit (@var{x}, @var{alpha}, ## @var{options})} specifies control parameters for the iterative algorithm used ## to compute ML estimates with the @code{fminsearch} function. @var{options} ## is a structure with the following fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## When @var{r} is an integer, the negative binomial distribution is also known ## as the Pascal distribution and it models the number of failures in @var{x} ## before a specified number of successes is reached in a series of independent, ## identical trials. Its parameters are the probability of success in a single ## trial, @var{ps}, and the number of successes, @var{r}. A special case of the ## negative binomial distribution, when @qcode{@var{r} = 1}, is the geometric ## distribution, which models the number of failures before the first success. ## ## @var{r} can also have non-integer positive values, in which form the negative ## binomial distribution, also known as the Polya distribution, has no ## interpretation in terms of repeated trials, but, like the Poisson ## distribution, it is useful in modeling count data. The negative binomial ## distribution is more general than the Poisson distribution because it has a ## variance that is greater than its mean, making it suitable for count data ## that do not meet the assumptions of the Poisson distribution. In the limit, ## as @var{r} increases to infinity, the negative binomial distribution ## approaches the Poisson distribution. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbincdf, nbininv, nbinpdf, nbinrnd, nbinlike, nbinstat} ## @end deftypefn function [paramhat, paramci] = nbinfit (x, alpha, varargin) ## Check data in X if (any (x < 0)) error ("nbinfit: X cannot have negative values."); endif if (! isvector (x)) error ("nbinfit: X must be a vector."); endif if (any (x < 0) || any (x != round (x)) || any (isinf (x))) error ("nbinfit: X must be a non-negative integer."); endif ## Check ALPHA if (nargin < 2 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("nbinfit: wrong value for ALPHA."); endif ## Add defaults freq = []; options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; ## Check extra arguments for FREQ vector and/or 'options' structure if (nargin > 2) if (numel (varargin) == 1 && isstruct (varargin{1})) options = varargin{1}; elseif (numel (varargin) == 1 && isnumeric (varargin{1})) freq = varargin{1}; elseif (numel (varargin) == 2) freq = varargin{1}; options = varargin{2}; endif if (isempty (freq)) freq = ones (size (x)); endif ## Check for valid freq vector if (! isequal (size (x), size (freq))) error ("nbinfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("nbinfit: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("nbinfit: FREQ must contain integer values."); endif ## Check for valid options structure if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["nbinfit: 'options' argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Expand frequency if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Ensure that a negative binomial fit is valid. xbar = mean (x); varx = var (x); if (varx <= xbar) paramhat = cast ([Inf, 1.0], class (x)); paramci = cast ([Inf, 1; Inf, 1], class (x)); fprintf ("warning: nbinfit: mean exceeds variance.\n"); return endif ## Use Method of Moments estimates as starting point for MLEs. rhat = (xbar .* xbar) ./ (varx - xbar); ## Minimize negative log-likelihood to estimate parameters by parameterizing ## with mu=r(1-p)/p, so it becomes 1-parameter search for rhat. f = @(rhat) nbinfit_search (rhat, x, numel (x), sum (x), options.TolX); [rhat, ~, err, output] = fminsearch (f, rhat, options); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning (strcat (["nbinfit: maximum number of function"], ... [" evaluations are exceeded."])); elseif (output.iterations >= options.MaxIter) warning ("nbinfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("nbinfit: NoSolution."); endif ## Compute parameter estimates pshat = rhat ./ (xbar + rhat); paramhat = [rhat, pshat]; ## Compute confidence interval if (nargout > 1) [~, avar] = nbinlike (paramhat, x); ## Get standard errors sigma = sqrt (diag (avar)); ## Get normal quantiles probs = [alpha/2; 1-alpha/2]; ## Compute paramci using a normal approximation paramci = norminv ([probs, probs], [paramhat; paramhat], [sigma'; sigma']); ## Restrict CI to valid values: r >= 0, 0 <= ps <= 1 paramci(paramci < 0) = 0; if (paramci(2,2) > 1) paramci(2,2) = 1; endif endif endfunction ## Helper function for minimizing the negative log-likelihood function nll = nbinfit_search (r, x, nx, sx, tol) if (r < tol) nll = Inf; else xbar = sx / nx; nll = -sum (gammaln (r +x )) + nx * gammaln (r) ... -nx * r * log (r / (xbar + r)) - sx * log (xbar / (xbar + r)); endif endfunction %!demo %! ## Sample 2 populations from different negative binomial distibutions %! randp ("seed", 5); randg ("seed", 5); # for reproducibility %! r1 = nbinrnd (2, 0.15, 5000, 1); %! randp ("seed", 8); randg ("seed", 8); # for reproducibility %! r2 = nbinrnd (5, 0.2, 5000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0:51], 1); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their probability of success %! r_psA = nbinfit (r(:,1)); %! r_psB = nbinfit (r(:,2)); %! %! ## Plot their estimated PDFs %! x = [0:40]; %! y = nbinpdf (x, r_psA(1), r_psA(2)); %! plot (x, y, "-pg"); %! x = [min(r(:,2)):max(r(:,2))]; %! y = nbinpdf (x, r_psB(1), r_psB(2)); %! plot (x, y, "-sc"); %! ylim ([0, 0.1]) %! xlim ([0, 50]) %! legend ({"Normalized HIST of sample 1 with r=2 and ps=0.15", ... %! "Normalized HIST of sample 2 with r=5 and ps=0.2", ... %! sprintf("PDF for sample 1 with estimated r=%0.2f and ps=%0.2f", ... %! r_psA(1), r_psA(2)), ... %! sprintf("PDF for sample 2 with estimated r=%0.2f and ps=%0.2f", ... %! r_psB(1), r_psB(2))}) %! title ("Two population samples from negative different binomial distibutions") %! hold off ## Test output %!test %! [paramhat, paramci] = nbinfit ([1:50]); %! assert (paramhat, [2.420857, 0.086704], 1e-6); %! assert (paramci(:,1), [1.382702; 3.459012], 1e-6); %! assert (paramci(:,2), [0.049676; 0.123732], 1e-6); %!test %! [paramhat, paramci] = nbinfit ([1:20]); %! assert (paramhat, [3.588233, 0.254697], 1e-6); %! assert (paramci(:,1), [0.451693; 6.724774], 1e-6); %! assert (paramci(:,2), [0.081143; 0.428251], 1e-6); %!test %! [paramhat, paramci] = nbinfit ([1:10]); %! assert (paramhat, [8.8067, 0.6156], 1e-4); %! assert (paramci(:,1), [0; 30.7068], 1e-4); %! assert (paramci(:,2), [0.0217; 1], 1e-4); %!test %! [paramhat, paramci] = nbinfit ([1:10], 0.05, ones (1, 10)); %! assert (paramhat, [8.8067, 0.6156], 1e-4); %! assert (paramci(:,1), [0; 30.7068], 1e-4); %! assert (paramci(:,2), [0.0217; 1], 1e-4); %!test %! [paramhat, paramci] = nbinfit ([1:11], 0.05, [ones(1, 10), 0]); %! assert (paramhat, [8.8067, 0.6156], 1e-4); %! assert (paramci(:,1), [0; 30.7068], 1e-4); %! assert (paramci(:,2), [0.0217; 1], 1e-4); ## Test input validation %!error nbinfit ([-1 2 3 3]) %!error nbinfit (ones (2)) %!error nbinfit ([1 2 1.2 3]) %!error nbinfit ([1 2 3], 0) %!error nbinfit ([1 2 3], 1.2) %!error nbinfit ([1 2 3], [0.02 0.05]) %!error ... %! nbinfit ([1, 2, 3, 4, 5], 0.05, [1, 2, 3, 2]); %!error ... %! nbinfit ([1, 2, 3, 4, 5], 0.05, [1, 2, 3, 2, -1]); %!error ... %! nbinfit ([1, 2, 3, 4, 5], 0.05, [1, 2, 3, 2, 1.5]); %!error ... %! nbinfit ([1, 2, 3, 4, 5], 0.05, struct ("option", 234)); %!error ... %! nbinfit ([1, 2, 3, 4, 5], 0.05, ones (1,5), struct ("option", 234)); statistics-release-1.7.3/inst/dist_fit/nbinlike.m000066400000000000000000000154571475240274700221150ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} nbinlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} nbinlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} nbinlike (@var{params}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the negative binomial distribution. ## ## @code{@var{nlogL} = nbinlike (@var{params}, @var{x})} returns the negative ## log likelihood of the negative binomial distribution with (1) parameter ## @var{r} and (2) parameter @var{ps}, given in the two-element vector ## @var{params}, where @var{r} is the number of successes until the experiment ## is stopped and @var{ps} is the probability of success in each experiment, ## given the number of failures in @var{x}. ## ## @code{[@var{nlogL}, @var{avar}] = nbinlike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{avar}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = nbinlike (@var{params}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## must contain non-negative integer frequencies for the corresponding elements ## in @var{x}. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## When @var{r} is an integer, the negative binomial distribution is also known ## as the Pascal distribution and it models the number of failures in @var{x} ## before a specified number of successes is reached in a series of independent, ## identical trials. Its parameters are the probability of success in a single ## trial, @var{ps}, and the number of successes, @var{r}. A special case of the ## negative binomial distribution, when @qcode{@var{r} = 1}, is the geometric ## distribution, which models the number of failures before the first success. ## ## @var{r} can also have non-integer positive values, in which form the negative ## binomial distribution, also known as the Polya distribution, has no ## interpretation in terms of repeated trials, but, like the Poisson ## distribution, it is useful in modeling count data. The negative binomial ## distribution is more general than the Poisson distribution because it has a ## variance that is greater than its mean, making it suitable for count data ## that do not meet the assumptions of the Poisson distribution. In the limit, ## as @var{r} increases to infinity, the negative binomial distribution ## approaches the Poisson distribution. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbincdf, nbininv, nbinpdf, nbinrnd, nbinfit, nbinstat} ## @end deftypefn function [nlogL, avar] = nbinlike (params, x, freq) ## Check input arguments if (nargin < 2) error ("nbinlike: function called with too few input arguments."); endif if (! isvector (x)) error ("nbinlike: X must be a vector."); endif if (any (x < 0)) error ("nbinlike: X cannot have negative values."); endif if (any (x != fix (x))) error ("nbinlike: number of failures, X, must be integers."); endif if (length (params) != 2) error ("nbinlike: PARAMS must be a two-element vector."); endif if (params(1) <= 0) error (strcat (["nbinlike: number of successes, PARAMS(1), must be"], ... [" a real positive value."])); endif if (params(2) < 0 || params(2) > 1) error (strcat (["nbinlike: probability of success, PARAMS(2), must be"], ... [" in the range [0,1]."])); endif if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("nbinlike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("nbinlike: FREQ must not contain negative values."); elseif (any (fix (freq) != freq)) error ("nbinlike: FREQ must contain integer values."); endif ## Expand frequency if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Compute negative log-likelihood and asymptotic variance r = params(1); ps = params(2); nx = numel (x); glnr = gammaln (r + x) - gammaln (x + 1) - gammaln (r); sumx = sum (x); nlogL = -(sum (glnr) + nx * r * log (ps)) - sumx * log (1 - ps); if (nargout == 2) dL11 = sum (psi (1, r + x) - psi (1, r)); dL12 = nx ./ ps; dL22 = -nx .*r ./ ps .^ 2 - sumx ./ (1 - ps) .^ 2; nH = -[dL11, dL12; dL12, dL22]; if (any (isnan (nH(:)))) avar = [NaN, NaN; NaN, NaN]; else avar = inv (nH); end endif endfunction ## Test output %!assert (nbinlike ([2.42086, 0.0867043], [1:50]), 205.5942, 1e-4) %!assert (nbinlike ([3.58823, 0.254697], [1:20]), 63.6435, 1e-4) %!assert (nbinlike ([8.80671, 0.615565], [1:10]), 24.7410, 1e-4) %!assert (nbinlike ([22.1756, 0.831306], [1:8]), 17.9528, 1e-4) %!assert (nbinlike ([22.1756, 0.831306], [1:9], [ones(1,8), 0]), 17.9528, 1e-4) ## Test input validation %!error nbinlike (3.25) %!error nbinlike ([5, 0.2], ones (2)) %!error nbinlike ([5, 0.2], [-1, 3]) %!error ... %! nbinlike ([1, 0.2, 3], [1, 3, 5, 7]) %!error nbinlike ([-5, 0.2], [1:15]) %!error nbinlike ([0, 0.2], [1:15]) %!error nbinlike ([5, 1.2], [3, 5]) %!error nbinlike ([5, -0.2], [3, 5]) %!error ... %! nbinlike ([5, 0.2], ones (10, 1), ones (8,1)) %!error ... %! nbinlike ([5, 0.2], ones (1, 8), [1 1 1 1 1 1 1 -1]) %!error ... %! nbinlike ([5, 0.2], ones (1, 8), [1 1 1 1 1 1 1 1.5]) statistics-release-1.7.3/inst/dist_fit/normfit.m000066400000000000000000000415011475240274700217650ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{muhat} =} normfit (@var{x}) ## @deftypefnx {statistics} {[@var{muhat}, @var{sigmahat}] =} normfit (@var{x}) ## @deftypefnx {statistics} {[@var{muhat}, @var{sigmahat}, @var{muci}] =} normfit (@var{x}) ## @deftypefnx {statistics} {[@var{muhat}, @var{sigmahat}, @var{muci}, @var{sigmaci}] =} normfit (@var{x}) ## @deftypefnx {statistics} {[@dots{}] =} normfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} normfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} normfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} normfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the normal distribution. ## ## @code{[@var{muhat}, @var{sigmahat}] = normfit (@var{x})} estimates the ## parameters of the normal distribution given the data in @var{x}. @var{muhat} ## is an estimate of the mean, and @var{sigmahat} is an estimate of the standard ## deviation. ## ## @code{[@var{muhat}, @var{sigmahat}, @var{muci}, @var{sigmaci}] = normfit ## (@var{x})} returns the 95% confidence intervals for the mean and standard ## deviation estimates in the arrays @var{muci} and @var{sigmaci}, respectively. ## ## @itemize ## @item ## @var{x} can be a vector or a matrix. When @var{x} is a matrix, the parameter ## estimates and their confidence intervals are computed for each column. In ## this case, @code{normfit} supports only 2 input arguments, @var{x} and ## @var{alpha}. Optional arguments @var{censor}, @var{freq}, and @var{options} ## can be used only when @var{x} is a vector. ## ## @item ## @var{alpha} is a scalar value in the range @math{(0,1)} specifying the ## confidence level for the confidence intervals calculated as ## @math{100x(1 – alpha)%}. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @item ## @var{censor} is a logical vector of the same length as @var{x} specifying ## whether each value in @var{x} is right-censored or not. 1 indicates ## observations that are right-censored and 0 indicates observations that are ## fully observed. With censoring, @var{muhat} and @var{sigmahat} are the ## maximum likelihood estimates (MLEs). If empty, the default is an array of ## 0s, meaning that all observations are fully observed. ## ## @item ## @var{freq} is a vector of the same length as @var{x} and it typically ## contains non-negative integer counts of the corresponding elements in ## @var{x}. If empty, the default is an array of 1s, meaning one observation ## per element of @var{x}. To obtain the weighted MLEs for a data set with ## censoring, specify weights of observations, normalized to the number of ## observations in @var{x}. However, when there is no censored data (default), ## the returned estimate for standard deviation is not exactly the WMLE. To ## compute the weighted MLE, multiply the value returned in @var{sigmahat} by ## @code{(SUM (@var{freq}) - 1) / SUM (@var{freq})}. This correction is needed ## because @code{normfit} normally computes @var{sigmahat} using an unbiased ## variance estimator when there is no censored data. When there is censoring ## in the data, the correction is not needed, since @code{normfit} does not use ## the unbiased variance estimator in that case. ## ## @item ## @var{options} is a structure with the control parameters for ## @code{fminsearch} which is used internally to compute MLEs for censored data. ## By default, it uses the following options: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## @end itemize ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{normcdf, norminv, normpdf, normrnd, normlike, normstat} ## @end deftypefn function [muhat, sigmahat, muci, sigmaci] = normfit (x, alpha, censor, freq, options) ## Check for valid number of input arguments narginchk (1, 5); ## Check X for being a vector or a matrix if (ndims (x) != 2) error ("normfit: X must not be a multi-dimensional array."); endif if (! isvector (x)) if (nargin < 3) [n, ncols] = size (x); else error ("normfit: matrix data acceptable only under 2-arg syntax."); endif else n = numel (x); ncols = 1; endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("normfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = 0; elseif (! isequal (size (x), size (censor))) error ("normfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = 1; elseif (any (freq < 0)) error ("normfit: FREQ must not contain negative values."); elseif (isequal (size (x), size (freq))) n = sum (freq); is_zero = find (freq == 0); if (numel (is_zero) > 0) x(is_zero) = []; if (numel (censor) == numel (freq)) censor(is_zero) = []; endif freq(is_zero) = []; end else error ("normfit: X and FREQ vectors mismatch."); endif ## Check options structure or add defaults if (nargin > 4 && ! isempty (options)) if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["normfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif else options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; endif ## Get number of censored and uncensored elements n_censored = sum(freq.*censor); % a scalar in all cases n_uncensored = n - n_censored; % a scalar in all cases ## Compute total sum in X totalsum = sum(freq.*x); ## Check cases that cannot make a fit. ## 1. Handle Infs and NaNs if (! isfinite (totalsum)) muhat = totalsum; sigmahat = NaN ("like", x); muci = NaN (2, 1, "like", x); sigmaci = NaN (2, 1, "like", x); return endif ## 2. All observations are censored or empty data if (n == 0 || n_uncensored == 0) muhat = NaN (1, ncols,'like',x); sigmahat = NaN (1, ncols,'like',x); muci = NaN (2, ncols,'like',x); sigmaci = NaN (2, ncols,'like',x); return endif ## 3. No censored values, compute parameter estimates explicitly. if (n_censored == 0) muhat = totalsum ./ n; if (n > 1) if numel(muhat) == 1 # X is a vector xc = x - muhat; else # X is a matrix xc = x - repmat (muhat, [n, 1]); endif sigmahat = sqrt (sum (conj (xc) .* xc .* freq) ./ (n - 1)); else sigmahat = zeros (1, ncols, "like", x); endif if (nargout > 2) if (n > 1) paramhat = [muhat; sigmahat]; ci = norm_ci (paramhat, [], alpha, x, [], freq); muci = ci(:,:,1); sigmaci = ci(:,:,2); else muci = [-Inf; Inf] * ones (1, ncols, "like", x); sigmaci = [0; Inf] * ones (1, ncols, "like", x); endif endif return endif ## 4. Αll uncensored observations equal and greater than all the ## censored observations x_uncensored = x(censor == 0); range_x_uncensored = range (x_uncensored); if (range_x_uncensored < realmin (class (x))) if (x_uncensored(1) == max (x)) muhat = x_uncensored(1); sigmahat = zeros ('like',x); if (n_uncensored > 1) muci = [muhat; muhat]; sigmaci = zeros (2, 1, "like", x); else muci = cast ([-Inf; Inf], "like", x); sigmaci = cast ([0; Inf], "like", x); endif return endif endif ## Get an initial estimate for parameters using the "least squares" method if (range_x_uncensored > 0) if (numel (freq) == numel (x)) [p,q] = ecdf (x, "censoring", censor, "frequency", freq); else [p,q] = ecdf (x, "censoring", censor); endif pmid = (p(1:(end-1)) + p(2:end)) / 2; linefit = polyfit (-sqrt (2) * erfcinv (2 * pmid), q(2:end), 1); paramhat = linefit ([2 1]); else # only one uncensored element in X paramhat = [x_uncensored(1) 1]; endif ## Optimize the parameters as doubles, regardless of input data type paramhat = cast (paramhat, "double"); ## Search for parameter that minimize the negative log likelihood function [paramhat, ~, err, output] = fminsearch ... (@(ph) norm_nlogl (ph, x, censor, freq), paramhat, options); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning ("normfit: maximum number of function evaluations are exceeded."); elseif (output.iterations >= options.MaxIter) warning ("normfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("normfit: NoSolution."); endif ## Make sure the outputs match the input data type muhat = cast (paramhat(1), "like", x); sigmahat = cast (paramhat(2), "like", x); if (nargout > 2) paramhat = paramhat(:); if (numel (freq) == numel (x)) [~, avar] = normlike (paramhat, x, censor, freq); else [~, avar] = normlike (paramhat, x, censor); endif ci = norm_ci (paramhat, avar, alpha, x, censor, freq); muci = ci(:,:,1); sigmaci = ci(:,:,2); endif endfunction ## Negative log-likelihood function and gradient for normal distribution. function [nlogL, avar] = norm_nlogl (params, x, censor, freq) ## Get mu and sigma values mu = params(1); sigma = params(2); ## Compute the individual log-likelihood terms. Force a log(0)==-Inf for ## data from extreme right tail, instead of getting exp(Inf-Inf)==NaN. z = (x - mu) ./ sigma; L = -0.5 .* z .^ 2 - log (sqrt (2 .* pi) .* sigma); if (any (censor)) censored = censor == 1; z_censor = z(censored); S_censor = 0.5 * erfc (z_censor / sqrt (2)); L(censored) = log (S_censor); endif ## Neg-log-like is the sum of the individual contributions nlogL = -sum (freq .* L); ## Compute the negative hessian at the parameter values. ## Invert to get the observed information matrix. if (nargout == 2) dL11 = -ones (size (z), class (z)); dL12 = -2 .* z; dL22 = 1 - 3 .* z .^ 2; if (any (censor)) dlogScen = exp (-0.5 .* z_censor .^ 2) ./ (sqrt (2 * pi) .* S_censor); d2logScen = dlogScen .* (dlogScen - z_censor); dL11(censored) = -d2logScen; dL12(censored) = -dlogScen - z_censor .* d2logScen; dL22(censored) = -z_censor .* (2 .* dlogScen + z_censor .* d2logScen); endif nH11 = -sum (freq .* dL11); nH12 = -sum (freq .* dL12); nH22 = -sum (freq .* dL22); avar = (sigma .^ 2) * [nH22, -nH12; -nH12, nH11] / ... (nH11 * nH22 - nH12 * nH12); endif endfunction ## Confidence intervals for normal distribution function ci = norm_ci (paramhat, cv, alpha, x, censor, freq) ## Check for missing input arguments if (nargin < 6 || isempty (freq)) freq = ones (size (x)); endif if (nargin < 5 || isempty (censor)) censor = false (size (x)); endif if (isvector (paramhat)) paramhat = paramhat(:); endif muhat = paramhat(1,:); sigmahat = paramhat(2,:); ## Get number of elements if (isempty (freq) || isequal (freq, 1)) if (isvector (x)) n = length (x); else n = size (x, 1); endif else n = sum (freq); endif ## Get number of censored and uncensored elements n_censored = sum (freq .* censor); n_uncensored = n - n_censored; ## Just in case if (any (censor) && (n == 0 || n_uncensored == 0 || ! isfinite (paramhat(1)))) ## X is a vector muci = NaN(2,1); sigmaci = NaN(2,1); ci = cast (cat (3, muci, sigmaci), "like", x); return endif ## Get confidence intervals for each parameter if ((isempty (censor) || ! any (censor(:))) && ! isequal (cv,zeros(2,2))) ## Use exact formulas tcrit = tinv ([alpha/2, 1-alpha/2], n-1); muci = [muhat+tcrit(1)*sigmahat/sqrt(n); muhat+tcrit(2)*sigmahat/sqrt(n)]; chi2crit = chi2inv ([alpha/2, 1-alpha/2], n-1); sigmaci = [sigmahat*sqrt((n-1)./chi2crit(2)); ... sigmahat*sqrt((n-1)./chi2crit(1))]; else ## Use normal approximation probs = [alpha/2; 1-alpha/2]; se = sqrt (diag (cv))'; z = norminv (probs); ## Compute the CI for mu using a normal distribution for muhat. muci = muhat + se(1) .* z; ## Compute the CI for sigma using a normal approximation for ## log(sigmahat), and transform back to the original scale. logsigci = log (sigmahat) + (se(2) ./ sigmahat) .* z; sigmaci = exp (logsigci); endif ## Return as a single array ci = cat (3, muci, sigmaci); endfunction %!demo %! ## Sample 3 populations from 3 different normal distibutions %! randn ("seed", 1); # for reproducibility %! r1 = normrnd (2, 5, 5000, 1); %! randn ("seed", 2); # for reproducibility %! r2 = normrnd (5, 2, 5000, 1); %! randn ("seed", 3); # for reproducibility %! r3 = normrnd (9, 4, 5000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 15, 0.4); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! hold on %! %! ## Estimate their mu and sigma parameters %! [muhat, sigmahat] = normfit (r); %! %! ## Plot their estimated PDFs %! x = [min(r(:)):max(r(:))]; %! y = normpdf (x, muhat(1), sigmahat(1)); %! plot (x, y, "-pr"); %! y = normpdf (x, muhat(2), sigmahat(2)); %! plot (x, y, "-sg"); %! y = normpdf (x, muhat(3), sigmahat(3)); %! plot (x, y, "-^c"); %! ylim ([0, 0.5]) %! xlim ([-20, 20]) %! hold off %! legend ({"Normalized HIST of sample 1 with mu=2, σ=5", ... %! "Normalized HIST of sample 2 with mu=5, σ=2", ... %! "Normalized HIST of sample 3 with mu=9, σ=4", ... %! sprintf("PDF for sample 1 with estimated mu=%0.2f and σ=%0.2f", ... %! muhat(1), sigmahat(1)), ... %! sprintf("PDF for sample 2 with estimated mu=%0.2f and σ=%0.2f", ... %! muhat(2), sigmahat(2)), ... %! sprintf("PDF for sample 3 with estimated mu=%0.2f and σ=%0.2f", ... %! muhat(3), sigmahat(3))}, "location", "northwest") %! title ("Three population samples from different normal distibutions") %! hold off ## Test output %!test %! load lightbulb %! idx = find (lightbulb(:,2) == 0); %! censoring = lightbulb(idx,3) == 1; %! [muHat, sigmaHat] = normfit (lightbulb(idx,1), [], censoring); %! assert (muHat, 9496.59586737857, 1e-11); %! assert (sigmaHat, 3064.021012796456, 2e-12); %!test %! randn ("seed", 234); %! x = normrnd (3, 5, [1000, 1]); %! [muHat, sigmaHat, muCI, sigmaCI] = normfit (x, 0.01); %! assert (muCI(1) < 3); %! assert (muCI(2) > 3); %! assert (sigmaCI(1) < 5); %! assert (sigmaCI(2) > 5); ## Test input validation %!error ... %! normfit (ones (3,3,3)) %!error ... %! normfit (ones (20,3), [], zeros (20,1)) %!error normfit (ones (20,1), 0) %!error normfit (ones (20,1), -0.3) %!error normfit (ones (20,1), 1.2) %!error normfit (ones (20,1), [0.05 0.1]) %!error normfit (ones (20,1), 0.02+i) %!error ... %! normfit (ones (20,1), [], zeros(15,1)) %!error ... %! normfit (ones (20,1), [], zeros(20,1), ones(25,1)) %!error ... %! normfit (ones (5,1), [], zeros(5,1), [1, 2, 1, 2, -1]') %!error normfit (ones (20,1), [], zeros(20,1), ones(20,1), "options") statistics-release-1.7.3/inst/dist_fit/normlike.m000066400000000000000000000153601475240274700221330ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} normlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} normlike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} normlike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} normlike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the normal distribution. ## ## @code{@var{nlogL} = normlike (@var{params}, @var{x})} returns the negative ## log-likelihood for the normal distribution, evaluated at parameters ## @var{params(1)} = mean and @var{params(2)} = standard deviation, given ## @var{x}. @var{nlogL} is a scalar. ## ## @code{[@var{nlogL}, @var{avar}] = normlike (@var{params}, @var{x})} ## returns the inverse of Fisher's information matrix, @var{avar}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{avar} are their asymptotic variances. @var{avar} ## is based on the observed Fisher's information, not the expected information. ## ## @code{[@dots{}] = normlike (@var{params}, @var{x}, @var{censor})} accepts ## a boolean vector of the same size as @var{x} that is 1 for observations ## that are right-censored and 0 for observations that are observed exactly. ## ## @code{[@dots{}] = normlike (@var{params}, @var{x}, @var{censor}, ## @var{freq})} accepts a frequency vector of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it may contain any non-integer non-negative ## values. Pass in [] for @var{censor} to use its default value. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{normcdf, norminv, normpdf, normrnd, normfit, normstat} ## @end deftypefn function [nlogL, avar] = normlike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("normlike: too few input arguments."); endif if (! isvector (x)) error ("normlike: X must be a vector."); endif if (numel (params) != 2) error ("normlike: PARAMS must be a two-element vector."); endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("normlike: X and CENSOR vectors mismatch."); endif if nargin < 4 || isempty (freq) freq = ones (size (x)); elseif (any (freq < 0)) error ("normlike: FREQ must not contain negative values."); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("normlike: X and FREQ vectors mismatch."); endif ## Get mu and sigma values mu = params(1); sigma = params(2); ## sigma must be positive, otherwise make it NaN if (sigma <= 0) sigma = NaN; endif ## Compute the individual log-likelihood terms. Force a log(0)==-Inf for ## x from extreme right tail, instead of getting exp(Inf-Inf)==NaN. z = (x - mu) ./ sigma; L = -0.5 .* z .^ 2 - log (sqrt (2 .* pi) .* sigma); if (any (censor)) censored = censor == 1; z_censor = z(censored); S_censor = 0.5 * erfc (z_censor / sqrt (2)); L(censored) = log (S_censor); endif ## Neg-log-like is the sum of the individual contributions nlogL = -sum (freq .* L); ## Compute the negative hessian at the parameter values. ## Invert to get the observed information matrix. if (nargout == 2) dL11 = -ones (size (z), class (z)); dL12 = -2 .* z; dL22 = 1 - 3 .* z .^ 2; if (any (censor)) dlogScen = exp (-0.5 .* z_censor .^ 2) ./ (sqrt (2 * pi) .* S_censor); d2logScen = dlogScen .* (dlogScen - z_censor); dL11(censored) = -d2logScen; dL12(censored) = -dlogScen - z_censor .* d2logScen; dL22(censored) = -z_censor .* (2 .* dlogScen + z_censor .* d2logScen); endif nH11 = -sum (freq .* dL11); nH12 = -sum (freq .* dL12); nH22 = -sum (freq .* dL22); avar = (sigma .^ 2) * [nH22, -nH12; -nH12, nH11] / ... (nH11 * nH22 - nH12 * nH12); endif endfunction ## Test input validation %!error normlike ([12, 15]); %!error normlike ([12, 15], ones (2)); %!error ... %! normlike ([12, 15, 3], [1:50]); %!error ... %! normlike ([12, 15], [1:50], [1, 2, 3]); %!error ... %! normlike ([12, 15], [1:50], [], [1, 2, 3]); %!error ... %! normlike ([12, 15], [1:5], [], [1, 2, 3, 2, -1]); ## Results compared with Matlab %!test %! x = 1:50; %! [nlogL, avar] = normlike ([2.3, 1.2], x); %! avar_out = [7.5767e-01, -1.8850e-02; -1.8850e-02, 4.8750e-04]; %! assert (nlogL, 13014.95883783327, 1e-10); %! assert (avar, avar_out, 1e-4); %!test %! x = 1:50; %! [nlogL, avar] = normlike ([2.3, 1.2], x * 0.5); %! avar_out = [3.0501e-01, -1.5859e-02; -1.5859e-02, 9.1057e-04]; %! assert (nlogL, 2854.802587833265, 1e-10); %! assert (avar, avar_out, 1e-4); %!test %! x = 1:50; %! [nlogL, avar] = normlike ([21, 15], x); %! avar_out = [5.460474308300396, -1.600790513833993; ... %! -1.600790513833993, 2.667984189723321]; %! assert (nlogL, 206.738325604233, 1e-12); %! assert (avar, avar_out, 1e-14); %!test %! x = 1:50; %! censor = ones (1, 50); %! censor([2, 4, 6, 8, 12, 14]) = 0; %! [nlogL, avar] = normlike ([2.3, 1.2], x, censor); %! avar_out = [3.0501e-01, -1.5859e-02; -1.5859e-02, 9.1057e-04]; %! assert (nlogL, Inf); %! assert (avar, [NaN, NaN; NaN, NaN]); %!test %! x = 1:50; %! censor = ones (1, 50); %! censor([2, 4, 6, 8, 12, 14]) = 0; %! [nlogL, avar] = normlike ([21, 15], x, censor); %! avar_out = [24.4824488866131, -10.6649544179636; ... %! -10.6649544179636, 6.22827849965737]; %! assert (nlogL, 86.9254371829733, 1e-12); %! assert (avar, avar_out, 8e-14); statistics-release-1.7.3/inst/dist_fit/poissfit.m000066400000000000000000000144711475240274700221550ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{lambdahat} =} poissfit (@var{x}) ## @deftypefnx {statistics} {[@var{lambdahat}, @var{lambdaci}] =} poissfit (@var{x}) ## @deftypefnx {statistics} {[@var{lambdahat}, @var{lambdaci}] =} poissfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{lambdahat}, @var{lambdaci}] =} poissfit (@var{x}, @var{alpha}, @var{freq}) ## ## Estimate parameter and confidence intervals for the Poisson distribution. ## ## @code{@var{lambdahat} = poissfit (@var{x})} returns the maximum likelihood ## estimate of the rate parameter, @var{lambda}, of the Poisson distribution ## given the data in @var{x}. @var{x} must be a vector of non-negative values. ## ## @code{[@var{lambdahat}, @var{lambdaci}] = poissfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimate. ## ## @code{[@var{lambdahat}, @var{lambdaci}] = poissfit (@var{x}, @var{alpha})} ## also returns the @qcode{100 * (1 - @var{alpha})} percent confidence intervals ## of the estimated parameter. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = poissfit (@var{x}, @var{alpha}, @var{freq})} accepts a ## frequency vector or matrix, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}. @var{freq} cannot contain negative values. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poisscdf, poissinv, poisspdf, poissrnd, poisslike, poisstat} ## @end deftypefn function [lambdahat, lambdaci] = poissfit (x, alpha, freq) ## Check input arguments if (any (x < 0)) error ("poissfit: X cannot have negative values."); endif if (nargin < 2 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("poissfit: wrong value for ALPHA."); endif if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("poissfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("poissfit: FREQ must not contain negative values."); endif if (isvector (x)) x = x(:); freq = freq(:); endif ## Compute lambdahat n = sum (freq, 1); lambdahat = double (sum (x .* freq) ./ n); ## Compute confidence intervals lambdasum = n .* lambdahat; ## Select elements for exact method or normal approximation k = (lambdasum < 100); if (any (k)) # exact method lb(k) = chi2inv (alpha / 2, 2 * lambdasum(k)) / 2; ub(k) = chi2inv (1 - alpha / 2, 2 * (lambdasum(k) + 1)) / 2; endif k = ! k; if (any (k)) # normal approximation lb(k) = norminv (alpha / 2, lambdasum(k), sqrt (lambdasum(k))); ub(k) = norminv (1 - alpha / 2, lambdasum(k), sqrt (lambdasum(k))); endif lambdaci = [lb; ub] / n; endfunction %!demo %! ## Sample 3 populations from 3 different Poisson distibutions %! randp ("seed", 2); # for reproducibility %! r1 = poissrnd (1, 1000, 1); %! randp ("seed", 2); # for reproducibility %! r2 = poissrnd (4, 1000, 1); %! randp ("seed", 3); # for reproducibility %! r3 = poissrnd (10, 1000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0:20], 1); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! hold on %! %! ## Estimate their lambda parameter %! lambdahat = poissfit (r); %! %! ## Plot their estimated PDFs %! x = [0:20]; %! y = poisspdf (x, lambdahat(1)); %! plot (x, y, "-pr"); %! y = poisspdf (x, lambdahat(2)); %! plot (x, y, "-sg"); %! y = poisspdf (x, lambdahat(3)); %! plot (x, y, "-^c"); %! xlim ([0, 20]) %! ylim ([0, 0.4]) %! legend ({"Normalized HIST of sample 1 with λ=1", ... %! "Normalized HIST of sample 2 with λ=4", ... %! "Normalized HIST of sample 3 with λ=10", ... %! sprintf("PDF for sample 1 with estimated λ=%0.2f", ... %! lambdahat(1)), ... %! sprintf("PDF for sample 2 with estimated λ=%0.2f", ... %! lambdahat(2)), ... %! sprintf("PDF for sample 3 with estimated λ=%0.2f", ... %! lambdahat(3))}) %! title ("Three population samples from different Poisson distibutions") %! hold off ## Test output %!test %! x = [1 3 2 4 5 4 3 4]; %! [lhat, lci] = poissfit (x); %! assert (lhat, 3.25) %! assert (lci, [2.123007901949543; 4.762003010390628], 1e-14) %!test %! x = [1 3 2 4 5 4 3 4]; %! [lhat, lci] = poissfit (x, 0.01); %! assert (lhat, 3.25) %! assert (lci, [1.842572740234582; 5.281369033298528], 1e-14) %!test %! x = [1 2 3 4 5]; %! f = [1 1 2 3 1]; %! [lhat, lci] = poissfit (x, [], f); %! assert (lhat, 3.25) %! assert (lci, [2.123007901949543; 4.762003010390628], 1e-14) %!test %! x = [1 2 3 4 5]; %! f = [1 1 2 3 1]; %! [lhat, lci] = poissfit (x, 0.01, f); %! assert (lhat, 3.25) %! assert (lci, [1.842572740234582; 5.281369033298528], 1e-14) ## Test input validation %!error poissfit ([1 2 -1 3]) %!error poissfit ([1 2 3], 0) %!error poissfit ([1 2 3], 1.2) %!error poissfit ([1 2 3], [0.02 0.05]) %!error %! poissfit ([1 2 3], [], [1 5]) %!error %! poissfit ([1 2 3], [], [1 5 -1]) statistics-release-1.7.3/inst/dist_fit/poisslike.m000066400000000000000000000076221475240274700223170ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} poisslike (@var{lambda}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{avar}] =} poisslike (@var{lambda}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} poisslike (@var{lambda}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the Poisson distribution. ## ## @code{@var{nlogL} = poisslike (@var{lambda}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Poisson ## distribution with rate parameter @var{lambda}. @var{x} must be a vector of ## non-negative values. ## ## @code{[@var{nlogL}, @var{avar}] = poisslike (@var{lambda}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{avar}. If the input ## rate parameter, @var{lambda}, is the maximum likelihood estimate, @var{avar} ## is its asymptotic variance. ## ## @code{[@dots{}] = poisslike (@var{lambda}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poisscdf, poissinv, poisspdf, poissrnd, poissfit, poisstat} ## @end deftypefn function [nlogL, avar] = poisslike (lambda, x, freq) ## Check input arguments if (nargin < 2) error ("poisslike: function called with too few input arguments."); endif if (! isscalar (lambda) || ! isnumeric (lambda) || lambda <= 0) error ("poisslike: LAMBDA must be a positive scalar."); endif if (! isvector (x) || any (x < 0)) error ("poisslike: X must be a vector of non-negative values."); endif if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("poisslike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("poisslike: FREQ must not contain negative values."); endif ## Compute negative log-likelihood and asymptotic covariance n = sum (freq); nlogL = - sum (freq .* log (poisspdf (x, lambda))); avar = lambda / n; endfunction ## Test output %!test %! x = [1 3 2 4 5 4 3 4]; %! [nlogL, avar] = poisslike (3.25, x); %! assert (nlogL, 13.9533, 1e-4) %!test %! x = [1 2 3 4 5]; %! f = [1 1 2 3 1]; %! [nlogL, avar] = poisslike (3.25, x, f); %! assert (nlogL, 13.9533, 1e-4) ## Test input validation %!error poisslike (1) %!error poisslike ([1 2 3], [1 2]) %!error ... %! poisslike (3.25, ones (10, 2)) %!error ... %! poisslike (3.25, [1 2 3 -4 5]) %!error ... %! poisslike (3.25, ones (10, 1), ones (8,1)) %!error ... %! poisslike (3.25, ones (1, 8), [1 1 1 1 1 1 1 -1]) statistics-release-1.7.3/inst/dist_fit/raylfit.m000066400000000000000000000170161475240274700217650ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{sigmaA} =} raylfit (@var{x}) ## @deftypefnx {statistics} {[@var{sigmaA}, @var{sigmaci}] =} raylfit (@var{x}) ## @deftypefnx {statistics} {[@var{sigmaA}, @var{sigmaci}] =} raylfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{sigmaA}, @var{sigmaci}] =} raylfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@var{sigmaA}, @var{sigmaci}] =} raylfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## ## Estimate parameter and confidence intervals for the Rayleigh distribution. ## ## @code{@var{sigmaA} = raylfit (@var{x})} returns the maximum likelihood ## estimate of the rate parameter, @var{lambda}, of the Rayleigh distribution ## given the data in @var{x}. @var{x} must be a vector of non-negative values. ## ## @code{[@var{sigmaA}, @var{sigmaci}] = raylfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimate. ## ## @code{[@var{sigmaA}, @var{sigmaci}] = raylfit (@var{x}, @var{alpha})} ## also returns the @qcode{100 * (1 - @var{alpha})} percent confidence intervals ## of the estimated parameter. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = raylfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = raylfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector or matrix, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}. @var{freq} cannot contain negative values. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylcdf, raylinv, raylpdf, raylrnd, rayllike, raylstat} ## @end deftypefn function [sigmaA, sigmaci] = raylfit (x, alpha, censor, freq) ## Check input arguments if (any (x < 0)) error ("raylfit: X cannot have negative values."); endif if (! isvector (x)) error ("raylfit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("raylfit: wrong value for ALPHA."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("raylfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("raylfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("raylfit: FREQ must not contain negative values."); endif ## Remove any censored data censored = censor == 1; freq(censored) = []; x(censored) = []; ## Expand frequency vector (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Compute sigmaA sigmaA = sqrt (0.5 * mean (x .^ 2)); ## Compute confidence intervals (based on chi-squared) if (nargout > 1) sx = 2 * numel (x); ci = [1-alpha/2; alpha/2]; sigmaci = sqrt (sx * sigmaA .^ 2 ./ chi2inv (ci, sx)); endif endfunction %!demo %! ## Sample 3 populations from 3 different Rayleigh distibutions %! rand ("seed", 2); # for reproducibility %! r1 = raylrnd (1, 1000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = raylrnd (2, 1000, 1); %! rand ("seed", 3); # for reproducibility %! r3 = raylrnd (4, 1000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [0.5:0.5:10.5], 2); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! hold on %! %! ## Estimate their lambda parameter %! sigmaA = raylfit (r(:,1)); %! sigmaB = raylfit (r(:,2)); %! sigmaC = raylfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0:0.1:10]; %! y = raylpdf (x, sigmaA); %! plot (x, y, "-pr"); %! y = raylpdf (x, sigmaB); %! plot (x, y, "-sg"); %! y = raylpdf (x, sigmaC); %! plot (x, y, "-^c"); %! xlim ([0, 10]) %! ylim ([0, 0.7]) %! legend ({"Normalized HIST of sample 1 with σ=1", ... %! "Normalized HIST of sample 2 with σ=2", ... %! "Normalized HIST of sample 3 with σ=4", ... %! sprintf("PDF for sample 1 with estimated σ=%0.2f", ... %! sigmaA), ... %! sprintf("PDF for sample 2 with estimated σ=%0.2f", ... %! sigmaB), ... %! sprintf("PDF for sample 3 with estimated σ=%0.2f", ... %! sigmaC)}) %! title ("Three population samples from different Rayleigh distibutions") %! hold off ## Test output %!test %! x = [1 3 2 4 5 4 3 4]; %! [shat, sci] = raylfit (x); %! assert (shat, 2.4495, 1e-4) %! assert (sci, [1.8243; 3.7279], 1e-4) %!test %! x = [1 3 2 4 5 4 3 4]; %! [shat, sci] = raylfit (x, 0.01); %! assert (shat, 2.4495, 1e-4) %! assert (sci, [1.6738; 4.3208], 1e-4) %!test %! x = [1 2 3 4 5]; %! f = [1 1 2 3 1]; %! [shat, sci] = raylfit (x, [], [], f); %! assert (shat, 2.4495, 1e-4) %! assert (sci, [1.8243; 3.7279], 1e-4) %!test %! x = [1 2 3 4 5]; %! f = [1 1 2 3 1]; %! [shat, sci] = raylfit (x, 0.01, [], f); %! assert (shat, 2.4495, 1e-4) %! assert (sci, [1.6738; 4.3208], 1e-4) %!test %! x = [1 2 3 4 5 6]; %! c = [0 0 0 0 0 1]; %! f = [1 1 2 3 1 1]; %! [shat, sci] = raylfit (x, 0.01, c, f); %! assert (shat, 2.4495, 1e-4) %! assert (sci, [1.6738; 4.3208], 1e-4) ## Test input validation %!error raylfit (ones (2,5)); %!error raylfit ([1 2 -1 3]) %!error raylfit ([1 2 3], 0) %!error raylfit ([1 2 3], 1.2) %!error raylfit ([1 2 3], [0.02 0.05]) %!error ... %! raylfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! raylfit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! raylfit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! raylfit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error %! raylfit ([1 2 3], [], [], [1 5]) %!error %! raylfit ([1 2 3], [], [], [1 5 -1]) statistics-release-1.7.3/inst/dist_fit/rayllike.m000066400000000000000000000117451475240274700221320ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} rayllike (@var{sigma}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} rayllike (@var{sigma}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} rayllike (@var{sigma}, @var{x}, @var{freq}) ## ## Negative log-likelihood for the Rayleigh distribution. ## ## @code{@var{nlogL} = rayllike (@var{sigma}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Rayleigh ## distribution with rate parameter @var{sigma}. @var{x} must be a vector of ## non-negative values. ## ## @code{[@var{nlogL}, @var{acov}] = rayllike (@var{sigma}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## rate parameter, @var{sigma}, is the maximum likelihood estimate, @var{acov} ## is its asymptotic variance. ## ## @code{[@dots{}] = rayllike (@var{sigma}, @var{x}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylcdf, raylinv, raylpdf, raylrnd, raylfit, raylstat} ## @end deftypefn function [nlogL, acov] = rayllike (sigma, x, censor, freq) ## Check input arguments if (nargin < 2) error ("rayllike: function called with too few input arguments."); endif if (! isscalar (sigma) || ! isnumeric (sigma) || sigma <= 0) error ("rayllike: SIGMA must be a positive scalar."); endif if (! isvector (x) || any (x < 0)) error ("rayllike: X must be a vector of non-negative values."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("rayllike: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("rayllike: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("rayllike: FREQ must not contain negative values."); endif ## Compute negative log-likelihood and asymptotic covariance zsq = (x / sigma) .^ 2; logfz = -2 * log (sigma) - zsq / 2 + log (x); dlogfz = (zsq - 2) / sigma; logS = - zsq / 2; dlogS = - zsq * 3 / sigma ^ 2; logfz(censor == 1) = logS(censor == 1); nlogL = - sum (freq .* logfz); if (nargout > 1) d2 = (2 - 3 * zsq) / sigma ^ 2; d2(censor == 1) = - 3 * zsq(censor == 1) / sigma ^ 2; acov = - sum (freq .* d2); endif endfunction ## Test output %!test %! x = [1 3 2 4 5 4 3 4]; %! [nlogL, acov] = rayllike (3.25, x); %! assert (nlogL, 14.7442, 1e-4) %!test %! x = [1 2 3 4 5]; %! f = [1 1 2 3 1]; %! [nlogL, acov] = rayllike (3.25, x, [], f); %! assert (nlogL, 14.7442, 1e-4) %!test %! x = [1 2 3 4 5 6]; %! f = [1 1 2 3 1 0]; %! [nlogL, acov] = rayllike (3.25, x, [], f); %! assert (nlogL, 14.7442, 1e-4) %!test %! x = [1 2 3 4 5 6]; %! c = [0 0 0 0 0 1]; %! f = [1 1 2 3 1 0]; %! [nlogL, acov] = rayllike (3.25, x, c, f); %! assert (nlogL, 14.7442, 1e-4) ## Test input validation %!error rayllike (1) %!error rayllike ([1 2 3], [1 2]) %!error ... %! rayllike (3.25, ones (10, 2)) %!error ... %! rayllike (3.25, [1 2 3 -4 5]) %!error ... %! rayllike (3.25, [1, 2, 3, 4, 5], [1 1 0]); %!error ... %! rayllike (3.25, [1, 2, 3, 4, 5], [1 1 0 1 1]'); %!error ... %! rayllike (3.25, [1, 2, 3, 4, 5], zeros (1,5), [1 1 0]); %!error ... %! rayllike (3.25, [1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! rayllike (3.25, ones (1, 8), [], [1 1 1 1 1 1 1 -1]) statistics-release-1.7.3/inst/dist_fit/ricefit.m000066400000000000000000000257411475240274700217440ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} ricefit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} ricefit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} ricefit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} ricefit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} ricefit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} ricefit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the Gamma distribution. ## ## @code{@var{paramhat} = ricefit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Rician distribution given the data in ## @var{x}. @qcode{@var{paramhat}(1)} is the non-centrality (distance) ## parameter, @math{s}, and @qcode{@var{paramhat}(2)} is the scale parameter, ## @math{sigma}. ## ## @code{[@var{paramhat}, @var{paramci}] = ricefit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = ricefit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = ricefit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = ricefit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = ricefit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute the maximum likelihood ## estimates. @var{options} is a structure with the following field and its ## default value: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 1000} ## @item @qcode{@var{options}.MaxIter = 500} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{ricecdf, ricepdf, riceinv, ricernd, ricelike, ricestat} ## @end deftypefn function [paramhat, paramci] = ricefit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("ricefit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("ricefit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("ricefit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("ricefit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("ricefit: FREQ cannot have negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["ricefit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Get sample size and data type cls = class (x); szx = sum (freq); ncen = sum (freq .* censor); nunc = szx - ncen; ## Check for illegal value in X if (any (x <= 0)) error ("ricefit: X must contain positive values."); endif ## Handle ill-conditioned cases: no data or all censored if (szx == 0 || nunc == 0 || any (! isfinite (x))) paramhat = nan (1, 2, cls); paramci = nan (2, cls); return endif ## Check for identical data in X if (! isscalar (x) && max (abs (diff (x)) ./ x(2:end)) <= sqrt (eps)) paramhat = cast ([Inf, 0], cls); paramci = cast ([Inf, 0; Inf, 0], cls); return endif ## Use 2nd and 4th Moment Estimators of uncensored data as starting point xsq_uncensored = x(censor == 0) .^ 2; meanxsq = mean (xsq_uncensored); meanx_4 = mean (xsq_uncensored .^ 2); if (meanxsq ^ 2 < meanx_4 && meanx_4 < 2 * meanxsq ^ 2) nu_4 = 2 * meanxsq ^ 2 - meanx_4; nusq = sqrt (nu_4); sigmasq = 0.5 * (meanxsq - nusq); params = cast ([sqrt(nusq), sqrt(sigmasq)], cls); else params = cast ([1, 1], cls); endif ## Minimize negative log-likelihood to estimate parameters f = @(params) ricelike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, params, options); ## Force positive parameter values paramhat = abs (paramhat); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning (strcat (["ricefit: maximum number of function"], ... [" evaluations are exceeded."])); elseif (output.iterations >= options.MaxIter) warning ("ricefit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("ricefit: no solution."); endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = ricelike (paramhat, x, censor, freq); ## Get standard errors stderr = sqrt (diag (acov))'; stderr = stderr ./ paramhat; ## Apply log transform phatlog = log (paramhat); ## Compute normal quantiles z = probit (alpha / 2); ## Compute CI paramci = [phatlog; phatlog] + [stderr; stderr] .* [z, z; -z, -z]; ## Inverse log transform paramci = exp (paramci); endif endfunction %!demo %! ## Sample 3 populations from different Gamma distibutions %! randg ("seed", 5); # for reproducibility %! randp ("seed", 6); %! r1 = ricernd (1, 2, 3000, 1); %! randg ("seed", 2); # for reproducibility %! randp ("seed", 8); %! r2 = ricernd (2, 4, 3000, 1); %! randg ("seed", 7); # for reproducibility %! randp ("seed", 9); %! r3 = ricernd (7.5, 1, 3000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 75, 4); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 0.7]); %! xlim ([0, 12]); %! hold on %! %! ## Estimate their α and β parameters %! s_sigmaA = ricefit (r(:,1)); %! s_sigmaB = ricefit (r(:,2)); %! s_sigmaC = ricefit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0.01,0.1:0.2:18]; %! y = ricepdf (x, s_sigmaA(1), s_sigmaA(2)); %! plot (x, y, "-pr"); %! y = ricepdf (x, s_sigmaB(1), s_sigmaB(2)); %! plot (x, y, "-sg"); %! y = ricepdf (x, s_sigmaC(1), s_sigmaC(2)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with s=1 and σ=2", ... %! "Normalized HIST of sample 2 with s=2 and σ=4", ... %! "Normalized HIST of sample 3 with s=7.5 and σ=1", ... %! sprintf("PDF for sample 1 with estimated s=%0.2f and σ=%0.2f", ... %! s_sigmaA(1), s_sigmaA(2)), ... %! sprintf("PDF for sample 2 with estimated s=%0.2f and σ=%0.2f", ... %! s_sigmaB(1), s_sigmaB(2)), ... %! sprintf("PDF for sample 3 with estimated s=%0.2f and σ=%0.2f", ... %! s_sigmaC(1), s_sigmaC(2))}) %! title ("Three population samples from different Rician distibutions") %! hold off ## Test output %!test %! [paramhat, paramci] = ricefit ([1:50]); %! assert (paramhat, [15.3057, 17.6668], 1e-4); %! assert (paramci, [9.5468, 11.7802; 24.5383, 26.4952], 1e-4); %!test %! [paramhat, paramci] = ricefit ([1:50], 0.01); %! assert (paramhat, [15.3057, 17.6668], 1e-4); %! assert (paramci, [8.2309, 10.3717; 28.4615, 30.0934], 1e-4); %!test %! [paramhat, paramci] = ricefit ([1:5]); %! assert (paramhat, [2.3123, 1.6812], 1e-4); %! assert (paramci, [1.0819, 0.6376; 4.9424, 4.4331], 1e-4); %!test %! [paramhat, paramci] = ricefit ([1:5], 0.01); %! assert (paramhat, [2.3123, 1.6812], 1e-4); %! assert (paramci, [0.8521, 0.4702; 6.2747, 6.0120], 1e-4); %!test %! freq = [1 1 1 1 5]; %! [paramhat, paramci] = ricefit ([1:5], [], [], freq); %! assert (paramhat, [3.5181, 1.5565], 1e-4); %! assert (paramci, [2.5893, 0.9049; 4.7801, 2.6772], 1e-4); %!test %! censor = [1 0 0 0 0]; %! [paramhat, paramci] = ricefit ([1:5], [], censor); %! assert (paramhat, [3.2978, 1.1527], 1e-4); %! assert (paramci, [2.3192, 0.5476; 4.6895, 2.4261], 1e-4); ## Test class of input preserved %!assert (class (ricefit (single ([1:50]))), "single") ## Test input validation %!error ricefit (ones (2)) %!error ricefit ([1:50], 1) %!error ricefit ([1:50], -1) %!error ricefit ([1:50], {0.05}) %!error ricefit ([1:50], "k") %!error ricefit ([1:50], i) %!error ricefit ([1:50], [0.01 0.02]) %!error ricefit ([1:50], [], [1 1]) %!error ricefit ([1:50], [], [], [1 1]) %!error ... %! ricefit ([1:5], [], [], [1, 1, 2, 1, -1]) %!error ricefit ([1 2 3 -4]) %!error ricefit ([1 2 0], [], [1 0 0]) statistics-release-1.7.3/inst/dist_fit/ricelike.m000066400000000000000000000206151475240274700221010ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} ricelike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} ricelike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} ricelike (@var{params}, @var{x}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} ricelike (@var{params}, @var{x}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the Rician distribution. ## ## @code{@var{nlogL} = ricelike (@var{params}, @var{x})} returns the negative ## log likelihood of the data in @var{x} corresponding to the Rician ## distribution with (1) non-centrality (distance) parameter @math{s} and (2) ## scale parameter @math{sigma} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = ricelike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{params} are their asymptotic variances. ## ## @code{[@dots{}] = ricelike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = ricelike (@var{params}, @var{x}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{ricecdf, riceinv, ricepdf, ricernd, ricefit, ricestat} ## @end deftypefn function [nlogL, acov] = ricelike (params, x, censor, freq) ## Check input arguments if (nargin < 2) error ("ricelike: function called with too few input arguments."); endif if (! isvector (x)) error ("ricelike: X must be a vector."); endif if (length (params) != 2) error ("ricelike: PARAMS must be a two-element vector."); endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("ricelike: X and CENSOR vector mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("ricelike: X and FREQ vector mismatch."); elseif (any (freq < 0)) error ("ricelike: FREQ must not contain negative values."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; cf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; cf = [cf, repmat(censor(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); censor = cf; endif ## Get parameters nu = params(1); sigma = params(2); theta = nu ./ sigma; xsigma = x ./ sigma; xstheta = xsigma.*theta; I_0 = besseli (0, xstheta, 1); XNS = (xsigma .^ 2 + theta .^ 2) ./ 2; ## Compute log likelihood L = -XNS + log (I_0) + log (xsigma ./ sigma) + xstheta; ## Handle censored data n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); xsigma_censored = xsigma(censored); Q = marcumQ1 (theta, xsigma_censored); L(censored) = log (Q); endif ## Sum up the neg log likelihood nlogL = -sum (freq .* L); ## Compute asymptotic covariance if (nargout > 1) ## Compute first order central differences of the log-likelihood gradient dp = 0.0001 .* max (abs (params), 1); ngrad_p1 = rice_grad (params + [dp(1), 0], x, censor, freq); ngrad_m1 = rice_grad (params - [dp(1), 0], x, censor, freq); ngrad_p2 = rice_grad (params + [0, dp(2)], x, censor, freq); ngrad_m2 = rice_grad (params - [0, dp(2)], x, censor, freq); ## Compute negative Hessian by normalizing the differences by the increment nH = [(ngrad_p1(:) - ngrad_m1(:))./(2 * dp(1)), ... (ngrad_p2(:) - ngrad_m2(:))./(2 * dp(2))]; ## Force neg Hessian being symmetric nH = 0.5 .* (nH + nH'); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("ricelike: non positive definite Hessian matrix."); acov = NaN (2); return endif ## ACOV estimate is the negative inverse of the Hessian. Rinv = inv (R); acov = Rinv * Rinv; endif endfunction ## Helper function for computing negative gradient function ngrad = rice_grad (params, x, censor, freq) ## Get parameters nu = params(1); sigma = params(2); theta = nu ./ sigma; xsigma = x ./ sigma; xstheta = xsigma.*theta; I_0 = besseli (0, xstheta, 1); XNS = (xsigma .^ 2 + theta .^ 2) ./ 2; ## Compute derivatives I_1 = besseli(1, xstheta, 1); dII = I_1 ./ I_0; dL1 = (-theta + dII .* xsigma) ./ sigma; dL2 = -2 * (1 - XNS + dII .* xstheta) ./ sigma; ## Handle censored data n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); xsigma_censored = xsigma(censored); Q = marcumQ1 (theta, xsigma_censored); expt = exp (-XNS(censored) + xstheta(censored)); dQdtheta = xsigma_censored .* I_1(censored) .* expt; dQdz = -xsigma_censored .* I_0(censored) .* expt; dtheta1 = 1 ./ sigma; dtheta2 = -theta ./ sigma; dz2 = -xsigma_censored ./ sigma; dL1(censored) = dQdtheta .* dtheta1 ./ Q; dL2(censored) = (dQdtheta .* dtheta2 + dQdz .* dz2) ./ Q; endif ## Compute gradient ngrad = -[sum(freq .* dL1) sum(freq .* dL2)]; endfunction ## Marcum's "Q" function of order 1 function Q = marcumQ1 (a, b) ## Prepare output matrix if (isa (a, "single") || isa (b, "single")) Q = NaN (size (b), "single"); else Q = NaN (size (b)); endif ## Force marginal cases Q(a != Inf & b == 0) = 1; Q(a != Inf & b == Inf) = 0; Q(a == Inf & b != Inf) = 1; z = isnan (Q) & a == 0 & b != Inf; if (any(z)) Q(z) = exp ((-b(z) .^ 2) ./ 2); endif ## Compute the remaining cases z = isnan (Q) & ! isnan (a) & ! isnan (b); if (any(z(:))) aa = (a(z) .^ 2) ./ 2; bb = (b(z) .^ 2) ./ 2; eA = exp (-aa); eB = bb .* exp (-bb); h = eA; d = eB .* h; s = d; j = (d > s.*eps(class(d))); k = 1; while (any (j)) eA = aa .* eA ./ k; h = h + eA; eB = bb .* eB ./ (k + 1); d = eB .* h; s(j) = s (j) + d(j); j = (d > s .* eps (class (d))); k = k + 1; endwhile Q(z) = 1 - s; endif endfunction ## Test output %!test %! nlogL = ricelike ([15.3057344, 17.6668458], [1:50]); %! assert (nlogL, 204.5230311010569, 1e-12); %!test %! nlogL = ricelike ([2.312346885, 1.681228265], [1:5]); %! assert (nlogL, 8.65562164930058, 1e-12); ## Test input validation %!error ricelike (3.25) %!error ricelike ([5, 0.2], ones (2)) %!error ... %! ricelike ([1, 0.2, 3], [1, 3, 5, 7]) %!error ... %! ricelike ([1.5, 0.2], [1:5], [0, 0, 0]) %!error ... %! ricelike ([1.5, 0.2], [1:5], [0, 0, 0, 0, 0], [1, 1, 1]) %!error ... %! ricelike ([1.5, 0.2], [1:5], [], [1, 1, 1]) %!error ... %! ricelike ([1.5, 0.2], [1:5], [], [1, 1, 1, 0, -1]) statistics-release-1.7.3/inst/dist_fit/tlsfit.m000066400000000000000000000237511475240274700216230ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} tlsfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} tlsfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} tlsfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} tlsfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} tlsfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} tlsfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the Location-scale Student's ## T distribution. ## ## @code{@var{muhat} = tlsfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the location-scale T distribution ## given the data in @var{x}. @qcode{@var{paramhat}(1)} is the location ## parameter, @math{mu}, @qcode{@var{paramhat}(2)} is the scale parameter, ## @math{sigma}, and @qcode{@var{paramhat}(3)} is the degrees of freedom, ## @math{nu}. ## ## @code{[@var{paramhat}, @var{paramci}] = tlsfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = tlsfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = tlsfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = tlsfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = tlsfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. @var{options} is a structure with the following ## fields and their default values: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlscdf, tlsinv, tlspdf, tlsrnd, tlslike, tlsstat} ## @end deftypefn function [paramhat, paramci] = tlsfit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("tlsfit: X must be a vector."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("tlsfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("tlsfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("tlsfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("tlsfit: FREQ cannot have negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ... ! isfield (options, "TolX")) error (strcat (["tlsfit: 'options' 5th argument must be a structure"], ... [" with 'Display' and 'TolX' fields present."])); endif endif ## Starting points as robust estimators for MU and SIGMA, and method ## of moments for DF x_uncensored = x(censor==0); mu = median (x_uncensored); sigma = 1.253 * mad (x_uncensored); mom = kurtosis (x_uncensored); mom(mom < 4) = 4; nu = 2 * (2 * mom - 3) / (mom - 3); x0 = [mu, sigma, nu]; ## Minimize negative log-likelihood to estimate parameters f = @(params) tlslike (params, x, censor, freq); [paramhat, ~, err, output] = fminsearch (f, x0, options); ## Handle errors if (err == 0) if (output.funcCount >= options.MaxFunEvals) warning (strcat (["tlsfit: maximum number of function"], ... [" evaluations are exceeded."])); elseif (output.iterations >= options.MaxIter) warning ("tlsfit: maximum number of iterations are exceeded."); endif elseif (err < 0) error ("tlsfit: no solution."); endif ## Compute CIs using a log normal approximation for parameters. if (nargout > 1) ## Compute asymptotic covariance [~, acov] = tlslike (paramhat, x, censor, freq); ## Get standard errors stderr = sqrt (diag (acov))'; mu_se = stderr(1); sigma_se = stderr(2) ./ paramhat(2); df_se = stderr(3) ./ paramhat(3); ## Apply log transform to SIGMA and DF sigma_log = log (paramhat(2)); df_log = log (paramhat(3)); ## Compute normal quantiles z = norminv (alpha / 2); ## Compute CI muci = [paramhat(1); paramhat(1)] + [mu_se; mu_se] .* [z; -z]; sigmaci = [sigma_log; sigma_log] + [sigma_se; sigma_se] .* [z; -z]; dfci = [df_log; df_log] + [df_se; df_se] .* [z; -z]; ## Inverse log transform paramci = [muci, exp([sigmaci, dfci])]; endif endfunction %!demo %! ## Sample 3 populations from 3 different location-scale T distibutions %! randn ("seed", 1); # for reproducibility %! randg ("seed", 2); # for reproducibility %! r1 = tlsrnd (-4, 3, 1, 2000, 1); %! randn ("seed", 3); # for reproducibility %! randg ("seed", 4); # for reproducibility %! r2 = tlsrnd (0, 3, 1, 2000, 1); %! randn ("seed", 5); # for reproducibility %! randg ("seed", 6); # for reproducibility %! r3 = tlsrnd (5, 5, 4, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, [-21:21], [1, 1, 1]); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 0.25]); %! xlim ([-20, 20]); %! hold on %! %! ## Estimate their lambda parameter %! mu_sigma_nuA = tlsfit (r(:,1)); %! mu_sigma_nuB = tlsfit (r(:,2)); %! mu_sigma_nuC = tlsfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [-20:0.1:20]; %! y = tlspdf (x, mu_sigma_nuA(1), mu_sigma_nuA(2), mu_sigma_nuA(3)); %! plot (x, y, "-pr"); %! y = tlspdf (x, mu_sigma_nuB(1), mu_sigma_nuB(2), mu_sigma_nuB(3)); %! plot (x, y, "-sg"); %! y = tlspdf (x, mu_sigma_nuC(1), mu_sigma_nuC(2), mu_sigma_nuC(3)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with μ=0, σ=2 and nu=1", ... %! "Normalized HIST of sample 2 with μ=5, σ=2 and nu=1", ... %! "Normalized HIST of sample 3 with μ=3, σ=4 and nu=3", ... %! sprintf("PDF for sample 1 with estimated μ=%0.2f, σ=%0.2f, and ν=%0.2f", ... %! mu_sigma_nuA(1), mu_sigma_nuA(2), mu_sigma_nuA(3)), ... %! sprintf("PDF for sample 2 with estimated μ=%0.2f, σ=%0.2f, and ν=%0.2f", ... %! mu_sigma_nuB(1), mu_sigma_nuB(2), mu_sigma_nuB(3)), ... %! sprintf("PDF for sample 3 with estimated μ=%0.2f, σ=%0.2f, and ν=%0.2f", ... %! mu_sigma_nuC(1), mu_sigma_nuC(2), mu_sigma_nuC(3))}) %! title ("Three population samples from different location-scale T distibutions") %! hold off ## Test output %!test %! x = [-1.2352, -0.2741, 0.1726, 7.4356, 1.0392, 16.4165]; %! [paramhat, paramci] = tlsfit (x); %! paramhat_out = [0.035893, 0.862711, 0.649261]; %! paramci_out = [-0.949034, 0.154655, 0.181080; 1.02082, 4.812444, 2.327914]; %! assert (paramhat, paramhat_out, 1e-6); %! assert (paramci, paramci_out, 1e-5); %!test %! x = [-1.2352, -0.2741, 0.1726, 7.4356, 1.0392, 16.4165]; %! [paramhat, paramci] = tlsfit (x, 0.01); %! paramci_out = [-1.2585, 0.0901, 0.1212; 1.3303, 8.2591, 3.4771]; %! assert (paramci, paramci_out, 1e-4); ## Test input validation %!error tlsfit (ones (2,5)); %!error tlsfit ([1, 2, 3, 4, 5], 1.2); %!error tlsfit ([1, 2, 3, 4, 5], 0); %!error tlsfit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! tlsfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! tlsfit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! tlsfit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! tlsfit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! tlsfit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 -1]); %!error ... %! tlsfit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/tlslike.m000066400000000000000000000161061475240274700217610ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR l PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} tlslike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} tlslike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} tlslike (@var{params}, @var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} tlslike (@var{params}, @var{x}, @var{alpha}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the location-scale Student's T distribution. ## ## @code{@var{nlogL} = tlslike (@var{params}, @var{x})} returns the negative ## log-likelihood of the x in @var{x} corresponding to the location-scale T ## distribution with (1) location parameter @math{mu}, (2) scale parameter ## @math{sigma} and (3) degrees of freedom @math{nu} given in the three-element ## vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = tlslike (@var{params}, @var{x})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. @var{acov} ## is based on the observed Fisher's information, not the expected information. ## ## @code{[@dots{}] = tlslike (@var{params}, @var{x}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = tlslike (@var{params}, @var{x}, @var{censor}, ## @var{freq})} accepts a frequency vector, @var{freq}, of the same size as ## @var{x}. @var{freq} typically contains integer frequencies for the ## corresponding elements in @var{x}, but may contain any non-integer ## non-negative values. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlscdf, tlsinv, tlspdf, tlsrnd, tlsfit, tlsstat} ## @end deftypefn function [nlogL, acov] = tlslike (params, x, censor, freq) ## Check input arguments and add defaults if (nargin < 2) error ("tlslike: too few input arguments."); endif if (numel (params) != 3) error ("tlslike: wrong parameters length."); endif if (! isvector (x)) error ("tlslike: X must be a vector."); endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("tlslike: X and CENSOR vectors mismatch."); endif if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (any (freq < 0)) error ("tlslike: FREQ cannot have negative values."); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("tlslike: X and FREQ vectors mismatch."); endif ## Compute the negative log-likelihood nlogL = tlsnll (x, params, censor, freq); ## Compute the negative hessian and invert to get the information matrix if (nargout > 1) ei = zeros (1, 3); ej = zeros (1, 3); nH = zeros (3, 3); dp = (eps ^ (1/4)) .* max (abs (params), 1); for i = 1:3 ei(i) = dp(i); for j = 1:(i-1) ej(j) = dp(j); ## Four-point central difference for mixed second partials nH(i,j) = tlsnll (x, params+ei+ej, censor, freq) ... - tlsnll (x, params+ei-ej, censor, freq) ... - tlsnll (x, params-ei+ej, censor, freq) ... + tlsnll (x, params-ei-ej, censor, freq); ej(j) = 0; endfor ## Five-point central difference for pure second partial nH(i,i) = - tlsnll (x, params+2*ei, censor, freq) ... + 16 * tlsnll (x, params+ei, censor, freq) - 30 * nlogL ... + 16 * tlsnll (x, params-ei, censor, freq) ... - tlsnll (x, params-2*ei, censor, freq); ei(i) = 0; endfor ## Fill in the upper triangle nH = nH + triu (nH', 1); ## Normalize the second differences to get derivative estimates nH = nH ./ (4 .* dp(:) * dp(:)' + diag (8 * dp(:) .^ 2)); ## Check neg Hessian is positive definite [R, p] = chol (nH); if (p > 0) warning ("tlslike: non positive definite Hessian matrix."); acov = NaN (3); return endif ## ACOV estimate is the negative inverse of the Hessian Rinv = inv (R); acov = Rinv * Rinv'; endif endfunction ## Internal function to calculate negative log likelihood for tlslike function nlogL = tlsnll (x, params, censor, freq) mu = params(1); sigma = params(2); sigma(sigma <= 0) = NaN; nu = params(3); nu(nu <= 0) = NaN; z = (x - mu) ./ sigma; w = nu + (z .^ 2); logw = log (w); L = - 0.5 .* (nu + 1) .* logw + gammaln (0.5 .* (nu + 1)) ... - gammaln (0.5 .* nu) + 0.5 .* nu .* log (nu) ... - log (sigma) - 0.5 .* log (pi); n_censored = sum (freq .* censor); if (n_censored > 0) censored = (censor == 1); if (nu < 1e7) # Use incomplete beta function S_censored = betainc (nu ./ w(censored), 0.5 .* nu, 0.5) ./ 2; S_censored(z(censored) < 0) = 1 - S_censored(z(censored) < 0); else # Use a normal approximation S_censored = log(0.5 * erfc(z(censored) ./ sqrt(2))); endif L(censored) = log(S_censored); endif nlogL = - sum (freq .* L); endfunction ## Test output %!test %! x = [-1.2352, -0.2741, 0.1726, 7.4356, 1.0392, 16.4165]; %! [nlogL, acov] = tlslike ([0.035893, 0.862711, 0.649261], x); %! acov_out = [0.2525, 0.0670, 0.0288; ... %! 0.0670, 0.5724, 0.1786; ... %! 0.0288, 0.1786, 0.1789]; %! assert (nlogL, 17.9979636579, 1e-10); %! assert (acov, acov_out, 1e-4); ## Test input validation %!error tlslike ([12, 15, 1]); %!error tlslike ([12, 15], [1:50]); %!error tlslike ([12, 3, 1], ones (10, 2)); %!error tlslike ([12, 15, 1], [1:50], [1, 2, 3]); %!error tlslike ([12, 15, 1], [1:50], [], [1, 2, 3]); %!error tlslike ([12, 15, 1], [1:3], [], [1, 2, -3]); statistics-release-1.7.3/inst/dist_fit/unidfit.m000066400000000000000000000127771475240274700217660ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Nhat} =} unidfit (@var{x}) ## @deftypefnx {statistics} {[@var{Nhat}, @var{Nci}] =} unidfit (@var{x}) ## @deftypefnx {statistics} {[@var{Nhat}, @var{Nci}] =} unidfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{Nhat}, @var{Nci}] =} unidfit (@var{x}, @var{alpha}, @var{freq}) ## ## Estimate parameter and confidence intervals for the discrete uniform distribution. ## ## @code{@var{Nhat} = unidfit (@var{x})} returns the maximum likelihood estimate ## (MLE) of the maximum observable value for the discrete uniform distribution. ## @var{x} must be a vector. ## ## @code{[@var{Nhat}, @var{Nci}] = unidfit (@var{x}, @var{alpha})} also ## returns the @qcode{100 * (1 - @var{alpha})} percent confidence intervals of ## the estimated parameter. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = unidfit (@var{x}, @var{alpha}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the discrete uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Discrete_uniform_distribution} ## ## @seealso{unidcdf, unidinv, unidpdf, unidrnd, unidstat} ## @end deftypefn function [Nhat, Nci] = unidfit (x, alpha, freq) ## Check input arguments if (nargin < 1) error ("unidfit: function called with too few input arguments."); endif ## Check data inX if (any (x < 0)) error ("unidfit: X cannot have negative values."); endif if (! isvector (x)) error ("unidfit: X must be a vector."); endif ## Check ALPHA if (nargin < 2 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("unidfit: wrong value for ALPHA."); endif ## Check frequency vector if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("unidfit: X and FREQ vector mismatch."); elseif (any (freq < 0)) error ("unidfit: FREQ cannot have negative values."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; freq = ones (size (x)); endif ## Compute N estimate Nhat = max (x); ## Compute confidence interval of N if (nargout > 1) Nci = [Nhat; ceil(Nhat ./ alpha .^ (1 ./ numel (x)))]; endif endfunction %!demo %! ## Sample 2 populations from different discrete uniform distibutions %! rand ("seed", 1); # for reproducibility %! r1 = unidrnd (5, 1000, 1); %! rand ("seed", 2); # for reproducibility %! r2 = unidrnd (9, 1000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, 0:0.5:20.5, 1); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their probability of success %! NhatA = unidfit (r(:,1)); %! NhatB = unidfit (r(:,2)); %! %! ## Plot their estimated PDFs %! x = [0:10]; %! y = unidpdf (x, NhatA); %! plot (x, y, "-pg"); %! y = unidpdf (x, NhatB); %! plot (x, y, "-sc"); %! xlim ([0, 10]) %! ylim ([0, 0.4]) %! legend ({"Normalized HIST of sample 1 with N=5", ... %! "Normalized HIST of sample 2 with N=9", ... %! sprintf("PDF for sample 1 with estimated N=%0.2f", NhatA), ... %! sprintf("PDF for sample 2 with estimated N=%0.2f", NhatB)}) %! title ("Two population samples from different discrete uniform distibutions") %! hold off ## Test output %!test %! x = 0:5; %! [Nhat, Nci] = unidfit (x); %! assert (Nhat, 5); %! assert (Nci, [5; 9]); %!test %! x = 0:5; %! [Nhat, Nci] = unidfit (x, [], [1 1 1 1 1 1]); %! assert (Nhat, 5); %! assert (Nci, [5; 9]); %!assert (unidfit ([1 1 2 3]), unidfit ([1 2 3], [] ,[2 1 1])) ## Test input validation %!error unidfit () %!error unidfit (-1, [1 2 3 3]) %!error unidfit (1, 0) %!error unidfit (1, 1.2) %!error unidfit (1, [0.02 0.05]) %!error ... %! unidfit ([1.5, 0.2], [], [0, 0, 0, 0, 0]) %!error ... %! unidfit ([1.5, 0.2], [], [1, 1, 1]) %!error ... %! unidfit ([1.5, 0.2], [], [1, -1]) statistics-release-1.7.3/inst/dist_fit/unifit.m000066400000000000000000000135351475240274700216130ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} unifit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} unifit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} unifit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} unifit (@var{x}, @var{alpha}, @var{freq}) ## ## Estimate parameter and confidence intervals for the continuous uniform distribution. ## ## @code{@var{paramhat} = unifit (@var{x})} returns the maximum likelihood ## estimate (MLE) of the parameters @var{a} and @var{b} of the continuous ## uniform distribution given the data in @var{x}. @var{x} must be a vector. ## ## @code{[@var{paramhat}, @var{paramci}] = unifit (@var{x}, @var{alpha})} also ## returns the @qcode{100 * (1 - @var{alpha})} percent confidence intervals of ## the estimated parameter. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = unifit (@var{x}, @var{alpha}, @var{freq})} accepts a ## frequency vector, @var{freq}, of the same size as @var{x}. @var{freq} ## typically contains integer frequencies for the corresponding elements in ## @var{x}, but it can contain any non-integer non-negative values. By default, ## or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the continuous uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{unifcdf, unifinv, unifpdf, unifrnd, unifstat} ## @end deftypefn function [paramhat, paramci] = unifit (x, alpha, freq) ## Check input arguments if (nargin < 1) error ("unifit: function called with too few input arguments."); endif ## Check data inX if (any (x < 0)) error ("unifit: X cannot have negative values."); endif if (! isvector (x)) error ("unifit: X must be a vector."); endif ## Check ALPHA if (nargin < 2 || isempty (alpha)) alpha = 0.05; elseif (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("unifit: wrong value for ALPHA."); endif ## Check frequency vector if (nargin < 3 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("unifit: X and FREQ vector mismatch."); elseif (any (freq < 0)) error ("unifit: FREQ cannot have negative values."); endif ## Expand frequency and censor vectors (if necessary) if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor x = xf; endif ## Compute A and B estimates ahat = min (x); bhat = max (x); paramhat = [ahat, bhat]; ## Compute confidence interval of A and B if (nargout > 1) tmp = (bhat - ahat) ./ alpha .^ (1 ./ numel (x)); paramci = [bhat-tmp, ahat+tmp; ahat, bhat]; endif endfunction %!demo %! ## Sample 2 populations from different continuous uniform distibutions %! rand ("seed", 5); # for reproducibility %! r1 = unifrnd (2, 5, 2000, 1); %! rand ("seed", 6); # for reproducibility %! r2 = unifrnd (3, 9, 2000, 1); %! r = [r1, r2]; %! %! ## Plot them normalized and fix their colors %! hist (r, 0:0.5:10, 2); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! hold on %! %! ## Estimate their probability of success %! a_bA = unifit (r(:,1)); %! a_bB = unifit (r(:,2)); %! %! ## Plot their estimated PDFs %! x = [0:10]; %! y = unifpdf (x, a_bA(1), a_bA(2)); %! plot (x, y, "-pg"); %! y = unifpdf (x, a_bB(1), a_bB(2)); %! plot (x, y, "-sc"); %! xlim ([1, 10]) %! ylim ([0, 0.5]) %! legend ({"Normalized HIST of sample 1 with a=2 and b=5", ... %! "Normalized HIST of sample 2 with a=3 and b=9", ... %! sprintf("PDF for sample 1 with estimated a=%0.2f and b=%0.2f", ... %! a_bA(1), a_bA(2)), ... %! sprintf("PDF for sample 2 with estimated a=%0.2f and b=%0.2f", ... %! a_bB(1), a_bB(2))}) %! title ("Two population samples from different continuous uniform distibutions") %! hold off ## Test output %!test %! x = 0:5; %! [paramhat, paramci] = unifit (x); %! assert (paramhat, [0, 5]); %! assert (paramci, [-3.2377, 8.2377; 0, 5], 1e-4); %!test %! x = 0:5; %! [paramhat, paramci] = unifit (x, [], [1 1 1 1 1 1]); %! assert (paramhat, [0, 5]); %! assert (paramci, [-3.2377, 8.2377; 0, 5], 1e-4); %!assert (unifit ([1 1 2 3]), unifit ([1 2 3], [] ,[2 1 1])) ## Test input validation %!error unifit () %!error unifit (-1, [1 2 3 3]) %!error unifit (1, 0) %!error unifit (1, 1.2) %!error unifit (1, [0.02 0.05]) %!error ... %! unifit ([1.5, 0.2], [], [0, 0, 0, 0, 0]) %!error ... %! unifit ([1.5, 0.2], [], [1, -1]) %!error ... %! unifit ([1.5, 0.2], [], [1, 1, 1]) statistics-release-1.7.3/inst/dist_fit/wblfit.m000066400000000000000000000206301475240274700215760ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{paramhat} =} wblfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} wblfit (@var{x}) ## @deftypefnx {statistics} {[@var{paramhat}, @var{paramci}] =} wblfit (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} wblfit (@var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} wblfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@dots{}] =} wblfit (@var{x}, @var{alpha}, @var{censor}, @var{freq}, @var{options}) ## ## Estimate parameters and confidence intervals for the Weibull distribution. ## ## @code{@var{muhat} = wblfit (@var{x})} returns the maximum likelihood ## estimates of the parameters of the Weibull distribution given the data in ## @var{x}. @qcode{@var{paramhat}(1)} is the scale parameter, @math{lambda}, ## and @qcode{@var{paramhat}(2)} is the shape parameter, @math{k}. ## ## @code{[@var{paramhat}, @var{paramci}] = wblfit (@var{x})} returns the 95% ## confidence intervals for the parameter estimates. ## ## @code{[@dots{}] = wblfit (@var{x}, @var{alpha})} also returns the ## @qcode{100 * (1 - @var{alpha})} percent confidence intervals for the ## parameter estimates. By default, the optional argument @var{alpha} is ## 0.05 corresponding to 95% confidence intervals. Pass in @qcode{[]} for ## @var{alpha} to use the default values. ## ## @code{[@dots{}] = wblfit (@var{x}, @var{alpha}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = wblfit (@var{x}, @var{alpha}, @var{censor}, @var{freq})} ## accepts a frequency vector, @var{freq}, of the same size as @var{x}. ## @var{freq} typically contains integer frequencies for the corresponding ## elements in @var{x}, but it can contain any non-integer non-negative values. ## By default, or if left empty, @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @code{[@dots{}] = wblfit (@dots{}, @var{options})} specifies control ## parameters for the iterative algorithm used to compute the maximum likelihood ## estimates. @var{options} is a structure with the following field and its ## default value: ## @itemize ## @item @qcode{@var{options}.Display = "off"} ## @item @qcode{@var{options}.MaxFunEvals = 400} ## @item @qcode{@var{options}.MaxIter = 200} ## @item @qcode{@var{options}.TolX = 1e-6} ## @end itemize ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblcdf, wblinv, wblpdf, wblrnd, wbllike, wblstat} ## @end deftypefn function [paramhat, paramci] = wblfit (x, alpha, censor, freq, options) ## Check input arguments if (! isvector (x)) error ("wblfit: X must be a vector."); elseif (any (x <= 0)) error ("wblfit: X must contain only positive values."); endif ## Check alpha if (nargin < 2 || isempty (alpha)) alpha = 0.05; else if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("wblfit: wrong value for ALPHA."); endif endif ## Check censor vector if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("wblfit: X and CENSOR vectors mismatch."); endif ## Check frequency vector if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (! isequal (size (x), size (freq))) error ("wblfit: X and FREQ vectors mismatch."); elseif (any (freq < 0)) error ("wblfit: FREQ cannot have negative values."); endif ## Get options structure or add defaults if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["wblfit: 'options' 5th argument must be a"], ... [" structure with 'Display', 'MaxFunEvals',"], ... [" 'MaxIter', and 'TolX' fields present."])); endif endif ## Fit an extreme value distribution to the logged data, then transform to ## the Weibull parameter scales. [paramhatEV, paramciEV] = evfit (log (x), alpha, censor, freq, options); paramhat = [exp(paramhatEV(1)), 1./paramhatEV(2)]; if (nargout > 1) paramci = [exp(paramciEV(:,1)) 1./paramciEV([2 1],2)]; endif endfunction %!demo %! ## Sample 3 populations from 3 different Weibull distibutions %! rande ("seed", 1); # for reproducibility %! r1 = wblrnd(2, 4, 2000, 1); %! rande ("seed", 2); # for reproducibility %! r2 = wblrnd(5, 2, 2000, 1); %! rande ("seed", 5); # for reproducibility %! r3 = wblrnd(1, 5, 2000, 1); %! r = [r1, r2, r3]; %! %! ## Plot them normalized and fix their colors %! hist (r, 30, [2.5 2.1 3.2]); %! h = findobj (gca, "Type", "patch"); %! set (h(1), "facecolor", "c"); %! set (h(2), "facecolor", "g"); %! set (h(3), "facecolor", "r"); %! ylim ([0, 2]); %! xlim ([0, 10]); %! hold on %! %! ## Estimate their lambda parameter %! lambda_kA = wblfit (r(:,1)); %! lambda_kB = wblfit (r(:,2)); %! lambda_kC = wblfit (r(:,3)); %! %! ## Plot their estimated PDFs %! x = [0:0.1:15]; %! y = wblpdf (x, lambda_kA(1), lambda_kA(2)); %! plot (x, y, "-pr"); %! y = wblpdf (x, lambda_kB(1), lambda_kB(2)); %! plot (x, y, "-sg"); %! y = wblpdf (x, lambda_kC(1), lambda_kC(2)); %! plot (x, y, "-^c"); %! hold off %! legend ({"Normalized HIST of sample 1 with λ=2 and k=4", ... %! "Normalized HIST of sample 2 with λ=5 and k=2", ... %! "Normalized HIST of sample 3 with λ=1 and k=5", ... %! sprintf("PDF for sample 1 with estimated λ=%0.2f and k=%0.2f", ... %! lambda_kA(1), lambda_kA(2)), ... %! sprintf("PDF for sample 2 with estimated λ=%0.2f and k=%0.2f", ... %! lambda_kB(1), lambda_kB(2)), ... %! sprintf("PDF for sample 3 with estimated λ=%0.2f and k=%0.2f", ... %! lambda_kC(1), lambda_kC(2))}) %! title ("Three population samples from different Weibull distibutions") %! hold off ## Test output %!test %! x = 1:50; %! [paramhat, paramci] = wblfit (x); %! paramhat_out = [28.3636, 1.7130]; %! paramci_out = [23.9531, 1.3551; 33.5861, 2.1655]; %! assert (paramhat, paramhat_out, 1e-4); %! assert (paramci, paramci_out, 1e-4); %!test %! x = 1:50; %! [paramhat, paramci] = wblfit (x, 0.01); %! paramci_out = [22.7143, 1.2589; 35.4179, 2.3310]; %! assert (paramci, paramci_out, 1e-4); ## Test input validation %!error wblfit (ones (2,5)); %!error wblfit ([-1 2 3 4]); %!error wblfit ([1, 2, 3, 4, 5], 1.2); %!error wblfit ([1, 2, 3, 4, 5], 0); %!error wblfit ([1, 2, 3, 4, 5], "alpha"); %!error ... %! wblfit ([1, 2, 3, 4, 5], 0.05, [1 1 0]); %!error ... %! wblfit ([1, 2, 3, 4, 5], [], [1 1 0 1 1]'); %!error ... %! wblfit ([1, 2, 3, 4, 5], 0.05, zeros (1,5), [1 1 0]); %!error ... %! wblfit ([1, 2, 3, 4, 5], [], [], [1 1 0 -1 1]); %!error ... %! wblfit ([1, 2, 3, 4, 5], [], [], [1 1 0 1 1]'); %!error ... %! wblfit ([1, 2, 3, 4, 5], 0.05, [], [], 2); statistics-release-1.7.3/inst/dist_fit/wbllike.m000066400000000000000000000132221475240274700217370ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR l PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{nlogL} =} wbllike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@var{nlogL}, @var{acov}] =} wbllike (@var{params}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} wbllike (@var{params}, @var{x}, @var{alpha}, @var{censor}) ## @deftypefnx {statistics} {[@dots{}] =} wbllike (@var{params}, @var{x}, @var{alpha}, @var{censor}, @var{freq}) ## ## Negative log-likelihood for the Weibull distribution. ## ## @code{@var{nlogL} = wbllike (@var{params}, @var{data})} returns the negative ## log-likelihood of the data in @var{x} corresponding to the Weibull ## distribution with (1) scale parameter @math{lambda} and (2) shape parameter ## @math{k} given in the two-element vector @var{params}. ## ## @code{[@var{nlogL}, @var{acov}] = wbllike (@var{params}, @var{data})} also ## returns the inverse of Fisher's information matrix, @var{acov}. If the input ## parameter values in @var{params} are the maximum likelihood estimates, the ## diagonal elements of @var{acov} are their asymptotic variances. @var{acov} ## is based on the observed Fisher's information, not the expected information. ## ## @code{[@dots{}] = wbllike (@var{params}, @var{data}, @var{censor})} accepts a ## boolean vector, @var{censor}, of the same size as @var{x} with @qcode{1}s for ## observations that are right-censored and @qcode{0}s for observations that are ## observed exactly. By default, or if left empty, ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @code{[@dots{}] = wbllike (@var{params}, @var{data}, @var{censor}, ## @var{freq})} accepts a frequency vector, @var{freq}, of the same size as ## @var{x}. @var{freq} typically contains integer frequencies for the ## corresponding elements in @var{x}, but may contain any non-integer ## non-negative values. By default, or if left empty, ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblcdf, wblinv, wblpdf, wblrnd, wblfit, wblstat} ## @end deftypefn function [nlogL, acov] = wbllike (params, x, censor, freq) ## Check input arguments and add defaults if (nargin < 2) error ("wbllike: too few input arguments."); endif if (numel (params) != 2) error ("wbllike: wrong parameters length."); endif if (! isvector (x)) error ("wbllike: X must be a vector."); endif if (nargin < 3 || isempty (censor)) censor = zeros (size (x)); elseif (! isequal (size (x), size (censor))) error ("wbllike: X and CENSOR vectors mismatch."); endif if (nargin < 4 || isempty (freq)) freq = ones (size (x)); elseif (any (freq < 0)) error ("wbllike: FREQ cannot have negative values."); elseif (isequal (size (x), size (freq))) nulls = find (freq == 0); if (numel (nulls) > 0) x(nulls) = []; censor(nulls) = []; freq(nulls) = []; endif else error ("wbllike: X and FREQ vectors mismatch."); endif ## Get lambda and k parameter values l = params(1); k = params(2); ## Force NaNs for out of range parameters or x. l(l <= 0) = NaN; k(k <= 0) = NaN; x(x < 0) = NaN; ## Compute the individual log-likelihood terms z = x ./ l; logz = log (z); expz = exp (k .* logz); ilogL = ((k - 1) .* logz + log (k ./ l)) .* (1 - censor) - expz; ilogL(z == Inf) = -Inf; ## Sum up the individual log-likelihood contributions nlogL = -sum (freq .* ilogL); ## Compute the negative hessian and invert to get the information matrix. if (nargout > 1) ucen = (1 - censor); nH11 = sum (freq .* (k .* ((1 + k) .* expz - ucen))) ./ l .^ 2; nH12 = -sum (freq .* (((1 + k .* logz) .* expz - ucen))) ./ l; nH22 = sum (freq .* ((logz .^ 2) .* expz + ucen ./ k .^ 2)); acov = [nH22, -nH12; -nH12, nH11] / (nH11 * nH22 - nH12 * nH12); endif endfunction ## Test output %!test %! x = 1:50; %! [nlogL, acov] = wbllike ([2.3, 1.2], x); %! avar_out = [0.0250, 0.0062; 0.0062, 0.0017]; %! assert (nlogL, 945.9589180651594, 1e-12); %! assert (acov, avar_out, 1e-4); %!test %! x = 1:50; %! [nlogL, acov] = wbllike ([2.3, 1.2], x * 0.5); %! avar_out = [-0.3238, -0.1112; -0.1112, -0.0376]; %! assert (nlogL, 424.9879809704742, 6e-14); %! assert (acov, avar_out, 1e-4); %!test %! x = 1:50; %! [nlogL, acov] = wbllike ([21, 15], x); %! avar_out = [-0.00001236, -0.00001166; -0.00001166, -0.00001009]; %! assert (nlogL, 1635190.328991511, 1e-8); %! assert (acov, avar_out, 1e-8); ## Test input validation %!error wbllike ([12, 15]); %!error wbllike ([12, 15, 3], [1:50]); %!error wbllike ([12, 3], ones (10, 2)); %!error wbllike ([12, 15], [1:50], [1, 2, 3]); %!error wbllike ([12, 15], [1:50], [], [1, 2, 3]); %!error ... %! wbllike ([12, 15], [1:5], [], [1, 2, 3, -1, 0]); statistics-release-1.7.3/inst/dist_fun/000077500000000000000000000000001475240274700201365ustar00rootroot00000000000000statistics-release-1.7.3/inst/dist_fun/betacdf.m000066400000000000000000000143311475240274700217060ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} betacdf (@var{x}, @var{a}, @var{b}) ## @deftypefnx {statistics} {@var{p} =} betacdf (@var{x}, @var{a}, @var{b}, @qcode{"upper"}) ## ## Beta cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function of ## the Beta distribution with shape parameters @var{a} and @var{b}. The size of ## @var{p} is the common size of @var{x}, @var{a}, and @var{b}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## @code{@var{p} = betacdf (@var{x}, @var{a}, @var{b}, "upper")} computes the ## upper tail probability of the Beta distribution with parameters @var{a} and ## @var{b}, at the values in @var{x}. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betainv, betapdf, betarnd, betafit, betalike, betastat} ## @end deftypefn function p = betacdf (x, a, b, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("betacdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("betacdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, A, and B if (! isscalar (x) || ! isscalar (a) || ! isscalar (b)) [err, x, a, b] = common_size (x, a, b); if (err > 0) error ("betacdf: X, A, and B must be of common size or scalars."); endif endif ## Check for X, A, and B being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b)) error ("betacdf: X, A, and B must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single")) is_type = "single"; else is_type = "double"; endif ## Find valid values in parameters and data okPARAM = (0 < a & a < Inf) & (0 < b & b < Inf); okDATA = (okPARAM & (0 <= x & x <= 1)); all_OK = all (okDATA(:)); ## Force NaNs for out of range parameters. ## Fill in edges cases when X is outside 0 or 1. if (! all_OK) p = NaN (size (okDATA), is_type); if (uflag) p(okPARAM & x <= 0) = 1; p(okPARAM & x >= 1) = 0; else p(okPARAM & x < 0) = 0; p(okPARAM & x > 1) = 1; endif ## Remove the out of range/edge cases. Return, if there's nothing left. if (any (okDATA(:))) if (numel (x) > 1) x = x(okDATA); endif if (numel (a) > 1) a = a(okDATA); endif if (numel (b) > 1) b = b(okDATA); endif else return; endif endif ## Call betainc for the actual work if (uflag) pk = betainc (x, a, b, "upper"); else pk = betainc (x, a, b); endif ## Relocate the values to the correct places if necessary. if all_OK p = pk; else p(okDATA) = pk; endif endfunction %!demo %! ## Plot various CDFs from the Beta distribution %! x = 0:0.005:1; %! p1 = betacdf (x, 0.5, 0.5); %! p2 = betacdf (x, 5, 1); %! p3 = betacdf (x, 1, 3); %! p4 = betacdf (x, 2, 2); %! p5 = betacdf (x, 2, 5); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-m") %! grid on %! legend ({"α = β = 0.5", "α = 5, β = 1", "α = 1, β = 3", ... %! "α = 2, β = 2", "α = 2, β = 5"}, "location", "northwest") %! title ("Beta CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y, x1, x2 %! x = [-1 0 0.5 1 2]; %! y = [0 0 0.75 1 1]; %!assert (betacdf (x, ones (1, 5), 2 * ones (1, 5)), y) %!assert (betacdf (x, 1, 2 * ones (1, 5)), y) %!assert (betacdf (x, ones (1, 5), 2), y) %!assert (betacdf (x, [0 1 NaN 1 1], 2), [NaN 0 NaN 1 1]) %!assert (betacdf (x, 1, 2 * [0 1 NaN 1 1]), [NaN 0 NaN 1 1]) %!assert (betacdf ([x(1:2) NaN x(4:5)], 1, 2), [y(1:2) NaN y(4:5)]) %! x1 = [0.1:0.2:0.9]; %!assert (betacdf (x1, 2, 2), [0.028, 0.216, 0.5, 0.784, 0.972], 1e-14); %!assert (betacdf (x1, 2, 2, "upper"), 1 - [0.028, 0.216, 0.5, 0.784, 0.972],... %! 1e-14); %! x2 = [1, 2, 3]; %!assert (betacdf (0.5, x2, x2), [0.5, 0.5, 0.5], 1e-14); %!assert (betacdf ([x, NaN], 1, 2), [y, NaN]) ## Test class of input preserved %!assert (betacdf (single ([x, NaN]), 1, 2), single ([y, NaN])) %!assert (betacdf ([x, NaN], single (1), 2), single ([y, NaN])) %!assert (betacdf ([x, NaN], 1, single (2)), single ([y, NaN])) ## Test input validation %!error betacdf () %!error betacdf (1) %!error betacdf (1, 2) %!error betacdf (1, 2, 3, 4, 5) %!error betacdf (1, 2, 3, "tail") %!error betacdf (1, 2, 3, 4) %!error ... %! betacdf (ones (3), ones (2), ones (2)) %!error ... %! betacdf (ones (2), ones (3), ones (2)) %!error ... %! betacdf (ones (2), ones (2), ones (3)) %!error betacdf (i, 2, 2) %!error betacdf (2, i, 2) %!error betacdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/betainv.m000066400000000000000000000132341475240274700217470ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} betainv (@var{p}, @var{a}, @var{b}) ## ## Inverse of the Beta distribution (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) ## of the Beta distribution with shape parameters @var{a} and @var{b}. The size ## of @var{x} is the common size of @var{x}, @var{a}, and @var{b}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betacdf, betapdf, betarnd, betafit, betalike, betastat} ## @end deftypefn function x = betainv (p, a, b) ## Check for valid number of input arguments if (nargin < 3) error ("betainv: function called with too few input arguments."); endif ## Check for common size of P, A, and B if (! isscalar (p) || ! isscalar (a) || ! isscalar (b)) [retval, p, a, b] = common_size (p, a, b); if (retval > 0) error ("betainv: P, A, and B must be of common size or scalars."); endif endif ## Check for P, A, and B being reals if (iscomplex (p) || iscomplex (a) || iscomplex (b)) error ("betainv: P, A, and B must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (a, "single") || isa (b, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif k = (p < 0) | (p > 1) | !(a > 0) | !(b > 0) | isnan (p); x(k) = NaN; k = (p == 1) & (a > 0) & (b > 0); x(k) = 1; k = find ((p > 0) & (p < 1) & (a > 0) & (b > 0)); if (! isempty (k)) if (! isscalar (a) || ! isscalar (b)) a = a(k); b = b(k); y = a ./ (a + b); else y = a / (a + b) * ones (size (k)); endif p = p(k); if (isa (y, "single")) myeps = eps ("single"); else myeps = eps; endif l = find (y < myeps); if (any (l)) y(l) = sqrt (myeps) * ones (length (l), 1); endif l = find (y > 1 - myeps); if (any (l)) y(l) = 1 - sqrt (myeps) * ones (length (l), 1); endif y_new = y; loopcnt = 0; do y_old = y_new; h = (betacdf (y_old, a, b) - p) ./ betapdf (y_old, a, b); y_new = y_old - h; ind = find (y_new <= myeps); if (any (ind)) y_new(ind) = y_old(ind) / 10; endif ind = find (y_new >= 1 - myeps); if (any (ind)) y_new(ind) = 1 - (1 - y_old(ind)) / 10; endif h = y_old - y_new; until (max (abs (h)) < sqrt (myeps) || ++loopcnt == 40) if (loopcnt == 40) warning ("betainv: calculation failed to converge for some values."); endif x(k) = y_new; endif endfunction %!demo %! ## Plot various iCDFs from the Beta distribution %! p = 0.001:0.001:0.999; %! x1 = betainv (p, 0.5, 0.5); %! x2 = betainv (p, 5, 1); %! x3 = betainv (p, 1, 3); %! x4 = betainv (p, 2, 2); %! x5 = betainv (p, 2, 5); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-m") %! grid on %! legend ({"α = β = 0.5", "α = 5, β = 1", "α = 1, β = 3", ... %! "α = 2, β = 2", "α = 2, β = 5"}, "location", "southeast") %! title ("Beta iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.75 1 2]; %!assert (betainv (p, ones (1,5), 2*ones (1,5)), [NaN 0 0.5 1 NaN], eps) %!assert (betainv (p, 1, 2*ones (1,5)), [NaN 0 0.5 1 NaN], eps) %!assert (betainv (p, ones (1,5), 2), [NaN 0 0.5 1 NaN], eps) %!assert (betainv (p, [1 0 NaN 1 1], 2), [NaN NaN NaN 1 NaN]) %!assert (betainv (p, 1, 2*[1 0 NaN 1 1]), [NaN NaN NaN 1 NaN]) %!assert (betainv ([p(1:2) NaN p(4:5)], 1, 2), [NaN 0 NaN 1 NaN]) ## Test class of input preserved %!assert (betainv ([p, NaN], 1, 2), [NaN 0 0.5 1 NaN NaN], eps) %!assert (betainv (single ([p, NaN]), 1, 2), single ([NaN 0 0.5 1 NaN NaN])) %!assert (betainv ([p, NaN], single (1), 2), single ([NaN 0 0.5 1 NaN NaN]), eps("single")) %!assert (betainv ([p, NaN], 1, single (2)), single ([NaN 0 0.5 1 NaN NaN]), eps("single")) ## Test input validation %!error betainv () %!error betainv (1) %!error betainv (1,2) %!error betainv (1,2,3,4) %!error ... %! betainv (ones (3), ones (2), ones (2)) %!error ... %! betainv (ones (2), ones (3), ones (2)) %!error ... %! betainv (ones (2), ones (2), ones (3)) %!error betainv (i, 2, 2) %!error betainv (2, i, 2) %!error betainv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/betapdf.m000066400000000000000000000133351475240274700217260ustar00rootroot00000000000000## Copyright (C) 2010 Christos Dimitrakakis ## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} betapdf (@var{x}, @var{a}, @var{b}) ## ## Beta probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Beta distribution with shape parameters @var{a} and @var{b}. The size ## of @var{y} is the common size of @var{x}, @var{a}, and @var{b}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betacdf, betainv, betarnd, betafit, betalike, betastat} ## @end deftypefn function y = betapdf (x, a, b) ## Check for valid number of input arguments if (nargin < 3) error ("betapdf: function called with too few input arguments."); endif ## Check for common size of X, A, and B if (! isscalar (x) || ! isscalar (a) || ! isscalar (b)) [retval, x, a, b] = common_size (x, a, b); if (retval > 0) error ("betapdf: X, A, and B must be of common size or scalars."); endif endif ## Check for X, A, and B being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b)) error ("betapdf: X, A, and B must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single")); y = zeros (size (x), "single"); else y = zeros (size (x)); endif k = !(a > 0) | !(b > 0) | isnan (x); y(k) = NaN; k = (x > 0) & (x < 1) & (a > 0) & (b > 0) & ((a != 1) | (b != 1)); if (isscalar (a) && isscalar (b)) y(k) = exp ((a - 1) * log (x(k)) + (b - 1) * log (1 - x(k)) + gammaln (a + b) - gammaln (a) - gammaln (b)); else y(k) = exp ((a(k) - 1) .* log (x(k)) + (b(k) - 1) .* log (1 - x(k)) + gammaln (a(k) + b(k)) - gammaln (a(k)) - gammaln (b(k))); endif ## Most important special cases when the density is finite. k = (x == 0) & (a == 1) & (b > 0) & (b != 1); if (isscalar (a) && isscalar (b)) y(k) = exp (gammaln (a + b) - gammaln (a) - gammaln (b)); else y(k) = exp (gammaln (a(k) + b(k)) - gammaln (a(k)) - gammaln (b(k))); endif k = (x == 1) & (b == 1) & (a > 0) & (a != 1); if (isscalar (a) && isscalar (b)) y(k) = exp (gammaln (a + b) - gammaln (a) - gammaln (b)); else y(k) = exp (gammaln (a(k) + b(k)) - gammaln (a(k)) - gammaln (b(k))); endif k = (x >= 0) & (x <= 1) & (a == 1) & (b == 1); y(k) = 1; ## Other special case when the density at the boundary is infinite. k = (x == 0) & (a < 1); y(k) = Inf; k = (x == 1) & (b < 1); y(k) = Inf; endfunction %!demo %! ## Plot various PDFs from the Beta distribution %! x = 0.001:0.001:0.999; %! y1 = betapdf (x, 0.5, 0.5); %! y2 = betapdf (x, 5, 1); %! y3 = betapdf (x, 1, 3); %! y4 = betapdf (x, 2, 2); %! y5 = betapdf (x, 2, 5); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-m") %! grid on %! ylim ([0, 2.5]) %! legend ({"α = β = 0.5", "α = 5, β = 1", "α = 1, β = 3", ... %! "α = 2, β = 2", "α = 2, β = 5"}, "location", "north") %! title ("Beta PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2]; %! y = [0 2 1 0 0]; %!assert (betapdf (x, ones (1, 5), 2 * ones (1, 5)), y) %!assert (betapdf (x, 1, 2 * ones (1, 5)), y) %!assert (betapdf (x, ones (1, 5), 2), y) %!assert (betapdf (x, [0 NaN 1 1 1], 2), [NaN NaN y(3:5)]) %!assert (betapdf (x, 1, 2 * [0 NaN 1 1 1]), [NaN NaN y(3:5)]) %!assert (betapdf ([x, NaN], 1, 2), [y, NaN]) ## Test class of input preserved %!assert (betapdf (single ([x, NaN]), 1, 2), single ([y, NaN])) %!assert (betapdf ([x, NaN], single (1), 2), single ([y, NaN])) %!assert (betapdf ([x, NaN], 1, single (2)), single ([y, NaN])) ## Beta (1/2,1/2) == arcsine distribution %!test %! x = rand (10,1); %! y = 1 ./ (pi * sqrt (x .* (1 - x))); %! assert (betapdf (x, 1/2, 1/2), y, 1e-12); ## Test large input values to betapdf %!assert (betapdf (0.5, 1000, 1000), 35.678, 1e-3) ## Test input validation %!error betapdf () %!error betapdf (1) %!error betapdf (1,2) %!error betapdf (1,2,3,4) %!error ... %! betapdf (ones (3), ones (2), ones (2)) %!error ... %! betapdf (ones (2), ones (3), ones (2)) %!error ... %! betapdf (ones (2), ones (2), ones (3)) %!error betapdf (i, 2, 2) %!error betapdf (2, i, 2) %!error betapdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/betarnd.m000066400000000000000000000161501475240274700217360ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} betarnd (@var{a}, @var{b}) ## @deftypefnx {statistics} {@var{r} =} betarnd (@var{a}, @var{b}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} betarnd (@var{a}, @var{b}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} betarnd (@var{a}, @var{b}, [@var{sz}]) ## ## Random arrays from the Beta distribution. ## ## @code{@var{r} = betarnd (@var{a}, @var{b})} returns an array of random ## numbers chosen from the Beta distribution with shape parameters @var{a} and ## @var{b}. The size of @var{r} is the common size of @var{a} and @var{b}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betacdf, betainv, betapdf, betafit, betalike, betastat} ## @end deftypefn function r = betarnd (a, b, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("betarnd: function called with too few input arguments."); endif ## Check for common size of A and B if (! isscalar (a) || ! isscalar (b)) [retval, a, b] = common_size (a, b); if (retval > 0) error ("betarnd: A and B must be of common size or scalars."); endif endif ## Check for A and B being reals if (iscomplex (a) || iscomplex (b)) error ("betarnd: A and B must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (a); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["betarnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("betarnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (a) && ! isequal (size (a), sz)) error ("betarnd: A and B must be scalars or of size SZ."); endif ## Check for class type if (isa (a, "single") || isa (b, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Beta distribution if (isscalar (a) && isscalar (b)) if ((a > 0) && (a < Inf) && (b > 0) && (b < Inf)) tmpr = randg (a, sz, cls); r = tmpr ./ (tmpr + randg (b, sz, cls)); else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (a > 0) & (a < Inf) & (b > 0) & (b < Inf); tmpr = randg (a(k), cls); r(k) = tmpr ./ (tmpr + randg (b(k), cls)); endif endfunction ## Test output %!assert (size (betarnd (2, 1/2)), [1 1]) %!assert (size (betarnd (2 * ones (2, 1), 1/2)), [2, 1]) %!assert (size (betarnd (2 * ones (2, 2), 1/2)), [2, 2]) %!assert (size (betarnd (2, 1/2 * ones (2, 1))), [2, 1]) %!assert (size (betarnd (1, 1/2 * ones (2, 2))), [2, 2]) %!assert (size (betarnd (ones (2, 1), 1)), [2, 1]) %!assert (size (betarnd (ones (2, 2), 1)), [2, 2]) %!assert (size (betarnd (2, 1/2, 3)), [3, 3]) %!assert (size (betarnd (1, 1, [4, 1])), [4, 1]) %!assert (size (betarnd (1, 1, 4, 1)), [4, 1]) %!assert (size (betarnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (betarnd (1, 1, 0, 1)), [0, 1]) %!assert (size (betarnd (1, 1, 1, 0)), [1, 0]) %!assert (size (betarnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (betarnd (1, 1)), "double") %!assert (class (betarnd (1, single (0))), "single") %!assert (class (betarnd (1, single ([0, 0]))), "single") %!assert (class (betarnd (1, single (1), 2)), "single") %!assert (class (betarnd (1, single ([1, 1]), 1, 2)), "single") %!assert (class (betarnd (single (1), 1, 2)), "single") %!assert (class (betarnd (single ([1, 1]), 1, 1, 2)), "single") ## Test input validation %!error betarnd () %!error betarnd (1) %!error ... %! betarnd (ones (3), ones (2)) %!error ... %! betarnd (ones (2), ones (3)) %!error betarnd (i, 2) %!error betarnd (1, i) %!error ... %! betarnd (1, 1/2, -1) %!error ... %! betarnd (1, 1/2, 1.2) %!error ... %! betarnd (1, 1/2, ones (2)) %!error ... %! betarnd (1, 1/2, [2 -1 2]) %!error ... %! betarnd (1, 1/2, [2 0 2.5]) %!error ... %! betarnd (1, 1/2, 2, -1, 5) %!error ... %! betarnd (1, 1/2, 2, 1.5, 5) %!error ... %! betarnd (2, 1/2 * ones (2), 3) %!error ... %! betarnd (2, 1/2 * ones (2), [3, 2]) %!error ... %! betarnd (2, 1/2 * ones (2), 3, 2) %!error ... %! betarnd (2 * ones (2), 1/2, 3) %!error ... %! betarnd (2 * ones (2), 1/2, [3, 2]) %!error ... %! betarnd (2 * ones (2), 1/2, 3, 2) statistics-release-1.7.3/inst/dist_fun/binocdf.m000066400000000000000000000142271475240274700217260ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} binocdf (@var{x}, @var{n}, @var{ps}) ## @deftypefnx {statistics} {@var{p} =} binocdf (@var{x}, @var{n}, @var{ps}, @qcode{"upper"}) ## ## Binomial cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the binomial distribution with parameters @var{n} and @var{ps}, ## where @var{n} is the number of trials and @var{ps} is the probability of ## success. The size of @var{p} is the common size of @var{x}, @var{n}, and ## @var{ps}. A scalar input functions as a constant matrix of the same size as ## the other inputs. ## ## @code{@var{p} = binocdf (@var{x}, @var{n}, @var{ps}, "upper")} computes the ## upper tail probability of the binomial distribution with parameters ## @var{n} and @var{ps}, at the values in @var{x}. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binoinv, binopdf, binornd, binofit, binolike, binostat, binotest} ## @end deftypefn function p = binocdf (x, n, ps, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("binocdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin == 4) if (strcmp (uflag, "upper")) uflag = true; else error ("binocdf: invalid argument for upper tail."); endif else uflag = false; endif ## Check for common size of X, N, and PS if (! isscalar (x) || ! isscalar (n) || ! isscalar (ps)) [retval, x, n, ps] = common_size (x, n, ps); if (retval > 0) error ("binocdf: X, N, and PS must be of common size or scalars."); endif endif ## Check for X, N, and PS being reals if (iscomplex (x) || iscomplex (n) || iscomplex (ps)) error ("binocdf: X, N, and PS must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (n, "single") || isa (ps, "single")); p = nan (size (x), "single"); else p = nan (size (x)); endif k = (x >= n) & (n >= 0) & (n == fix (n) & (ps >= 0) & (ps <= 1)); p(k) = !uflag; k = (x < 0) & (n >= 0) & (n == fix (n) & (ps >= 0) & (ps <= 1)); p(k) = uflag; k = (x >= 0) & (x < n) & (n == fix (n)) & (ps >= 0) & (ps <= 1); tmp = floor (x(k)); if (! uflag) if (isscalar (n) && isscalar (ps)) p(k) = betainc (1 - ps, n - tmp, tmp + 1); else p(k) = betainc (1 - ps(k), n(k) - tmp, tmp + 1); endif else if (isscalar (n) && isscalar (ps)); p(k) = betainc (ps, tmp + 1, n - tmp); else p(k) = betainc (ps(k), tmp + 1, n(k) - tmp); endif endif endfunction %!demo %! ## Plot various CDFs from the binomial distribution %! x = 0:40; %! p1 = binocdf (x, 20, 0.5); %! p2 = binocdf (x, 20, 0.7); %! p3 = binocdf (x, 40, 0.5); %! plot (x, p1, "*b", x, p2, "*g", x, p3, "*r") %! grid on %! legend ({"n = 20, ps = 0.5", "n = 20, ps = 0.7", ... %! "n = 40, ps = 0.5"}, "location", "southeast") %! title ("Binomial CDF") %! xlabel ("values in x (number of successes)") %! ylabel ("probability") ## Test output %!shared x, p, p1 %! x = [-1 0 1 2 3]; %! p = [0 1/4 3/4 1 1]; %! p1 = 1 - p; %!assert (binocdf (x, 2 * ones (1, 5), 0.5 * ones (1, 5)), p, eps) %!assert (binocdf (x, 2, 0.5 * ones (1, 5)), p, eps) %!assert (binocdf (x, 2 * ones (1, 5), 0.5), p, eps) %!assert (binocdf (x, 2 * [0 -1 NaN 1.1 1], 0.5), [0 NaN NaN NaN 1]) %!assert (binocdf (x, 2, 0.5 * [0 -1 NaN 3 1]), [0 NaN NaN NaN 1]) %!assert (binocdf ([x(1:2) NaN x(4:5)], 2, 0.5), [p(1:2) NaN p(4:5)], eps) %!assert (binocdf (99, 100, 0.1, "upper"), 1e-100, 1e-112); %!assert (binocdf (x, 2 * ones (1, 5), 0.5*ones (1,5), "upper"), p1, eps) %!assert (binocdf (x, 2, 0.5 * ones (1, 5), "upper"), p1, eps) %!assert (binocdf (x, 2 * ones (1, 5), 0.5, "upper"), p1, eps) %!assert (binocdf (x, 2 * [0 -1 NaN 1.1 1], 0.5, "upper"), [1 NaN NaN NaN 0]) %!assert (binocdf (x, 2, 0.5 * [0 -1 NaN 3 1], "upper"), [1 NaN NaN NaN 0]) %!assert (binocdf ([x(1:2) NaN x(4:5)], 2, 0.5, "upper"), [p1(1:2) NaN p1(4:5)]) %!assert (binocdf ([x, NaN], 2, 0.5), [p, NaN], eps) ## Test class of input preserved %!assert (binocdf (single ([x, NaN]), 2, 0.5), single ([p, NaN])) %!assert (binocdf ([x, NaN], single (2), 0.5), single ([p, NaN])) %!assert (binocdf ([x, NaN], 2, single (0.5)), single ([p, NaN])) ## Test input validation %!error binocdf () %!error binocdf (1) %!error binocdf (1, 2) %!error binocdf (1, 2, 3, 4, 5) %!error binocdf (1, 2, 3, "tail") %!error binocdf (1, 2, 3, 4) %!error ... %! binocdf (ones (3), ones (2), ones (2)) %!error ... %! binocdf (ones (2), ones (3), ones (2)) %!error ... %! binocdf (ones (2), ones (2), ones (3)) %!error binocdf (i, 2, 2) %!error binocdf (2, i, 2) %!error binocdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/binoinv.m000066400000000000000000000202541475240274700217630ustar00rootroot00000000000000## Copyright (C) 2016-2017 Lachlan Andrew ## Copyright (C) 2012-2016 Rik Wehbring ## Copyright (C) 1995-2012 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} binoinv (@var{p}, @var{n}, @var{ps}) ## ## Inverse of the Binomial cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the binomial distribution with parameters @var{n} and @var{ps}, where @var{n} ## is the number of trials and @var{ps} is the probability of success. The size ## of @var{x} is the common size of @var{p}, @var{n}, and @var{ps}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binocdf, binopdf, binornd, binofit, binolike, binostat, binotest} ## @end deftypefn function x = binoinv (p, n, ps) ## Check for valid number of input arguments if (nargin < 3) error ("binoinv: function called with too few input arguments."); endif ## Check for common size of P, N, and PS if (! isscalar (n) || ! isscalar (ps)) [retval, p, n, ps] = common_size (p, n, ps); if (retval > 0) error ("binoinv: P, N, and PS must be of common size or scalars."); endif endif ## Check for P, N, and PS being reals if (iscomplex (p) || iscomplex (n) || iscomplex (ps)) error ("binoinv: P, N, and PS must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (n, "single") || isa (ps, "single")); x = zeros (size (p), "single"); else x = zeros (size (p)); endif k = (! (p >= 0) | ! (p <= 1) | ! (n >= 0) | (n != fix (n)) | ! (ps >= 0) | ... ! (ps <= 1)); x(k) = NaN; k = find ((p >= 0) & (p <= 1) & (n >= 0) & (n == fix (n) ... & (ps >= 0) & (ps <= 1))); if (! isempty (k)) p = p(k); if (isscalar (n) && isscalar (ps)) [x(k), unfinished] = scalar_binoinv (p(:), n, ps); k = k(unfinished); if (! isempty (k)) x(k) = bin_search_binoinv (p(k), n, ps); endif else [x(k), unfinished] = vector_binoinv (p(:), n(:), ps(:)); k = k(unfinished); if (! isempty (k)) x(k) = bin_search_binoinv (p(k), n(k), ps(k)); endif endif endif endfunction ## Core algorithm to calculate the inverse binomial, for n and ps real scalars ## and x a column vector, and for which the output is not NaN or Inf. ## Compute CDF in batches of doubling size until CDF > p, or answer > 500 ## Return the locations of unfinished cases in k. function [m, k] = scalar_binoinv (p, n, ps) k = 1:length (p); m = zeros (size (p)); prev_limit = 0; limit = 10; cdf = 0; v = 0; do cdf = binocdf (prev_limit:limit-1, n, ps); r = bsxfun (@le, p(k), cdf); [v, m(k)] = max (r, [], 2); # find first instance of p <= cdf m(k) += prev_limit - 1; k = k(v == 0); prev_limit = limit; limit += limit; until (isempty (k) || limit >= 1000) endfunction ## Core algorithm to calculate the inverse binomial, for n, ps, and x column ## vectors, and for which the output is not NaN or Inf. ## Compute CDF in batches of doubling size until CDF > p, or answer > 500 ## Return the locations of unfinished cases in k. ## Calculates CDF by summing PDF, which is faster than calls to binocdf. function [m, k] = vector_binoinv (p, n, ps) k = 1:length(p); m = zeros (size (p)); prev_limit = 0; limit = 10; cdf = 0; v = 0; do xx = repmat (prev_limit:limit-1, [length(k), 1]); nn = kron (ones (1, limit-prev_limit), n(k)); pp = kron (ones (1, limit-prev_limit), ps(k)); pdf = binopdf (xx, nn, pp); pdf(:,1) += cdf(v==0, end); cdf = cumsum (pdf, 2); r = bsxfun (@le, p(k), cdf); [v, m(k)] = max (r, [], 2); # find first instance of p <= cdf m(k) += prev_limit - 1; k = k(v == 0); prev_limit = limit; limit += min (limit, max (1e4/numel (k), 10)); # limit memory use until (isempty (k) || limit >= 1000) endfunction ## Vectorized binary search. ## Can handle vectors n and ps, and is faster than the scalar case when the ## answer is large. ## Could be optimized to call binocdf only for a subset of the p at each stage, ## but care must be taken to handle both scalar and vector n, ps. Bookkeeping ## may cost more than the extra computations. function m = bin_search_binoinv (p, n, ps) k = 1:length (p); lower = zeros (size (p)); limit = 500; # lower bound on point at which prev phase finished while (any (k) && limit < 1e100) cdf = binocdf (limit, n, ps); k = (p > cdf); lower(k) = limit; limit += limit; endwhile upper = max (2*lower, 1); k = find (lower != limit/2); # elements for which above loop finished for i = 1:ceil (log2 (max (lower))) mid = (upper + lower)/2; cdf = binocdf (floor(mid(:)), n, ps); r = (p <= cdf); upper(r) = mid(r); lower(! r) = mid(! r); endfor m = ceil (lower); m(p > binocdf (m(:), n, ps)) += 1; # fix off-by-one errors from binary search endfunction %!demo %! ## Plot various iCDFs from the binomial distribution %! p = 0.001:0.001:0.999; %! x1 = binoinv (p, 20, 0.5); %! x2 = binoinv (p, 20, 0.7); %! x3 = binoinv (p, 40, 0.5); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r") %! grid on %! legend ({"n = 20, ps = 0.5", "n = 20, ps = 0.7", ... %! "n = 40, ps = 0.5"}, "location", "southeast") %! title ("Binomial iCDF") %! xlabel ("probability") %! ylabel ("values in x (number of successes)") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (binoinv (p, 2*ones (1,5), 0.5*ones (1,5)), [NaN 0 1 2 NaN]) %!assert (binoinv (p, 2, 0.5*ones (1,5)), [NaN 0 1 2 NaN]) %!assert (binoinv (p, 2*ones (1,5), 0.5), [NaN 0 1 2 NaN]) %!assert (binoinv (p, 2*[0 -1 NaN 1.1 1], 0.5), [NaN NaN NaN NaN NaN]) %!assert (binoinv (p, 2, 0.5*[0 -1 NaN 3 1]), [NaN NaN NaN NaN NaN]) %!assert (binoinv ([p(1:2) NaN p(4:5)], 2, 0.5), [NaN 0 NaN 2 NaN]) ## Test class of input preserved %!assert (binoinv ([p, NaN], 2, 0.5), [NaN 0 1 2 NaN NaN]) %!assert (binoinv (single ([p, NaN]), 2, 0.5), single ([NaN 0 1 2 NaN NaN])) %!assert (binoinv ([p, NaN], single (2), 0.5), single ([NaN 0 1 2 NaN NaN])) %!assert (binoinv ([p, NaN], 2, single (0.5)), single ([NaN 0 1 2 NaN NaN])) ## Test accuracy, to within +/- 1 since it is a discrete distribution %!shared x, tol %! x = magic (3) + 1; %! tol = 1; %!assert (binoinv (binocdf (1:10, 11, 0.1), 11, 0.1), 1:10, tol) %!assert (binoinv (binocdf (1:10, 2*(1:10), 0.1), 2*(1:10), 0.1), 1:10, tol) %!assert (binoinv (binocdf (x, 2*x, 1./x), 2*x, 1./x), x, tol) ## Test input validation %!error binoinv () %!error binoinv (1) %!error binoinv (1,2) %!error binoinv (1,2,3,4) %!error ... %! binoinv (ones (3), ones (2), ones (2)) %!error ... %! binoinv (ones (2), ones (3), ones (2)) %!error ... %! binoinv (ones (2), ones (2), ones (3)) %!error binoinv (i, 2, 2) %!error binoinv (2, i, 2) %!error binoinv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/binopdf.m000066400000000000000000000271761475240274700217520ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2021 Nicholas R. Jankowski ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} binopdf (@var{x}, @var{n}, @var{ps}) ## ## Binomial probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the binomial distribution with parameters @var{n} and @var{ps}, where ## @var{n} is the number of trials and @var{ps} is the probability of success. ## The size of @var{y} is the common size of @var{x}, @var{n}, and @var{ps}. A ## scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## Matlab incompatibility: Octave's @code{binopdf} does not allow complex ## input values. Matlab 2021b returns values for complex inputs despite the ## documentation indicates integer and real value inputs are required. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binocdf, binoinv, binornd, binofit, binolike, binostat, binotest} ## @end deftypefn function y = binopdf (x, n, ps) ## Check for valid number of input arguments if (nargin < 3) error ("binopdf: function called with too few input arguments."); endif ## Check for common size of X, N, and PS if (! isscalar (x) || ! isscalar (n) || ! isscalar (ps)) [retval, x, n, ps] = common_size (x, n, ps); if (retval > 0) error ("binopdf: X, N, and PS must be of common size or scalars."); endif endif ## Check for X, N, and PS being reals if (iscomplex (x) || iscomplex (n) || iscomplex (ps)) error ("binopdf: X, N, and PS must not be complex."); endif sz_x = size (x); # save original size for reshape later x = x(:); n = n(:); ps = ps(:); # columns for easier vectorization ## Initialize output, preserve class of output if any are singles if (isa (x, "single") || isa (n, "single") || isa (ps, "single")); y = zeros (numel (x), 1, "single"); else y = zeros (numel (x), 1); endif ## k - index of array locations needing calculation k = (x == fix (x)) & (n == fix (n)) & (n >= 0) & (ps >= 0) & (ps <= 1) ... & (x >= 0) & (x <= n); nx = n - x; q = 1 - ps; ## Catch special cases ahead of calculations: ## Matlab incompatibility: Matlab 2021b returns values for complex inputs ## despite documentation indicating integer and real value inputs required. ## Octave chooses to return an NaN instead. catch_special = (iscomplex (x) | iscomplex (n) | iscomplex (ps)); k(catch_special) = false; y(catch_special) = NaN; ## x = 0 and x = n cases where ps != 0 or 1, respectivly ## remove them from k, use alternate calculation to avoid /0 catch_special = (x == 0)& (! catch_special); k(catch_special) = false; y(catch_special) = exp (n(catch_special) .* log (q(catch_special))); catch_special = (nx == 0) & (! catch_special); k(catch_special) = false; y(catch_special) = exp (n(catch_special) .* log (ps(catch_special))); ## Perform Loader pdf calculation on non-trivial elements if (any (k)) y(k) = loader_expansion (x(k), n(k), ps(k), nx(k), q(k)); endif ## Trivial case special outputs: ksp = ((ps == 0) & (x == 0)) | (ps == 1) & (x == n); y(ksp) = 1; ## Input NaN, n not pos int, or ps outside [0,1], ## set output to NaN (overrides 0 or 1) ksp = (n ~= fix (n)) | (n < 0) | (ps < 0) | (ps > 1) | isnan (x) ... | isnan (n) | isnan (ps); y(ksp) = NaN; y = reshape (y, sz_x); ## restore output to input shape endfunction function y = loader_expansion (x, n, ps, nx, q) ## Precalculated constants, d_n from n = 0 to 30 ## extended from Loader using octave symbolic vpa ## out to n = 30 d_n = [ 0.08106146679532725821967026359438236013860, 0.04134069595540929409382208140711750802535, 0.02767792568499833914878929274624466659538, 0.02079067210376509311152277176784865633309, 0.01664469118982119216319486537359339114739, 0.01387612882307074799874572702376290856175, 0.01189670994589177009505572411765943862013, 0.01041126526197209649747856713253462919952, 0.00925546218271273291772863663310013611743, 0.00833056343336287125646931865962855220929, 0.00757367548795184079497202421159508389293, 0.00694284010720952986566415266347536265992, 0.00640899418800420706843963108297831257520, 0.00595137011275884773562441604646945832642, 0.00555473355196280137103868995979228464907, 0.00520765591960964044071799685790189865099, 0.00490139594843473786071681819096755442865, 0.00462915374933402859242721316419232323878, 0.00438556024923232426828773634861946570116, 0.00416631969199692245746292338221831613633, 0.00396795421864085961728763680734281467287, 0.00378761806844443457786667706893349200129, 0.00362296022468309470738119836390285473489, 0.00347202138297876696294511542270952959204, 0.00333315563672809287580701911737271025035, 0.00320497022805503801118415655381541759643, 0.00308627868260877706325624133564397946129, 0.00297606398355040882602116255686080370692, 0.00287344936235246638755235148906672207372, 0.00277767492975269360359490376220667282839 ]; stored_dn = numel(d_n); ## Indices for precalculated vs to-be-calculated values n_precalc = (n > 0) & (n < stored_dn); x_precalc = (x > 0) & (x < stored_dn); nx_precalc = (nx > 0) & (nx < stored_dn); [delta_n, delta_x, delta_nx] = deal (zeros (size (x))); ## Fetch precalculated values delta_n(n_precalc) = d_n(n(n_precalc)); delta_x(x_precalc) = d_n(x(x_precalc)); delta_nx(nx_precalc) = d_n(nx(nx_precalc)); ## Calculate any other d(n) values delta_n(!n_precalc) = delta_fn (n(!n_precalc)); delta_x(!x_precalc) = delta_fn (x(!x_precalc)); delta_nx(!nx_precalc) = delta_fn (nx(!nx_precalc)); ## Calculate exp(log(pdf)); y = exp ((delta_n - delta_x - delta_nx - ... deviance (x, n .* ps) - ... deviance (nx, n .* q)) - ... 0.5 * (log(2*pi) + log (x) + log (1-x./n))); endfunction function y = delta_fn (n) ## Stirling formula error term approximations based on Loader paper. ## exact expression, n^n overflows to Inf for n > ~145: ## = log (n!*exp(n)/(sqrt(2pi*n)*n^n)); ## ## Rewritten to avoid overflow out to n> 1e305. accurate to ~10^-12 ## = n + gammaln (n+1) - (n+0.5) * log(n) - log(2*pi)/2; ## ## Approximated as: ## accurate to ~10^-16 for n=30. underflow to 0 at n~10^309 ## = 1/(12n)-1/(360n^3)+1/(1260n^5)- 1/(1680n.^7)+1/(1188n^9) + O(n^-11); ## ## Factored to reduced operation count. Used by Loader and in R: ## 25% faster than unfactored form. ## =(1/12-(1/360-(1/1260-(1/1680-(1/1188)/n^2)/n^2)/n^2)/n^2)/n; nn = n.^2; y = (0.08333333333333333333333333333333333333333 - ... (0.00277777777777777777777777777777777777778 - ... (0.00079365079365079365079365079365079365079 - ... (0.00059523809523809523809523809523809523810 - ... (0.00084175084175084175084175084175084175084)./nn)./nn)./nn)./nn)./n; endfunction function D = deviance (x, np) ## requires equal length column inputs epsilon = x ./ np; v = (epsilon - 1) ./ (epsilon + 1); vtest = abs (v) < 0.1; if (any (vtest)) ## For abs(v) < 0.1, do taylor expansion for higher precision. Expansion ## term: v^(2j+1)/(2j+1). For abs(v)< 0.1, term drops slowest for max ## abs(v) = 0.1. (n+1)th term is <= 10. jmax = 12; two_jpone = 2 * [1:jmax] + 1; # sum term 2*j+1 (row vector expansion) D = zeros (numel (epsilon), 1); ## D = (x-np)*v + 2*x*sum_over_j(v^2j+1 / 2j+1) D(vtest) = (x(vtest) - np(vtest)) .* v(vtest) + 2 .* x(vtest) .* ... sum (v(vtest).^(two_jpone) ./ two_jpone, 2); D(! vtest) = x(! vtest) .* (log (epsilon(! vtest)) - 1) + np(! vtest); else D = x.* (log (epsilon) - 1) + np; endif endfunction %!demo %! ## Plot various PDFs from the binomial distribution %! x = 0:40; %! y1 = binopdf (x, 20, 0.5); %! y2 = binopdf (x, 20, 0.7); %! y3 = binopdf (x, 40, 0.5); %! plot (x, y1, "*b", x, y2, "*g", x, y3, "*r") %! grid on %! ylim ([0, 0.25]) %! legend ({"n = 20, ps = 0.5", "n = 20, ps = 0.7", ... %! "n = 40, ps = 0.5"}, "location", "northeast") %! title ("Binomial PDF") %! xlabel ("values in x (number of successes)") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 1 2 3]; %! y = [0 1/4 1/2 1/4 0]; %!assert (binopdf (x, 2 * ones (1, 5), 0.5 * ones (1, 5)), y, eps) %!assert (binopdf (x, 2, 0.5 * ones (1, 5)), y, eps) %!assert (binopdf (x, 2 * ones (1, 5), 0.5), y, eps) %!assert (binopdf (x, 2 * [0 -1 NaN 1.1 1], 0.5), [0 NaN NaN NaN 0]) %!assert (binopdf (x, 2, 0.5 * [0 -1 NaN 3 1]), [0 NaN NaN NaN 0]) %!assert (binopdf ([x, NaN], 2, 0.5), [y, NaN], eps) %!assert (binopdf (cat (3, x, x), 2, 0.5), cat (3, y, y), eps) ## Test Special input values %!assert (binopdf (1, 1, 1), 1) %!assert (binopdf (0, 3, 0), 1) %!assert (binopdf (2, 2, 1), 1) %!assert (binopdf (1, 2, 1), 0) %!assert (binopdf (0, 1.1, 0), NaN) %!assert (binopdf (1, 2, -1), NaN) %!assert (binopdf (1, 2, 1.5), NaN) ## Test empty inputs %!assert (binopdf ([], 1, 1), []) %!assert (binopdf (1, [], 1), []) %!assert (binopdf (1, 1, []), []) %!assert (binopdf (ones (1, 0), 2, .5), ones(1, 0)) %!assert (binopdf (ones (0, 1), 2, .5), ones(0, 1)) %!assert (binopdf (ones (0, 1, 2), 2, .5), ones(0, 1, 2)) %!assert (binopdf (1, ones (0, 1, 2), .5), ones(0, 1, 2)) %!assert (binopdf (1, 2, ones (0, 1, 2)), ones(0, 1, 2)) %!assert (binopdf (ones (1, 0, 2), 2, .5), ones(1, 0, 2)) %!assert (binopdf (ones (1, 2, 0), 2, .5), ones(1, 2, 0)) %!assert (binopdf (ones (0, 1, 2), NaN, .5), ones(0, 1, 2)) %!assert (binopdf (ones (0, 1, 2), 2, NaN), ones(0, 1, 2)) ## Test class of input preserved %!assert (binopdf (single ([x, NaN]), 2, 0.5), single ([y, NaN])) %!assert (binopdf ([x, NaN], single (2), 0.5), single ([y, NaN])) %!assert (binopdf ([x, NaN], 2, single (0.5)), single ([y, NaN])) ## Test input validation %!error binopdf () %!error binopdf (1) %!error binopdf (1, 2) %!error binopdf (1, 2, 3, 4) %!error ... %! binopdf (ones (3), ones (2), ones (2)) %!error ... %! binopdf (ones (2), ones (3), ones (2)) %!error ... %! binopdf (ones (2), ones (2), ones (3)) %!error binopdf (i, 2, 2) %!error binopdf (2, i, 2) %!error binopdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/binornd.m000066400000000000000000000173311475240274700217540ustar00rootroot00000000000000## Copyright (C) 2015 Michael Leitner ## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} binornd (@var{n}, @var{ps}) ## @deftypefnx {statistics} {@var{r} =} binornd (@var{n}, @var{ps}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} binornd (@var{n}, @var{ps}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} binornd (@var{n}, @var{ps}, [@var{sz}]) ## ## Random arrays from the Binomial distribution. ## ## @code{@var{r} = binornd (@var{n}, @var{ps})} returns a matrix of random ## samples from the binomial distribution with parameters @var{n} and @var{ps}, ## where @var{n} is the number of trials and @var{ps} is the probability of ## success. The size of @var{r} is the common size of @var{n} and @var{ps}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## When called with a single size argument, @code{binornd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binocdf, binoinv, binopdf, binofit, binolike, binostat, binotest} ## @end deftypefn function r = binornd (n, ps, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("binornd: function called with too few input arguments."); endif ## Check for common size of N and PS if (! isscalar (n) || ! isscalar (ps)) [retval, n, ps] = common_size (n, ps); if (retval > 0) error ("binornd: N and PS must be of common size or scalars."); endif endif ## Check for N and PS being reals if (iscomplex (n) || iscomplex (ps)) error ("binornd: N and PS must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (n); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["binornd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("binornd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (n) && ! isequal (size (n), sz)) error ("binornd: N and PS must be scalars or of size SZ."); endif ## Check for class type if (isa (n, "single") || isa (ps, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from binomial distribution if (isscalar (n) && isscalar (ps)) if ((n > 0) && (n < Inf) && (n == fix (n)) && (ps >= 0) && (ps <= 1)) nel = prod (sz); tmp = rand (n, nel); r = sum (tmp < ps, 1); r = reshape (r, sz); if (strcmp (cls, "single")) r = single (r); endif elseif ((n == 0) && (ps >= 0) && (ps <= 1)) r = zeros (sz, cls); else r = NaN (sz, cls); endif else r = zeros (sz, cls); k = !(n >= 0) | !(n < Inf) | !(n == fix (n)) | !(ps >= 0) | !(ps <= 1); r(k) = NaN; k = (n > 0) & (n < Inf) & (n == fix (n)) & (ps >= 0) & (ps <= 1); if (any (k(:))) L = sum (k(:)); ind = repelems ((1 : L), [(1 : L); n(k)(:)'])'; p_ext = ps(k)(ind)(:); r(k) = accumarray (ind, rand (sum(n(k)(:)), 1) < p_ext); endif endif endfunction ## Test output %!assert (size (binornd (2, 1/2)), [1 1]) %!assert (size (binornd (2 * ones (2, 1), 1/2)), [2, 1]) %!assert (size (binornd (2 * ones (2, 2), 1/2)), [2, 2]) %!assert (size (binornd (2, 1/2 * ones (2, 1))), [2, 1]) %!assert (size (binornd (1, 1/2 * ones (2, 2))), [2, 2]) %!assert (size (binornd (ones (2, 1), 1)), [2, 1]) %!assert (size (binornd (ones (2, 2), 1)), [2, 2]) %!assert (size (binornd (2, 1/2, 3)), [3, 3]) %!assert (size (binornd (1, 1, [4, 1])), [4, 1]) %!assert (size (binornd (1, 1, 4, 1)), [4, 1]) %!assert (size (binornd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (binornd (1, 1, 0, 1)), [0, 1]) %!assert (size (binornd (1, 1, 1, 0)), [1, 0]) %!assert (size (binornd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (binornd (1, 1)), "double") %!assert (class (binornd (1, single (0))), "single") %!assert (class (binornd (1, single ([0, 0]))), "single") %!assert (class (binornd (1, single (1), 2)), "single") %!assert (class (binornd (1, single ([1, 1]), 1, 2)), "single") %!assert (class (binornd (single (1), 1, 2)), "single") %!assert (class (binornd (single ([1, 1]), 1, 1, 2)), "single") ## Test input validation %!error binornd () %!error binornd (1) %!error ... %! binornd (ones (3), ones (2)) %!error ... %! binornd (ones (2), ones (3)) %!error binornd (i, 2) %!error binornd (1, i) %!error ... %! binornd (1, 1/2, -1) %!error ... %! binornd (1, 1/2, 1.2) %!error ... %! binornd (1, 1/2, ones (2)) %!error ... %! binornd (1, 1/2, [2 -1 2]) %!error ... %! binornd (1, 1/2, [2 0 2.5]) %!error ... %! binornd (1, 1/2, 2, -1, 5) %!error ... %! binornd (1, 1/2, 2, 1.5, 5) %!error ... %! binornd (2, 1/2 * ones (2), 3) %!error ... %! binornd (2, 1/2 * ones (2), [3, 2]) %!error ... %! binornd (2, 1/2 * ones (2), 3, 2) %!error ... %! binornd (2 * ones (2), 1/2, 3) %!error ... %! binornd (2 * ones (2), 1/2, [3, 2]) %!error ... %! binornd (2 * ones (2), 1/2, 3, 2) statistics-release-1.7.3/inst/dist_fun/bisacdf.m000066400000000000000000000154221475240274700217130ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} bisacdf (@var{x}, @var{beta}, @var{gamma}) ## @deftypefnx {statistics} {@var{p} =} bisacdf (@var{x}, @var{beta}, @var{gamma}, @qcode{"upper"}) ## ## Birnbaum-Saunders cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Birnbaum-Saunders distribution with scale parameter @var{beta} ## and shape parameter @var{gamma}. The size of @var{p} is the common size of ## @var{x}, @var{beta} and @var{gamma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## @code{@var{p} = bisacdf (@var{x}, @var{beta}, @var{gamma}, "upper")} ## computes the upper tail probability of the Birnbaum-Saunders distribution ## with parameters @var{beta} and @var{gamma}, at the values in @var{x}. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisainv, bisapdf, bisarnd, bisafit, bisalike, bisastat} ## @end deftypefn function p = bisacdf (x, beta, gamma, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("bisacdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("bisacdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, BETA, and GAMMA if (! isscalar (x) || ! isscalar (beta) || ! isscalar (gamma)) [retval, x, beta, gamma] = common_size (x, beta, gamma); if (retval > 0) error (strcat (["bisacdf: X, BETA, and GAMMA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, BETA, and GAMMA being reals if (iscomplex (x) || iscomplex (beta) || iscomplex (gamma)) error ("bisacdf: X, BETA, and GAMMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (beta, "single") || isa (gamma, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force NaNs for out of range parameters. k = isnan (x) | ! (beta > 0) | ! (beta < Inf) ... | ! (gamma > 0) | ! (gamma < Inf); p(k) = NaN; ## Find valid values in parameters and data k = (x > 0) & (x <= Inf) & (beta > 0) & (beta < Inf) ... & (gamma > 0) & (gamma < Inf); xk = x(k); ## Compute Birnbaum-Saunders CDF if (isscalar (beta) && isscalar (gamma)) if (uflag) z = (-sqrt (xk ./ beta) + sqrt (beta ./ xk)) ./ gamma; else z = (sqrt (xk ./ beta) - sqrt (beta ./ xk)) ./ gamma; endif p(k) = 0.5 * erfc (-z ./ sqrt (2)); else if (uflag) z = (-sqrt (xk ./ beta(k)) + sqrt (beta(k) ./ xk)) ./ gamma(k); else z = (sqrt (xk ./ beta(k)) - sqrt (beta(k) ./ xk)) ./ gamma(k); endif p(k) = 0.5 * erfc (-z ./ sqrt (2)); endif endfunction %!demo %! ## Plot various CDFs from the Birnbaum-Saunders distribution %! x = 0.01:0.01:4; %! p1 = bisacdf (x, 1, 0.5); %! p2 = bisacdf (x, 1, 1); %! p3 = bisacdf (x, 1, 2); %! p4 = bisacdf (x, 1, 5); %! p5 = bisacdf (x, 1, 10); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-m") %! grid on %! legend ({"β = 1, γ = 0.5", "β = 1, γ = 1", "β = 1, γ = 2", ... %! "β = 1, γ = 5", "β = 1, γ = 10"}, "location", "southeast") %! title ("Birnbaum-Saunders CDF") %! xlabel ("values in x") %! ylabel ("probability") %!demo %! ## Plot various CDFs from the Birnbaum-Saunders distribution %! x = 0.01:0.01:6; %! p1 = bisacdf (x, 1, 0.3); %! p2 = bisacdf (x, 2, 0.3); %! p3 = bisacdf (x, 1, 0.5); %! p4 = bisacdf (x, 3, 0.5); %! p5 = bisacdf (x, 5, 0.5); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-m") %! grid on %! legend ({"β = 1, γ = 0.3", "β = 2, γ = 0.3", "β = 1, γ = 0.5", ... %! "β = 3, γ = 0.5", "β = 5, γ = 0.5"}, "location", "southeast") %! title ("Birnbaum-Saunders CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1, 0, 1, 2, Inf]; %! y = [0, 0, 1/2, 0.76024993890652337, 1]; %!assert (bisacdf (x, ones (1,5), ones (1,5)), y, eps) %!assert (bisacdf (x, 1, 1), y, eps) %!assert (bisacdf (x, 1, ones (1,5)), y, eps) %!assert (bisacdf (x, ones (1,5), 1), y, eps) %!assert (bisacdf (x, 1, 1), y, eps) %!assert (bisacdf (x, 1, [1, 1, NaN, 1, 1]), [y(1:2), NaN, y(4:5)], eps) %!assert (bisacdf (x, [1, 1, NaN, 1, 1], 1), [y(1:2), NaN, y(4:5)], eps) %!assert (bisacdf ([x, NaN], 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (bisacdf (single ([x, NaN]), 1, 1), single ([y, NaN]), eps ("single")) %!assert (bisacdf ([x, NaN], 1, single (1)), single ([y, NaN]), eps ("single")) %!assert (bisacdf ([x, NaN], single (1), 1), single ([y, NaN]), eps ("single")) ## Test input validation %!error bisacdf () %!error bisacdf (1) %!error bisacdf (1, 2) %!error ... %! bisacdf (1, 2, 3, 4, 5) %!error bisacdf (1, 2, 3, "tail") %!error bisacdf (1, 2, 3, 4) %!error ... %! bisacdf (ones (3), ones (2), ones(2)) %!error ... %! bisacdf (ones (2), ones (3), ones(2)) %!error ... %! bisacdf (ones (2), ones (2), ones(3)) %!error bisacdf (i, 4, 3) %!error bisacdf (1, i, 3) %!error bisacdf (1, 4, i) statistics-release-1.7.3/inst/dist_fun/bisainv.m000066400000000000000000000141021475240274700217450ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} bisainv (@var{p}, @var{beta}, @var{gamma}) ## ## Inverse of the Birnbaum-Saunders cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Birnbaum-Saunders distribution with scale parameter @var{beta} and shape ## parameter @var{gamma}. The size of @var{x} is the common size of @var{p}, ## @var{beta}, and @var{gamma}. A scalar input functions as a constant matrix ## of the same size as the other inputs. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisainv, bisapdf, bisarnd, bisafit, bisalike, bisastat} ## @end deftypefn function x = bisainv (p, beta, gamma) ## Check for valid number of input arguments if (nargin < 3) error ("bisainv: function called with too few input arguments."); endif ## Check for common size of X, BETA, and GAMMA if (! isscalar (p) || ! isscalar (beta) || ! isscalar (gamma)) [retval, p, beta, gamma] = common_size (p, beta, gamma); if (retval > 0) error (strcat (["bisainv: P, BETA, and GAMMA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, BETA, and GAMMA being reals if (iscomplex (p) || iscomplex (beta) || iscomplex (gamma)) error ("bisainv: P, BETA, and GAMMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (beta, "single") || isa (gamma, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif ## Force NaNs for out of range parameters kn = isnan (p) | (p < 0) | (p > 1) | ! (beta > 0) | ! (beta < Inf) ... | ! (gamma > 0) | ! (gamma < Inf); x(kn) = NaN; ## Find valid values in parameters kv = (beta > 0) & (beta < Inf) & (gamma > 0) & (gamma < Inf); ## Handle edge cases k0 = (p == 0) & kv; x(k0) = 0; k1 = (p == 1) & kv; x(k1) = Inf; ## Handle all other valid cases k = (p > 0) & (p < 1) & kv; if (isscalar (beta) && isscalar (gamma)) z = -sqrt (2) .* erfcinv (2 .* p(k)) .* gamma; x(k) = 0.25 .* beta .* (z + sqrt (4 + z .^ 2)) .^ 2; else z = -sqrt (2) .* erfcinv (2 .* p(k)) .* gamma(k); x(k) = 0.25 .* beta(k) .* (z + sqrt (4 + z .^ 2)) .^ 2; endif endfunction %!demo %! ## Plot various iCDFs from the Birnbaum-Saunders distribution %! p = 0.001:0.001:0.999; %! x1 = bisainv (p, 1, 0.5); %! x2 = bisainv (p, 1, 1); %! x3 = bisainv (p, 1, 2); %! x4 = bisainv (p, 1, 5); %! x5 = bisainv (p, 1, 10); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-m") %! grid on %! ylim ([0, 10]) %! legend ({"β = 1, γ = 0.5", "β = 1, γ = 1", "β = 1, γ = 2", ... %! "β = 1, γ = 5", "β = 1, γ = 10"}, "location", "northwest") %! title ("Birnbaum-Saunders iCDF") %! xlabel ("probability") %! ylabel ("values in x") %!demo %! ## Plot various iCDFs from the Birnbaum-Saunders distribution %! p = 0.001:0.001:0.999; %! x1 = bisainv (p, 1, 0.3); %! x2 = bisainv (p, 2, 0.3); %! x3 = bisainv (p, 1, 0.5); %! x4 = bisainv (p, 3, 0.5); %! x5 = bisainv (p, 5, 0.5); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-m") %! grid on %! ylim ([0, 10]) %! legend ({"β = 1, γ = 0.3", "β = 2, γ = 0.3", "β = 1, γ = 0.5", ... %! "β = 3, γ = 0.5", "β = 5, γ = 0.5"}, "location", "northwest") %! title ("Birnbaum-Saunders iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, y, f %! f = @(p,b,c) (b * (c * norminv (p) + sqrt (4 + (c * norminv(p))^2))^2) / 4; %! p = [-1, 0, 1/4, 1/2, 1, 2]; %! y = [NaN, 0, f(1/4, 1, 1), 1, Inf, NaN]; %!assert (bisainv (p, ones (1,6), ones (1,6)), y) %!assert (bisainv (p, 1, ones (1,6)), y) %!assert (bisainv (p, ones (1,6), 1), y) %!assert (bisainv (p, 1, 1), y) %!assert (bisainv (p, 1, [1, 1, 1, NaN, 1, 1]), [y(1:3), NaN, y(5:6)]) %!assert (bisainv (p, [1, 1, 1, NaN, 1, 1], 1), [y(1:3), NaN, y(5:6)]) %!assert (bisainv ([p, NaN], 1, 1), [y, NaN]) ## Test class of input preserved %!assert (bisainv (single ([p, NaN]), 1, 1), single ([y, NaN]), eps ("single")) %!assert (bisainv ([p, NaN], 1, single (1)), single ([y, NaN]), eps ("single")) %!assert (bisainv ([p, NaN], single (1), 1), single ([y, NaN]), eps ("single")) ## Test input validation %!error bisainv () %!error bisainv (1) %!error bisainv (1, 2) %!error bisainv (1, 2, 3, 4) %!error ... %! bisainv (ones (3), ones (2), ones(2)) %!error ... %! bisainv (ones (2), ones (3), ones(2)) %!error ... %! bisainv (ones (2), ones (2), ones(3)) %!error bisainv (i, 4, 3) %!error bisainv (1, i, 3) %!error bisainv (1, 4, i) statistics-release-1.7.3/inst/dist_fun/bisapdf.m000066400000000000000000000140211475240274700217220ustar00rootroot00000000000000## Copyright (C) 2018 John Donoghue ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} bisapdf (@var{x}, @var{beta}, @var{gamma}) ## ## Birnbaum-Saunders probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Birnbaum-Saunders distribution with scale parameter @var{beta} and ## shape parameter @var{gamma}. The size of @var{y} is the common size of ## @var{x}, @var{beta}, and @var{gamma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisacdf, bisapdf, bisarnd, bisafit, bisalike, bisastat} ## @end deftypefn function y = bisapdf (x, beta, gamma) ## Check for valid number of input arguments if (nargin < 3) error ("bisapdf: function called with too few input arguments."); endif ## Check for common size of X, BETA and GAMMA if (! isscalar (x) || ! isscalar (beta) || ! isscalar (gamma)) [retval, x, beta, gamma] = common_size (x, beta, gamma); if (retval > 0) error (strcat (["bisapdf: X, BETA, and GAMMA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, BETA and GAMMA being reals if (iscomplex (x) || iscomplex (beta) || iscomplex (gamma)) error ("bisapdf: X, BETA, and GAMMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (beta, "single") || isa (gamma, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Force NaNs for out of range parameters. k = isnan (x) | ! (beta > 0) | ! (beta < Inf) ... | ! (gamma > 0) | ! (gamma < Inf); y(k) = NaN; ## Find valid values in parameters and data k = (x > 0) & (x < Inf) & (beta > 0) & (beta < Inf) ... & (gamma > 0) & (gamma < Inf); xk = x(k); if (isscalar (beta) && isscalar (gamma)) z = (sqrt (xk ./ beta) - sqrt (beta ./ xk)) ./ gamma; w = (sqrt (xk ./ beta) + sqrt (beta ./ xk)) ./ gamma; y(k) = (exp (-0.5 .* z .^ 2) ./ sqrt (2 .* pi)) .* w ./ (2.*xk); else z = (sqrt (xk ./ beta(k)) - sqrt (beta(k) ./ xk)) ./ gamma(k); w = (sqrt (xk ./ beta(k)) + sqrt (beta(k) ./ xk)) ./ gamma(k); y(k) = (exp (-0.5 .* z .^ 2) ./ sqrt (2 .* pi)) .* w ./ (2 .* xk); endif endfunction %!demo %! ## Plot various PDFs from the Birnbaum-Saunders distribution %! x = 0.01:0.01:4; %! y1 = bisapdf (x, 1, 0.5); %! y2 = bisapdf (x, 1, 1); %! y3 = bisapdf (x, 1, 2); %! y4 = bisapdf (x, 1, 5); %! y5 = bisapdf (x, 1, 10); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-m") %! grid on %! ylim ([0, 1.5]) %! legend ({"β = 1 ,γ = 0.5", "β = 1, γ = 1", "β = 1, γ = 2", ... %! "β = 1, γ = 5", "β = 1, γ = 10"}, "location", "northeast") %! title ("Birnbaum-Saunders PDF") %! xlabel ("values in x") %! ylabel ("density") %!demo %! ## Plot various PDFs from the Birnbaum-Saunders distribution %! x = 0.01:0.01:6; %! y1 = bisapdf (x, 1, 0.3); %! y2 = bisapdf (x, 2, 0.3); %! y3 = bisapdf (x, 1, 0.5); %! y4 = bisapdf (x, 3, 0.5); %! y5 = bisapdf (x, 5, 0.5); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-m") %! grid on %! ylim ([0, 1.5]) %! legend ({"β = 1, γ = 0.3", "β = 2, γ = 0.3", "β = 1, γ = 0.5", ... %! "β = 3, γ = 0.5", "β = 5, γ = 0.5"}, "location", "northeast") %! title ("Birnbaum-Saunders CDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1, 0, 1, 2, Inf]; %! y = [0, 0, 0.3989422804014327, 0.1647717335503959, 0]; %!assert (bisapdf (x, ones (1,5), ones (1,5)), y, eps) %!assert (bisapdf (x, 1, 1), y, eps) %!assert (bisapdf (x, 1, ones (1,5)), y, eps) %!assert (bisapdf (x, ones (1,5), 1), y, eps) %!assert (bisapdf (x, 1, [1, 1, NaN, 1, 1]), [y(1:2), NaN, y(4:5)], eps) %!assert (bisapdf (x, [1, 1, NaN, 1, 1], 1), [y(1:2), NaN, y(4:5)], eps) %!assert (bisapdf ([x, NaN], 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (bisapdf (single ([x, NaN]), 1, 1), single ([y, NaN]), eps ("single")) %!assert (bisapdf ([x, NaN], 1, single (1)), single ([y, NaN]), eps ("single")) %!assert (bisapdf ([x, NaN], single (1), 1), single ([y, NaN]), eps ("single")) ## Test input validation %!error bisapdf () %!error bisapdf (1) %!error bisapdf (1, 2) %!error bisapdf (1, 2, 3, 4) %!error ... %! bisapdf (ones (3), ones (2), ones(2)) %!error ... %! bisapdf (ones (2), ones (3), ones(2)) %!error ... %! bisapdf (ones (2), ones (2), ones(3)) %!error bisapdf (i, 4, 3) %!error bisapdf (1, i, 3) %!error bisapdf (1, 4, i) statistics-release-1.7.3/inst/dist_fun/bisarnd.m000066400000000000000000000156761475240274700217550ustar00rootroot00000000000000## Copyright (C) 2018 John Donoghue ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} bisarnd (@var{beta}, @var{gamma}) ## @deftypefnx {statistics} {@var{r} =} bisarnd (@var{beta}, @var{gamma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} bisarnd (@var{beta}, @var{gamma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} bisarnd (@var{beta}, @var{gamma}, [@var{sz}]) ## ## Random arrays from the Birnbaum-Saunders distribution. ## ## @code{@var{r} = bisarnd (@var{beta}, @var{gamma})} returns an array of ## random numbers chosen from the Birnbaum-Saunders distribution with scale ## parameter @var{beta} and shape parameter @var{gamma}. The size of @var{r} is ## the common size of @var{beta} and @var{gamma}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{bisarnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisacdf, bisainv, bisapdf, bisafit, bisalike, bisastat} ## @end deftypefn function r = bisarnd (beta, gamma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("bisarnd: function called with too few input arguments."); endif ## Check for common size of BETA and GAMMA if (! isscalar (beta) || ! isscalar (gamma)) [retval, beta, gamma] = common_size (beta, gamma); if (retval > 0) error ("bisarnd: BETA and GAMMA must be of common size or scalars."); endif endif ## Check for BETA and GAMMA being reals if (iscomplex (beta) || iscomplex (gamma)) error ("bisarnd: BETA and GAMMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (beta); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["bisarnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("bisarnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (beta) && ! isequal (size (beta), sz)) error ("bisarnd: BETA and GAMMA must be scalars or of size SZ."); endif ## Check for class type if (isa (beta, "single") || isa (gamma, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Birnbaum-Saunders distribution if (isscalar (beta) && isscalar (gamma)) if ((beta > 0) && (beta < Inf) && (gamma > 0) && (gamma < Inf)) r = rand (sz, cls); y = gamma * norminv (r); r = beta * (y + sqrt (4 + y .^ 2)) .^ 2 / 4; else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (beta > 0) & (beta < Inf) & (gamma > 0) & (gamma < Inf); r(k) = rand (sum (k(:)),1); y = gamma(k) .* norminv (r(k)); r(k) = beta(k) .* (y + sqrt (4 + y.^2)).^2 / 4; endif endfunction ## Test output %!assert (size (bisarnd (1, 1)), [1 1]) %!assert (size (bisarnd (1, ones (2,1))), [2, 1]) %!assert (size (bisarnd (1, ones (2,2))), [2, 2]) %!assert (size (bisarnd (ones (2,1), 1)), [2, 1]) %!assert (size (bisarnd (ones (2,2), 1)), [2, 2]) %!assert (size (bisarnd (1, 1, 3)), [3, 3]) %!assert (size (bisarnd (1, 1, [4, 1])), [4, 1]) %!assert (size (bisarnd (1, 1, 4, 1)), [4, 1]) %!assert (size (bisarnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (bisarnd (1, 1, 0, 1)), [0, 1]) %!assert (size (bisarnd (1, 1, 1, 0)), [1, 0]) %!assert (size (bisarnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (bisarnd (1, 1)), "double") %!assert (class (bisarnd (1, single (1))), "single") %!assert (class (bisarnd (1, single ([1, 1]))), "single") %!assert (class (bisarnd (single (1), 1)), "single") %!assert (class (bisarnd (single ([1, 1]), 1)), "single") ## Test input validation %!error bisarnd () %!error bisarnd (1) %!error ... %! bisarnd (ones (3), ones (2)) %!error ... %! bisarnd (ones (2), ones (3)) %!error bisarnd (i, 2, 3) %!error bisarnd (1, i, 3) %!error ... %! bisarnd (1, 2, -1) %!error ... %! bisarnd (1, 2, 1.2) %!error ... %! bisarnd (1, 2, ones (2)) %!error ... %! bisarnd (1, 2, [2 -1 2]) %!error ... %! bisarnd (1, 2, [2 0 2.5]) %!error ... %! bisarnd (1, 2, 2, -1, 5) %!error ... %! bisarnd (1, 2, 2, 1.5, 5) %!error ... %! bisarnd (2, ones (2), 3) %!error ... %! bisarnd (2, ones (2), [3, 2]) %!error ... %! bisarnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/burrcdf.m000066400000000000000000000151211475240274700217430ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} burrcdf (@var{x}, @var{lambda}, @var{c}, @var{k}) ## @deftypefnx {statistics} {@var{p} =} burrcdf (@var{x}, @var{lambda}, @var{c}, @var{k}, @qcode{"upper"}) ## ## Burr type XII cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Burr type XII distribution with scale parameter @var{lambda}, ## first shape parameter @var{c}, and second shape parameter @var{k}. The size ## of @var{p} is the common size of @var{x}, @var{lambda}, @var{c}, and @var{k}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## @code{@var{p} = burrcdf (@var{x}, @var{beta}, @var{gamma}, "upper")} ## computes the upper tail probability of the Birnbaum-Saunders distribution ## with parameters @var{beta} and @var{gamma}, at the values in @var{x}. ## ## Further information about the Burr distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{burrinv, burrpdf, burrrnd, burrfit, burrlike, burrstat} ## @end deftypefn function p = burrcdf (x, lambda, c, k, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("burrcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 4) if (! strcmpi (uflag, "upper")) error ("burrcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, LANBDA, C and K if (! isscalar (x) || ! isscalar (lambda) || ! isscalar (c) || ! isscalar (k)) [retval, x, lambda, c, k] = common_size (x, lambda, c, k); if (retval > 0) error ("burrcdf: X, LAMBDA, C, and K must be of common size or scalars."); endif endif ## Check for X, LANBDA, C and K being reals if (iscomplex (x) || iscomplex (lambda) || iscomplex (c) || iscomplex (k)) error ("burrcdf: X, LAMBDA, C, and K must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (lambda, "single") || isa (c, "single") ... || isa (k, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force NaNs for out of range parameters j = isnan (x) | ! (lambda > 0) | ! (c > 0) | ! (k > 0); p(j) = NaN; ## Find valid values in parameters and data j = (x > 0) & (lambda > 0) & (lambda < Inf) & (c > 0) & (c < Inf) ... & (k > 0) & (k < Inf); ## Compute Burr CDF if (isscalar (lambda) && isscalar(c) && isscalar(k)) if (uflag) p(j) = (1 + (x(j) / lambda) .^ c) .^ (-k); else p(j) = 1 - (1 + (x(j) / lambda) .^ c) .^ (-k); endif else if (uflag) p(j) = (1 + (x(j) ./ lambda(j)) .^ c(j)) .^ (-k(j)); else p(j) = 1 - (1 + (x(j) ./ lambda(j)) .^ c(j)) .^ (-k(j)); endif endif endfunction %!demo %! ## Plot various CDFs from the Burr type XII distribution %! x = 0.001:0.001:5; %! p1 = burrcdf (x, 1, 1, 1); %! p2 = burrcdf (x, 1, 1, 2); %! p3 = burrcdf (x, 1, 1, 3); %! p4 = burrcdf (x, 1, 2, 1); %! p5 = burrcdf (x, 1, 3, 1); %! p6 = burrcdf (x, 1, 0.5, 2); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", ... %! x, p4, "-c", x, p5, "-m", x, p6, "-k") %! grid on %! legend ({"λ = 1, c = 1, k = 1", "λ = 1, c = 1, k = 2", ... %! "λ = 1, c = 1, k = 3", "λ = 1, c = 2, k = 1", ... %! "λ = 1, c = 3, k = 1", "λ = 1, c = 0.5, k = 2"}, ... %! "location", "southeast") %! title ("Burr type XII CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1, 0, 1, 2, Inf]; %! y = [0, 0, 1/2, 2/3, 1]; %!assert (burrcdf (x, ones(1,5), ones (1,5), ones (1,5)), y, eps) %!assert (burrcdf (x, 1, 1, 1), y, eps) %!assert (burrcdf (x, [1, 1, NaN, 1, 1], 1, 1), [y(1:2), NaN, y(4:5)], eps) %!assert (burrcdf (x, 1, [1, 1, NaN, 1, 1], 1), [y(1:2), NaN, y(4:5)], eps) %!assert (burrcdf (x, 1, 1, [1, 1, NaN, 1, 1]), [y(1:2), NaN, y(4:5)], eps) %!assert (burrcdf ([x, NaN], 1, 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (burrcdf (single ([x, NaN]), 1, 1, 1), single ([y, NaN]), eps("single")) %!assert (burrcdf ([x, NaN], single (1), 1, 1), single ([y, NaN]), eps("single")) %!assert (burrcdf ([x, NaN], 1, single (1), 1), single ([y, NaN]), eps("single")) %!assert (burrcdf ([x, NaN], 1, 1, single (1)), single ([y, NaN]), eps("single")) ## Test input validation %!error burrcdf () %!error burrcdf (1) %!error burrcdf (1, 2) %!error burrcdf (1, 2, 3) %!error ... %! burrcdf (1, 2, 3, 4, 5, 6) %!error burrcdf (1, 2, 3, 4, "tail") %!error burrcdf (1, 2, 3, 4, 5) %!error ... %! burrcdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! burrcdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! burrcdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! burrcdf (ones (2), ones (2), ones(2), ones(3)) %!error burrcdf (i, 2, 3, 4) %!error burrcdf (1, i, 3, 4) %!error burrcdf (1, 2, i, 4) %!error burrcdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/burrinv.m000066400000000000000000000136611475240274700220120ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} burrinv (@var{p}, @var{lambda}, @var{c}, @var{k}) ## ## Inverse of the Burr type XII cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Burr type XII distribution with scale parameter @var{lambda}, first shape ## parameter @var{c}, and second shape parameter @var{k}. The size of @var{x} ## is the common size of @var{p}, @var{lambda}, @var{c}, and @var{k}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Burr distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{burrcdf, burrpdf, burrrnd, burrfit, burrlike, burrstat} ## @end deftypefn function x = burrinv (p, lambda, c, k) ## Check for valid number of input arguments if (nargin < 4) error ("burrinv: function called with too few input arguments."); endif ## Check for common size of P, LANBDA, C and K if (! isscalar (p) || ! isscalar (lambda) || ! isscalar (c) || ! isscalar (k)) [retval, p, lambda, c, k] = common_size (p, lambda, c, k); if (retval > 0) error ("burrinv: P, LAMBDA, C, and K must be of common size or scalars."); endif endif ## Check for P, LANBDA, C and K being reals if (iscomplex (p) || iscomplex (lambda) || iscomplex (c) || iscomplex (k)) error ("burrinv: P, LAMBDA, C, and K must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (lambda, "single") || isa (c, "single") ... || isa (k, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif ## Force NaNs for out of range parameters j = isnan (p) | (p < 0) | (p > 1) | ! (lambda > 0) | ! (c > 0) | ! (k > 0); x(j) = NaN; ## Handle edge cases j = (p == 1) & (lambda > 0) & (lambda < Inf) & (c > 0) & (c < Inf) ... & (k > 0) & (k < Inf); x(j) = Inf; ## Handle all other valid cases j = (0 < p) & (p < 1) & (0 < lambda) & (lambda < Inf) & (0 < c) & (c < Inf) ... & (0 < k) & (k < Inf); if (isscalar (lambda) && isscalar(c) && isscalar(k)) x(j) = ((1 - p(j) / lambda).^(-1 / k) - 1).^(1 / c) ; else x(j) = ((1 - p(j) ./ lambda(j)).^(-1 ./ k(j)) - 1).^(1 ./ c(j)) ; endif endfunction %!demo %! ## Plot various iCDFs from the Burr type XII distribution %! p = 0.001:0.001:0.999; %! x1 = burrinv (p, 1, 1, 1); %! x2 = burrinv (p, 1, 1, 2); %! x3 = burrinv (p, 1, 1, 3); %! x4 = burrinv (p, 1, 2, 1); %! x5 = burrinv (p, 1, 3, 1); %! x6 = burrinv (p, 1, 0.5, 2); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", ... %! p, x4, "-c", p, x5, "-m", p, x6, "-k") %! grid on %! ylim ([0, 5]) %! legend ({"λ = 1, c = 1, k = 1", "λ = 1, c = 1, k = 2", ... %! "λ = 1, c = 1, k = 3", "λ = 1, c = 2, k = 1", ... %! "λ = 1, c = 3, k = 1", "λ = 1, c = 0.5, k = 2"}, ... %! "location", "northwest") %! title ("Burr type XII iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, y %! p = [-Inf, -1, 0, 1/2, 1, 2, Inf]; %! y = [NaN, NaN, 0, 1 , Inf, NaN, NaN]; %!assert (burrinv (p, ones (1,7), ones (1,7), ones(1,7)), y, eps) %!assert (burrinv (p, 1, 1, 1), y, eps) %!assert (burrinv (p, [1, 1, 1, NaN, 1, 1, 1], 1, 1), [y(1:3), NaN, y(5:7)], eps) %!assert (burrinv (p, 1, [1, 1, 1, NaN, 1, 1, 1], 1), [y(1:3), NaN, y(5:7)], eps) %!assert (burrinv (p, 1, 1, [1, 1, 1, NaN, 1, 1, 1]), [y(1:3), NaN, y(5:7)], eps) %!assert (burrinv ([p, NaN], 1, 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (burrinv (single ([p, NaN]), 1, 1, 1), single ([y, NaN]), eps("single")) %!assert (burrinv ([p, NaN], single (1), 1, 1), single ([y, NaN]), eps("single")) %!assert (burrinv ([p, NaN], 1, single (1), 1), single ([y, NaN]), eps("single")) %!assert (burrinv ([p, NaN], 1, 1, single (1)), single ([y, NaN]), eps("single")) ## Test input validation %!error burrinv () %!error burrinv (1) %!error burrinv (1, 2) %!error burrinv (1, 2, 3) %!error ... %! burrinv (1, 2, 3, 4, 5) %!error ... %! burrinv (ones (3), ones (2), ones(2), ones(2)) %!error ... %! burrinv (ones (2), ones (3), ones(2), ones(2)) %!error ... %! burrinv (ones (2), ones (2), ones(3), ones(2)) %!error ... %! burrinv (ones (2), ones (2), ones(2), ones(3)) %!error burrinv (i, 2, 3, 4) %!error burrinv (1, i, 3, 4) %!error burrinv (1, 2, i, 4) %!error burrinv (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/burrpdf.m000066400000000000000000000134061475240274700217640ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} burrpdf (@var{x}, @var{lambda}, @var{c}, @var{k}) ## ## Burr type XII probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Burr type XII distribution with with scale parameter @var{lambda}, ## first shape parameter @var{c}, and second shape parameter @var{k}. The size ## of @var{y} is the common size of @var{x}, @var{lambda}, @var{c}, and @var{k}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## Further information about the Burr distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{burrcdf, burrinv, burrrnd, burrfit, burrlike, burrstat} ## @end deftypefn function y = burrpdf (x, lambda, c, k) ## Check for valid number of input arguments if (nargin < 4) error ("burrpdf: function called with too few input arguments."); endif ## Check for common size of X, LANBDA, C and K if (! isscalar (x) || ! isscalar (lambda) || ! isscalar (c) || ! isscalar (k)) [retval, x, lambda, c, k] = common_size (x, lambda, c, k); if (retval > 0) error ("burrpdf: X, LAMBDA, C, and K must be of common size or scalars."); endif endif ## Check for X, LANBDA, C and K being reals if (iscomplex (x) || iscomplex (lambda) || iscomplex (c) || iscomplex (k)) error ("burrpdf: X, LAMBDA, C, and K must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (lambda, "single") ... || isa (c, "single") || isa (k, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Force NaNs for out of range parameters j = isnan (x) | ! (lambda > 0) | ! (c > 0) | ! (k > 0); y(j) = NaN; ## Find valid values in parameters and data j = (x >= 0) & (0 < lambda) & (lambda < Inf) & (0 < c) & (c < Inf) ... & (0 < k) & (k < Inf); ## Compute Burr PDF if (isscalar (lambda) && isscalar (c) && isscalar(k)) y(j) = (c * k / lambda) .* (x(j) / lambda) .^ (c - 1) ./ ... (1 + (x(j) / lambda) .^ c) .^ (k + 1); else y(j) = (c(j) .* k(j) ./ lambda(j) ) .* x(j).^(c(j) - 1) ./ ... (1 + (x(j) ./ lambda(j) ) .^ c(j)) .^ (k(j) + 1); endif endfunction %!demo %! ## Plot various PDFs from the Burr type XII distribution %! x = 0.001:0.001:3; %! y1 = burrpdf (x, 1, 1, 1); %! y2 = burrpdf (x, 1, 1, 2); %! y3 = burrpdf (x, 1, 1, 3); %! y4 = burrpdf (x, 1, 2, 1); %! y5 = burrpdf (x, 1, 3, 1); %! y6 = burrpdf (x, 1, 0.5, 2); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", ... %! x, y4, "-c", x, y5, "-m", x, y6, "-k") %! grid on %! ylim ([0, 2]) %! legend ({"λ = 1, c = 1, k = 1", "λ = 1, c = 1, k = 2", ... %! "λ = 1, c = 1, k = 3", "λ = 1, c = 2, k = 1", ... %! "λ = 1, c = 3, k = 1", "λ = 1, c = 0.5, k = 2"}, ... %! "location", "northeast") %! title ("Burr type XII PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1, 0, 1, 2, Inf]; %! y = [0, 1, 1/4, 1/9, 0]; %!assert (burrpdf (x, ones(1,5), ones (1,5), ones (1,5)), y) %!assert (burrpdf (x, 1, 1, 1), y) %!assert (burrpdf (x, [1, 1, NaN, 1, 1], 1, 1), [y(1:2), NaN, y(4:5)]) %!assert (burrpdf (x, 1, [1, 1, NaN, 1, 1], 1), [y(1:2), NaN, y(4:5)]) %!assert (burrpdf (x, 1, 1, [1, 1, NaN, 1, 1]), [y(1:2), NaN, y(4:5)]) %!assert (burrpdf ([x, NaN], 1, 1, 1), [y, NaN]) ## Test class of input preserved %!assert (burrpdf (single ([x, NaN]), 1, 1, 1), single ([y, NaN])) %!assert (burrpdf ([x, NaN], single (1), 1, 1), single ([y, NaN])) %!assert (burrpdf ([x, NaN], 1, single (1), 1), single ([y, NaN])) %!assert (burrpdf ([x, NaN], 1, 1, single (1)), single ([y, NaN])) ## Test input validation %!error burrpdf () %!error burrpdf (1) %!error burrpdf (1, 2) %!error burrpdf (1, 2, 3) %!error ... %! burrpdf (1, 2, 3, 4, 5) %!error ... %! burrpdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! burrpdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! burrpdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! burrpdf (ones (2), ones (2), ones(2), ones(3)) %!error burrpdf (i, 2, 3, 4) %!error burrpdf (1, i, 3, 4) %!error burrpdf (1, 2, i, 4) %!error burrpdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/burrrnd.m000066400000000000000000000160521475240274700217760ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} burrrnd (@var{lambda}, @var{c}, @var{k}) ## @deftypefnx {statistics} {@var{r} =} burrrnd (@var{lambda}, @var{c}, @var{k}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} burrrnd (@var{lambda}, @var{c}, @var{k}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} burrrnd (@var{lambda}, @var{c}, @var{k}, [@var{sz}]) ## ## Random arrays from the Burr type XII distribution. ## ## @code{@var{r} = burrrnd (@var{lambda}, @var{c}, @var{k})} returns an array of ## random numbers chosen from the Burr type XII distribution with scale ## parameter @var{lambda}, first shape parameter @var{c}, and second shape ## parameter@var{c}, and @var{k}. The size of @var{r} is the common size of ## @var{lambda}, @var{c}, and @var{k}. LAMBDA scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{burrrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Burr distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{burrcdf, burrinv, burrpdf, burrfit, burrlike, burrstat} ## @end deftypefn function r = burrrnd (lambda, c, k, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("burrrnd: function called with too few input arguments."); endif ## Check for common size of LAMBDA, C, and K if (! isscalar (lambda) || ! isscalar (c) || ! isscalar (k)) [retval, lambda, c, k] = common_size (lambda, c, k); if (retval > 0) error ("burrrnd: LAMBDA, C, and K must be of common size or scalars."); endif endif ## Check for LAMBDA, C, and K being reals if (iscomplex (lambda) || iscomplex (c) || iscomplex (k)) error ("burrrnd: LAMBDA, C, and K must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (lambda); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["burrrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("burrrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (lambda) && ! isequal (size (lambda), sz)) error ("burrrnd: LAMBDA, C, and K must be scalars or of size SZ."); endif ## Check for class type if (isa (lambda, "single") || isa (c, "single") || isa (k, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Burr type XII distribution lambda(lambda <= 0) = NaN; c(c <= 0) = NaN; k(k <= 0) = NaN; r = lambda .* (((1 - rand (sz, cls)) .^ (-(1./k))) - 1) .^ (1./c); endfunction ## Test output %!assert (size (burrrnd (1, 1, 1)), [1 1]) %!assert (size (burrrnd (ones (2,1), 1, 1)), [2, 1]) %!assert (size (burrrnd (ones (2,2), 1, 1)), [2, 2]) %!assert (size (burrrnd (1, ones (2,1), 1)), [2, 1]) %!assert (size (burrrnd (1, ones (2,2), 1)), [2, 2]) %!assert (size (burrrnd (1, 1, ones (2,1))), [2, 1]) %!assert (size (burrrnd (1, 1, ones (2,2))), [2, 2]) %!assert (size (burrrnd (1, 1, 1, 3)), [3, 3]) %!assert (size (burrrnd (1, 1, 1, [4 1])), [4, 1]) %!assert (size (burrrnd (1, 1, 1, 4, 1)), [4, 1]) ## Test class of input preserved %!assert (class (burrrnd (1,1,1)), "double") %!assert (class (burrrnd (single (1),1,1)), "single") %!assert (class (burrrnd (single ([1 1]),1,1)), "single") %!assert (class (burrrnd (1,single (1),1)), "single") %!assert (class (burrrnd (1,single ([1 1]),1)), "single") %!assert (class (burrrnd (1,1,single (1))), "single") %!assert (class (burrrnd (1,1,single ([1 1]))), "single") ## Test input validation %!error burrrnd () %!error burrrnd (1) %!error burrrnd (1, 2) %!error ... %! burrrnd (ones (3), ones (2), ones (2)) %!error ... %! burrrnd (ones (2), ones (3), ones (2)) %!error ... %! burrrnd (ones (2), ones (2), ones (3)) %!error burrrnd (i, 2, 3) %!error burrrnd (1, i, 3) %!error burrrnd (1, 2, i) %!error ... %! burrrnd (1, 2, 3, -1) %!error ... %! burrrnd (1, 2, 3, 1.2) %!error ... %! burrrnd (1, 2, 3, ones (2)) %!error ... %! burrrnd (1, 2, 3, [2 -1 2]) %!error ... %! burrrnd (1, 2, 3, [2 0 2.5]) %!error ... %! burrrnd (1, 2, 3, 2, -1, 5) %!error ... %! burrrnd (1, 2, 3, 2, 1.5, 5) %!error ... %! burrrnd (2, ones (2), 2, 3) %!error ... %! burrrnd (2, ones (2), 2, [3, 2]) %!error ... %! burrrnd (2, ones (2), 2, 3, 2) statistics-release-1.7.3/inst/dist_fun/bvncdf.m000066400000000000000000000201051475240274700215540ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} bvncdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} bvncdf (@var{x}, [], @var{sigma}) ## ## Bivariate normal cumulative distribution function (CDF). ## ## @code{@var{p} = bvncdf (@var{x}, @var{mu}, @var{sigma})} will compute the ## bivariate normal cumulative distribution function of @var{x} given a mean ## parameter @var{mu} and a scale parameter @var{sigma}. ## ## @itemize ## @item @var{x} must be an @math{Nx2} matrix with each variable as a column ## vector. ## @item @var{mu} can be either a scalar (common mean) or a two-element row ## vector (each element corresponds to a variable). If empty, a zero mean is ## assumed. ## @item @var{sigma} can be a scalar (common variance) or a @math{2x2} ## covariance matrix, which must be positive definite. ## @end itemize ## ## @seealso{mvncdf} ## @end deftypefn ## Code adapted from Thomas H. Jørgensen's work in BVNcdf.m function retrieved ## from https://www.tjeconomics.com/code/ function p = bvncdf (x, mu, sigma) ## Check input arguments and add defaults if (size (x, 2) != 2) error (strcat (["bvncdf: X must be an Nx2 matrix with each variable"], ... [" as a column vector."])); endif if (isempty (mu)) mu = [0, 0]; elseif (isscalar (mu)) mu = [mu, mu]; elseif (numel (mu) == 2); mu = mu(:)'; else error ("bvncdf: MU must be a scalar or a two-element vector."); endif if (numel (sigma) == 1) sigma = sigma * ones (2,2); sigma(1,1) = 1; sigma(2,2) = 1; elseif (numel (sigma) != 4) error (strcat (["bvncdf: the covariance matrix must be either a"], ... [" scalar or a 2x2 matrix."])); endif ## Test for symmetric positive definite covariance matrix [~, err] = chol (sigma); if (err != 0) error (strcat (["bvncdf: the covariance matrix is not positive"], ... [" definite and/or symmetric."])); endif dh = (x(:,1) - mu(:,1)) / sqrt (sigma(1,1)); dk = (x(:,2) - mu(:,2)) / sqrt (sigma(2,2)); r = sigma(1,2) / sqrt (sigma(1,1) * sigma(2,2)); p = NaN (size (dh)); p(dh == Inf & dk == Inf) = 1; p(dk == Inf) = 0.5 * erfc (- dh(dk == Inf) / sqrt (2)); p(dh == Inf) = 0.5 * erfc (- dh(dk == Inf) / sqrt (2)); p(dh == -Inf | dk == -Inf) = 0; ind = (dh > -Inf & dh < Inf & dk > -Inf & dk < Inf); ## For p(x1 < dh, x2 < dk, r) if (sum (ind) > 0) p(ind) = calculate_bvncdf (-dh(ind), -dk(ind), r); endif endfunction function p = calculate_bvncdf (dh,dk,r) if (abs (r) < 0.3) lg = 3; ## Gauss Legendre points and weights, n = 6 w = [0.1713244923791705, 0.3607615730481384, 0.4679139345726904]; x = [0.9324695142031522, 0.6612093864662647, 0.2386191860831970]; elseif (abs (r) < 0.75) lg = 6; ## Gauss Legendre points and weights, n = 12 w = [.04717533638651177, 0.1069393259953183, 0.1600783285433464, ... 0.2031674267230659, 0.2334925365383547, 0.2491470458134029]; x = [0.9815606342467191, 0.9041172563704750, 0.7699026741943050, ... 0.5873179542866171, 0.3678314989981802, 0.1252334085114692]; else lg = 10; ## Gauss Legendre points and weights, n = 20 w = [.01761400713915212, .04060142980038694, .06267204833410906, ... .08327674157670475, 0.1019301198172404, 0.1181945319615184, ... 0.1316886384491766, 0.1420961093183821, 0.1491729864726037, ... 0.1527533871307259]; x = [0.9931285991850949, 0.9639719272779138, 0.9122344282513259, ... 0.8391169718222188, 0.7463319064601508, 0.6360536807265150, ... 0.5108670019508271, 0.3737060887154196, 0.2277858511416451, ... 0.07652652113349733]; endif dim1 = ones (size (dh, 1), 1); dim2 = ones (1, lg); hk = dh .* dk; bvn = dim1 * 0; phi_dh = 0.5 * erfc (dh / sqrt (2)); phi_dk = 0.5 * erfc (dk / sqrt (2)); if (abs(r) < 0.925) hs = (dh .* dh + dk .* dk) / 2; asr = asin (r); sn1 = sin (asr * (1 - x) / 2); sn2 = sin (asr * (1 + x) / 2); bvn = sum ((dim1 * w) .* exp (((dim1 * sn1) .* (hk * dim2) - ... hs * dim2) ./ (1 - dim1 * (sn1 .^ 2))) + ... (dim1 * w) .* exp (((dim1 * sn2) .* (hk * dim2) - ... hs * dim2) ./ (1 - dim1 * (sn2 .^ 2))), 2) * ... asr / (4 * pi) + phi_dh .* phi_dk; else twopi = 2 * pi; if r < 0 dk = -dk; hk = -hk; endif if abs(r) < 1 as = (1 - r) * (1 + r); a = sqrt (as); bs = (dh - dk) .^ 2; c = (4 - hk) / 8; d = (12 - hk) / 16; asr = - (bs ./ as + hk) / 2; ind = asr > -100; bvn(ind) = a * exp (asr(ind)) .* (1 - (c(ind) .* (bs(ind) - as)) ... .* (1 - d(ind) .* bs(ind) / 5) /3 ... + (c(ind) .* d(ind)) .* as .^ 2 / 5 ); ind = hk > -100; b = sqrt (bs); phi_ba = 0.5 * erfc ((b/a) / sqrt (2)); sp = sqrt (twopi) * phi_ba; bvn(ind) = bvn(ind) - (exp (-hk(ind) / 2) .* sp(ind)) ... .* b(ind) .* (1 - c(ind) .* bs(ind) ... .* (1 - d(ind) .* bs(ind) / 5) /3); a = a/2; for is = -1:2:1 xs = (a + a * is * x) .^ 2; rs = sqrt (1 - xs); asr1 = - ((bs * dim2) ./ (dim1 * xs) + hk * dim2) / 2; ind1 = (asr1 > -100); sp1 = (1 + (c * dim2) .* (dim1 * xs) .* ... (1 + (d * dim2) .* (dim1 * xs))); ep1 = exp (- (hk * dim2) .* (1 - dim1 * rs) ./ ... (2 * (1 + dim1 * rs))) ./ (dim1 * rs); bvn = bvn + sum (a .* (dim1 * w) .* exp (asr1 .* ind1) ... .* (ep1 .* ind1 - sp1 .* ind1), 2); endfor bvn = -bvn/twopi; endif if (r > 0) tmp = max (dh, dk); bvn = bvn + 0.5 * erfc (tmp / sqrt(2)); elseif (r < 0) phi_dh = 0.5 * erfc (dh / sqrt (2)); phi_dk = 0.5 * erfc (dk / sqrt (2)); bvn = - bvn + max (0, phi_dh - phi_dk); endif endif p = max (0, min (1, bvn)); endfunction %!demo %! mu = [1, -1]; %! sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1, X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! x = [X1(:), X2(:)]; %! p = bvncdf (x, mu, sigma); %! Z = reshape (p, 25, 25); %! surf (X1, X2, Z); %! title ("Bivariate Normal Distribution"); %! ylabel "X1" %! xlabel "X2" ## Test output %!test %! mu = [1, -1]; %! sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1,X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! x = [X1(:), X2(:)]; %! p = bvncdf (x, mu, sigma); %! p_out = [0.00011878988774500, 0.00034404112322371, ... %! 0.00087682502191813, 0.00195221905058185, ... %! 0.00378235566873474, 0.00638175749734415, ... %! 0.00943764224329656, 0.01239164888125426, ... %! 0.01472750274376648, 0.01623228313374828]'; %! assert (p([1:10]), p_out, 1e-16); %!test %! mu = [1, -1]; %! sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1,X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! x = [X1(:), X2(:)]; %! p = bvncdf (x, mu, sigma); %! p_out = [0.8180695783608276, 0.8854485749482751, ... %! 0.9308108777385832, 0.9579855743025508, ... %! 0.9722897881414742, 0.9788150170059926, ... %! 0.9813597788804785, 0.9821977956568989, ... %! 0.9824283794464095, 0.9824809345614861]'; %! assert (p([616:625]), p_out, 3e-16); %!error bvncdf (randn (25,3), [], [1, 1; 1, 1]); %!error bvncdf (randn (25,2), [], [1, 1; 1, 1]); %!error bvncdf (randn (25,2), [], ones (3, 2)); statistics-release-1.7.3/inst/dist_fun/bvtcdf.m000066400000000000000000000143621475240274700215720ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} bvtcdf (@var{x}, @var{rho}, @var{df}) ## @deftypefnx {statistics} {@var{p} =} bvtcdf (@var{x}, @var{rho}, @var{df}, @var{Tol}) ## ## Bivariate Student's t cumulative distribution function (CDF). ## ## @code{@var{p} = bvtcdf (@var{x}, @var{rho}, @var{df})} will compute the ## bivariate student's t cumulative distribution function of @var{x}, which must ## be an @math{Nx2} matrix, given a correlation coefficient @var{rho}, which ## must be a scalar, and @var{df} degrees of freedom, which can be a scalar or a ## vector of positive numbers commensurate with @var{x}. ## ## @var{Tol} is the tolerance for numerical integration and by default ## @code{@var{Tol} = 1e-8}. ## ## @seealso{mvtcdf} ## @end deftypefn function p = bvtcdf (x, rho, df, TolFun) narginchk (3,4); if (nargin < 4) TolFun = 1e-8; endif if (isa (x, "single") || isa (rho, "single") || isa (df, "single")) is_type = "single"; else is_type = "double"; endif if (abs (rho) < 1) largeNu = 1e4; general = ! (fix (df) == df & df < largeNu); if (isscalar (df)) if general p = generalDF (x, rho, repmat (df, size (x, 1), 1), TolFun); else p = integerDF (x, rho, df); endif else p = zeros (size (x, 1), 1, is_type); ## For large of non-integer df if (any (general)) p(general) = generalDF (x(general,:), rho, df(general), TolFun); endif ## For small integer df for i = find (! general(:)') p(i) = integerDF (x(i,:), rho, df(i)); endfor endif elseif (rho == 1) p = tcdf (min (x, [], 2), df); p(any (isnan( x), 2)) = NaN; else p = tcdf (x(:,1), df) - tcdf (-x(:,2), df); endif endfunction ## CDF for the bivariate t with integer degrees of freedom function p = integerDF (x, rho, df) x1 = x(:,1); x2 = x(:,2); tau = 1 - rho .^ 2; x1rx2 = x1 - rho * x2; x2rx1 = x2 - rho * x1; sx1r2 = sign (x1rx2); sx2r1 = sign (x2rx1); dfx1s = df + x1 .^ 2; dfx2s = df + x2 .^ 2; tdfx1x2 = tau * dfx2s ./ x1rx2 .^ 2; tdfx2x1 = tau * dfx1s ./ x2rx1 .^ 2; x_tdf12 = 1 ./ (1 + tdfx1x2); x_tdf21 = 1 ./ (1 + tdfx2x1); y_tdf12 = 1 ./ (1 + 1 ./ tdfx1x2); y_tdf21 = 1 ./ (1 + 1 ./ tdfx2x1); sqrtDF = sqrt (df); halfDF = df/2; if (fix (halfDF) == halfDF) # for even DF p1 = atan2 (sqrt (tau), -rho) ./ (2 * pi); c1 = x1 ./ (4 * sqrt (dfx1s)); c2 = x2 ./ (4 * sqrt (dfx2s)); beta12 = 2 *atan2 (sqrt (x_tdf12), sqrt (y_tdf12)) / pi; beta21 = 2 *atan2 (sqrt (x_tdf21), sqrt (y_tdf21)) / pi; p2 = (1 + sx1r2 .* beta12) .* c2 + (1 + sx2r1 .* beta21) .* c1; betaT12 = 2 * sqrt (x_tdf12 .* y_tdf12) / pi; betaT21 = 2 * sqrt (x_tdf21 .* y_tdf21) / pi; for j = 2:halfDF fact = df * (j - 1.5) / (j - 1); c2 = c2 .* fact ./ dfx2s; c1 = c1 .* fact ./ dfx1s; beta12 = beta12 + betaT12; beta21 = beta21 + betaT21; p2 = p2 + (1 + sx1r2 .* beta12) .* c2 + (1 + sx2r1 .* beta21) .* c1; fact = 2 * (j - 1) / (2 * (j - 1) + 1); betaT12 = fact * betaT12 .* y_tdf12; betaT21 = fact * betaT21 .* y_tdf21; endfor else # for odd DF x1x2p = x1.*x2; x1x2s = x1 + x2; t1 = sqrt (x1 .^ 2 - 2 * rho * x1x2p + x2 .^ 2 + tau * df); t2 = x1x2p + rho * df; t3 = x1x2p - df; p1 = atan2 (sqrtDF .* (-x1x2s .* t2 - t3 .* t1), ... t3 .* t2 - df .* x1x2s .* t1) ./ (2 * pi); p1 = p1 + (p1 < 0); p2 = 0; if (df > 1) c1 = sqrtDF .* x1 ./ (2 * pi .* dfx1s); c2 = sqrtDF .* x2 ./ (2 * pi .* dfx2s); betaT12 = sqrt (x_tdf12); betaT21 = sqrt (x_tdf21); beta12 = betaT12; beta21 = betaT21; p2 = (1 + sx1r2 .* beta12) .* c2 + (1 + sx2r1 .* beta21) .* c1; for j = 2:(halfDF - 0.5) fact = df * (j - 1) / (j - 0.5); c2 = fact * c2 ./ dfx2s; c1 = fact * c1 ./ dfx1s; fact = 1 - 0.5 / (j - 1); betaT12 = fact * betaT12 .* y_tdf12; betaT21 = fact * betaT21 .* y_tdf21; beta12 = beta12 + betaT12; beta21 = beta21 + betaT21; p2 = p2 + (1 + sx1r2 .* beta12) .* c2 + (1 + sx2r1 .* beta21) .* c1; endfor endif endif p = p1 + p2; ## Fix limit cases large = 1e10; p(x1 < -large | x2 < -large) = 0; p(x1 > large) = tcdf (x2(x1 > large), df); p(x2 > large) = tcdf (x1(x2 > large), df); endfunction ## CDF for the bivariate t with arbitrary degrees of freedom. function p = generalDF (x, rho, df, TolFun) n = size (x, 1); if (rho >= 0) p1 = tcdf (min (x, [], 2), df); p1(any (isnan (x), 2)) = NaN; else p1 = tcdf (x(:,1), df) - tcdf (-x(:,2), df); p1(p1 < 0) = 0; endif lo = asin (rho); hi = (sign (rho) + (rho == 0)) .* pi ./ 2; p2 = zeros (size (p1), class (rho)); for i = 1:n b1 = x(i,1); b2 = x(i,2); v = df(i); if (isfinite (b1) && isfinite (b2)) p2(i) = quadgk (@bvtIntegrand, lo, hi, "AbsTol", TolFun, "RelTol", 0); endif endfor p = p1 - p2 ./ (2 .* pi); function integrand = bvtIntegrand (theta) st = sin (theta); ct2 = cos (theta).^2; integrand = (1 ./ (1 + ((b1 * st - b2) .^ 2 ./ ct2 + b1 .^ 2) / v)) ... .^ (v / 2); endfunction endfunction ## Test output %!test %! x = [1, 2]; %! rho = [1, 0.5; 0.5, 1]; %! df = 4; %! assert (bvtcdf(x, rho(2), df), mvtcdf(x, rho, df), 1e-14); %!test %! x = [3, 2;2, 4;1, 5]; %! rho = [1, 0.5; 0.5, 1]; %! df = 4; %! assert (bvtcdf(x, rho(2), df), mvtcdf(x, rho, df), 1e-14); statistics-release-1.7.3/inst/dist_fun/cauchycdf.m000066400000000000000000000133031475240274700222450ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} cauchycdf (@var{x}, @var{x0}, @var{gamma}) ## @deftypefnx {statistics} {@var{p} =} cauchycdf (@var{x}, @var{x0}, @var{gamma}, @qcode{"upper"}) ## ## Cauchy cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Cauchy distribution with location parameter @var{x0} and scale ## parameter @var{gamma}. The size of @var{p} is the common size of @var{x}, ## @var{x0}, and @var{gamma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## @code{@var{p} = cauchycdf (@var{x}, @var{x0}, @var{gamma}, "upper")} computes ## the upper tail probability of the Cauchy distribution with parameters ## @var{x0} and @var{gamma}, at the values in @var{x}. ## ## Further information about the Cauchy distribution can be found at ## @url{https://en.wikipedia.org/wiki/Cauchy_distribution} ## ## @seealso{cauchyinv, cauchypdf, cauchyrnd} ## @end deftypefn function p = cauchycdf (x, x0, gamma, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("cauchycdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("cauchycdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, X0, and GAMMA if (! isscalar (x) || ! isscalar (x0) || ! isscalar (gamma)) [retval, x, x0, gamma] = common_size (x, x0, gamma); if (retval > 0) error (strcat (["cauchycdf: X, X0, and GAMMA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, X0, and GAMMA being reals if (iscomplex (x) || iscomplex (x0) || iscomplex (gamma)) error ("cauchycdf: X, X0, and GAMMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (x0, "single") || isa (gamma, "single")); p = NaN (size (x), "single"); else p = NaN (size (x)); endif ## Find valid values in parameters and data k = ! isinf (x0) & (gamma > 0) & (gamma < Inf); ## Compute Cauchy CDF if (isscalar (x0) && isscalar (gamma)) if (uflag) p = 0.5 + atan ((-x(k) + x0) / gamma) / pi; else p = 0.5 + atan ((x(k) - x0) / gamma) / pi; endif else if (uflag) p(k) = 0.5 + atan ((-x(k) + x0(k)) ./ gamma(k)) / pi; else p(k) = 0.5 + atan ((x(k) - x0(k)) ./ gamma(k)) / pi; endif endif endfunction %!demo %! ## Plot various CDFs from the Cauchy distribution %! x = -5:0.01:5; %! p1 = cauchycdf (x, 0, 0.5); %! p2 = cauchycdf (x, 0, 1); %! p3 = cauchycdf (x, 0, 2); %! p4 = cauchycdf (x, -2, 1); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! xlim ([-5, 5]) %! legend ({"x0 = 0, γ = 0.5", "x0 = 0, γ = 1", ... %! "x0 = 0, γ = 2", "x0 = -2, γ = 1"}, "location", "southeast") %! title ("Cauchy CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2]; %! y = 1/pi * atan ((x-1) / 2) + 1/2; %!assert (cauchycdf (x, ones (1,5), 2*ones (1,5)), y) %!assert (cauchycdf (x, 1, 2*ones (1,5)), y) %!assert (cauchycdf (x, ones (1,5), 2), y) %!assert (cauchycdf (x, [-Inf 1 NaN 1 Inf], 2), [NaN y(2) NaN y(4) NaN]) %!assert (cauchycdf (x, 1, 2*[0 1 NaN 1 Inf]), [NaN y(2) NaN y(4) NaN]) %!assert (cauchycdf ([x(1:2) NaN x(4:5)], 1, 2), [y(1:2) NaN y(4:5)]) %!assert (cauchycdf ([x, NaN], 1, 2), [y, NaN]) ## Test class of input preserved %!assert (cauchycdf (single ([x, NaN]), 1, 2), single ([y, NaN]), eps ("single")) %!assert (cauchycdf ([x, NaN], single (1), 2), single ([y, NaN]), eps ("single")) %!assert (cauchycdf ([x, NaN], 1, single (2)), single ([y, NaN]), eps ("single")) ## Test input validation %!error cauchycdf () %!error cauchycdf (1) %!error ... %! cauchycdf (1, 2) %!error ... %! cauchycdf (1, 2, 3, 4, 5) %!error cauchycdf (1, 2, 3, "tail") %!error cauchycdf (1, 2, 3, 4) %!error ... %! cauchycdf (ones (3), ones (2), ones (2)) %!error ... %! cauchycdf (ones (2), ones (3), ones (2)) %!error ... %! cauchycdf (ones (2), ones (2), ones (3)) %!error cauchycdf (i, 2, 2) %!error cauchycdf (2, i, 2) %!error cauchycdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/cauchyinv.m000066400000000000000000000121141475240274700223040ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} cauchyinv (@var{p}, @var{x0}, @var{gamma}) ## ## Inverse of the Cauchy cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Cauchy distribution with location parameter @var{x0} and scale parameter ## @var{gamma}. The size of @var{x} is the common size of @var{p}, @var{x0}, ## and @var{gamma}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## Further information about the Cauchy distribution can be found at ## @url{https://en.wikipedia.org/wiki/Cauchy_distribution} ## ## @seealso{cauchycdf, cauchypdf, cauchyrnd} ## @end deftypefn function x = cauchyinv (p, x0, gamma) ## Check for valid number of input arguments if (nargin < 3) error ("cauchyinv: function called with too few input arguments."); endif ## Check for common size of P, X0, and GAMMA if (! isscalar (p) || ! isscalar (x0) || ! isscalar (gamma)) [retval, p, x0, gamma] = common_size (p, x0, gamma); if (retval > 0) error (strcat (["cauchyinv: P, X0, and GAMMA must be of"], ... [" common size or scalars."])); endif endif ## Check for P, X0, and GAMMA being reals if (iscomplex (p) || iscomplex (x0) || iscomplex (gamma)) error ("cauchyinv: P, X0, and GAMMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (x0, "single") || isa (gamma, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Find valid values in parameters ok = ! isinf (x0) & (gamma > 0) & (gamma < Inf); ## Handle edge cases k0 = (p == 0) & ok; x(k0) = -Inf; k1 = (p == 1) & ok; x(k1) = Inf; ## Handle all other valid cases k = (p > 0) & (p < 1) & ok; if (isscalar (x0) && isscalar (gamma)) x(k) = x0 - gamma * cot (pi * p(k)); else x(k) = x0(k) - gamma(k) .* cot (pi * p(k)); endif endfunction %!demo %! ## Plot various iCDFs from the Cauchy distribution %! p = 0.001:0.001:0.999; %! x1 = cauchyinv (p, 0, 0.5); %! x2 = cauchyinv (p, 0, 1); %! x3 = cauchyinv (p, 0, 2); %! x4 = cauchyinv (p, -2, 1); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([-5, 5]) %! legend ({"x0 = 0, γ = 0.5", "x0 = 0, γ = 1", ... %! "x0 = 0, γ = 2", "x0 = -2, γ = 1"}, "location", "northwest") %! title ("Cauchy iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (cauchyinv (p, ones (1,5), 2 * ones (1,5)), [NaN -Inf 1 Inf NaN], eps) %!assert (cauchyinv (p, 1, 2 * ones (1,5)), [NaN -Inf 1 Inf NaN], eps) %!assert (cauchyinv (p, ones (1,5), 2), [NaN -Inf 1 Inf NaN], eps) %!assert (cauchyinv (p, [1 -Inf NaN Inf 1], 2), [NaN NaN NaN NaN NaN]) %!assert (cauchyinv (p, 1, 2 * [1 0 NaN Inf 1]), [NaN NaN NaN NaN NaN]) %!assert (cauchyinv ([p(1:2) NaN p(4:5)], 1, 2), [NaN -Inf NaN Inf NaN]) %!assert (cauchyinv ([p, NaN], 1, 2), [NaN -Inf 1 Inf NaN NaN], eps) ## Test class of input preserved %!assert (cauchyinv (single ([p, NaN]), 1, 2), ... %! single ([NaN -Inf 1 Inf NaN NaN]), eps ("single")) %!assert (cauchyinv ([p, NaN], single (1), 2), ... %! single ([NaN -Inf 1 Inf NaN NaN]), eps ("single")) %!assert (cauchyinv ([p, NaN], 1, single (2)), ... %! single ([NaN -Inf 1 Inf NaN NaN]), eps ("single")) ## Test input validation %!error cauchyinv () %!error cauchyinv (1) %!error ... %! cauchyinv (1, 2) %!error cauchyinv (1, 2, 3, 4) %!error ... %! cauchyinv (ones (3), ones (2), ones(2)) %!error ... %! cauchyinv (ones (2), ones (3), ones(2)) %!error ... %! cauchyinv (ones (2), ones (2), ones(3)) %!error cauchyinv (i, 4, 3) %!error cauchyinv (1, i, 3) %!error cauchyinv (1, 4, i) statistics-release-1.7.3/inst/dist_fun/cauchypdf.m000066400000000000000000000116271475240274700222710ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} cauchypdf (@var{x}, @var{x0}, @var{gamma}) ## ## Cauchy probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Cauchy distribution with location parameter @var{x0} and scale ## parameter @var{gamma}. The size of @var{y} is the common size of @var{x}, ## @var{x0}, and @var{gamma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Further information about the Cauchy distribution can be found at ## @url{https://en.wikipedia.org/wiki/Cauchy_distribution} ## ## @seealso{cauchycdf, cauchypdf, cauchyrnd} ## @end deftypefn function y = cauchypdf (x, x0, gamma) ## Check for valid number of input arguments if (nargin < 3) error ("cauchypdf: function called with too few input arguments."); endif ## Check for common size of X, X0, and GAMMA if (! isscalar (x) || ! isscalar (x0) || ! isscalar (gamma)) [retval, x, x0, gamma] = common_size (x, x0, gamma); if (retval > 0) error (strcat (["cauchypdf: X, X0, and GAMMA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, X0, and GAMMA being reals if (iscomplex (x) || iscomplex (x0) || iscomplex (gamma)) error ("cauchypdf: X, X0, and GAMMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (x0, "single") || isa (gamma, "single")) y = NaN (size (x), "single"); else y = NaN (size (x)); endif ## Find valid values in parameters k = ! isinf (x0) & (gamma > 0) & (gamma < Inf); if (isscalar (x0) && isscalar (gamma)) y(k) = ((1 ./ (1 + ((x(k) - x0) / gamma) .^ 2)) / pi / gamma); else y(k) = ((1 ./ (1 + ((x(k) - x0(k)) ./ gamma(k)) .^ 2)) / pi ./ gamma(k)); endif endfunction %!demo %! ## Plot various PDFs from the Cauchy distribution %! x = -5:0.01:5; %! y1 = cauchypdf (x, 0, 0.5); %! y2 = cauchypdf (x, 0, 1); %! y3 = cauchypdf (x, 0, 2); %! y4 = cauchypdf (x, -2, 1); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! xlim ([-5, 5]) %! ylim ([0, 0.7]) %! legend ({"x0 = 0, γ = 0.5", "x0 = 0, γ = 1", ... %! "x0 = 0, γ = 2", "x0 = -2, γ = 1"}, "location", "northeast") %! title ("Cauchy PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2]; %! y = 1/pi * ( 2 ./ ((x-1).^2 + 2^2) ); %!assert (cauchypdf (x, ones (1,5), 2*ones (1,5)), y) %!assert (cauchypdf (x, 1, 2*ones (1,5)), y) %!assert (cauchypdf (x, ones (1,5), 2), y) %!assert (cauchypdf (x, [-Inf 1 NaN 1 Inf], 2), [NaN y(2) NaN y(4) NaN]) %!assert (cauchypdf (x, 1, 2*[0 1 NaN 1 Inf]), [NaN y(2) NaN y(4) NaN]) %!assert (cauchypdf ([x, NaN], 1, 2), [y, NaN]) ## Test class of input preserved %!assert (cauchypdf (single ([x, NaN]), 1, 2), single ([y, NaN]), eps ("single")) %!assert (cauchypdf ([x, NaN], single (1), 2), single ([y, NaN]), eps ("single")) %!assert (cauchypdf ([x, NaN], 1, single (2)), single ([y, NaN]), eps ("single")) ## Cauchy (0,1) == Student's T distribution with 1 DOF %!test %! x = rand (10, 1); %! assert (cauchypdf (x, 0, 1), tpdf (x, 1), eps); ## Test input validation %!error cauchypdf () %!error cauchypdf (1) %!error ... %! cauchypdf (1, 2) %!error cauchypdf (1, 2, 3, 4) %!error ... %! cauchypdf (ones (3), ones (2), ones(2)) %!error ... %! cauchypdf (ones (2), ones (3), ones(2)) %!error ... %! cauchypdf (ones (2), ones (2), ones(3)) %!error cauchypdf (i, 4, 3) %!error cauchypdf (1, i, 3) %!error cauchypdf (1, 4, i) statistics-release-1.7.3/inst/dist_fun/cauchyrnd.m000066400000000000000000000154641475240274700223060ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} cauchyrnd (@var{x0}, @var{gamma}) ## @deftypefnx {statistics} {@var{r} =} cauchyrnd (@var{x0}, @var{gamma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} cauchyrnd (@var{x0}, @var{gamma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} cauchyrnd (@var{x0}, @var{gamma}, [@var{sz}]) ## ## Random arrays from the Cauchy distribution. ## ## @code{@var{r} = cauchyrnd (@var{x0}, @var{gamma})} returns an array of ## random numbers chosen from the Cauchy distribution with location parameter ## @var{x0} and scale parameter @var{gamma}. The size of @var{r} is the common ## size of @var{x0} and @var{gamma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{cauchyrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Cauchy distribution can be found at ## @url{https://en.wikipedia.org/wiki/Cauchy_distribution} ## ## @seealso{cauchycdf, cauchyinv, cauchypdf} ## @end deftypefn function r = cauchyrnd (x0, gamma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("cauchyrnd: function called with too few input arguments."); endif ## Check for common size of X0 and GAMMA if (! isscalar (x0) || ! isscalar (gamma)) [retval, x0, gamma] = common_size (x0, gamma); if (retval > 0) error (strcat (["cauchyrnd: X0 and GAMMA must be of common"], ... [" size or scalars."])); endif endif ## Check for X0 and GAMMA being reals if (iscomplex (x0) || iscomplex (gamma)) error ("cauchyrnd: X0 and GAMMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (x0); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["cauchyrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("cauchyrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (x0) && ! isequal (size (x0), sz)) error ("cauchyrnd: X0 and GAMMA must be scalars or of size SZ."); endif ## Check for class type if (isa (x0, "single") || isa (gamma, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Cauchy distribution if (isscalar (x0) && isscalar (gamma)) if (! isinf (x0) && (gamma > 0) && (gamma < Inf)) r = x0 - cot (pi * rand (sz, cls)) * gamma; else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = ! isinf (x0) & (gamma > 0) & (gamma < Inf); r(k) = x0(k)(:) - cot (pi * rand (sum (k(:)), 1, cls)) .* gamma(k)(:); endif endfunction ## Test output %!assert (size (cauchyrnd (1, 1)), [1 1]) %!assert (size (cauchyrnd (1, ones (2,1))), [2, 1]) %!assert (size (cauchyrnd (1, ones (2,2))), [2, 2]) %!assert (size (cauchyrnd (ones (2,1), 1)), [2, 1]) %!assert (size (cauchyrnd (ones (2,2), 1)), [2, 2]) %!assert (size (cauchyrnd (1, 1, 3)), [3, 3]) %!assert (size (cauchyrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (cauchyrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (cauchyrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (cauchyrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (cauchyrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (cauchyrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (cauchyrnd (1, 1)), "double") %!assert (class (cauchyrnd (1, single (1))), "single") %!assert (class (cauchyrnd (1, single ([1, 1]))), "single") %!assert (class (cauchyrnd (single (1), 1)), "single") %!assert (class (cauchyrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error cauchyrnd () %!error cauchyrnd (1) %!error ... %! cauchyrnd (ones (3), ones (2)) %!error ... %! cauchyrnd (ones (2), ones (3)) %!error cauchyrnd (i, 2, 3) %!error cauchyrnd (1, i, 3) %!error ... %! cauchyrnd (1, 2, -1) %!error ... %! cauchyrnd (1, 2, 1.2) %!error ... %! cauchyrnd (1, 2, ones (2)) %!error ... %! cauchyrnd (1, 2, [2 -1 2]) %!error ... %! cauchyrnd (1, 2, [2 0 2.5]) %!error ... %! cauchyrnd (1, 2, 2, -1, 5) %!error ... %! cauchyrnd (1, 2, 2, 1.5, 5) %!error ... %! cauchyrnd (2, ones (2), 3) %!error ... %! cauchyrnd (2, ones (2), [3, 2]) %!error ... %! cauchyrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/chi2cdf.m000066400000000000000000000112371475240274700216220ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} chi2cdf (@var{x}, @var{df}) ## @deftypefnx {statistics} {@var{p} =} chi2cdf (@var{x}, @var{df}, @qcode{"upper"}) ## ## Chi-squared cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the chi-squared distribution with @var{df} degrees of freedom. The ## chi-squared density function with @var{df} degrees of freedom is the same as ## a gamma density function with parameters @qcode{@var{df}/2} and @qcode{2}. ## ## The size of @var{p} is the common size of @var{x} and @var{df}. A scalar ## input functions as a constant matrix of the same size as the other input. ## ## @code{@var{p} = chi2cdf (@var{x}, @var{df}, "upper")} computes the upper tail ## probability of the chi-squared distribution with @var{df} degrees of freedom, ## at the values in @var{x}. ## ## Further information about the chi-squared distribution can be found at ## @url{https://en.wikipedia.org/wiki/Chi-squared_distribution} ## ## @seealso{chi2inv, chi2pdf, chi2rnd, chi2stat} ## @end deftypefn function p = chi2cdf (x, df, uflag) ## Check for valid number of input arguments if (nargin < 2) error ("chi2cdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 2 && ! strcmpi (uflag, "upper")) error ("chi2cdf: invalid argument for upper tail."); else uflag = []; endif ## Check for common size of X and DF if (! isscalar (x) || ! isscalar (df)) [err, x, df] = common_size (x, df); if (err > 0) error ("chi2cdf: X and DF must be of common size or scalars."); endif endif ## Check for X and DF being reals if (iscomplex (x) || iscomplex (df)) error ("chi2cdf: X and DF must not be complex."); endif ## Compute chi-squared CDF p = gamcdf (x, df/2, 2, uflag); endfunction %!demo %! ## Plot various CDFs from the chi-squared distribution %! x = 0:0.01:8; %! p1 = chi2cdf (x, 1); %! p2 = chi2cdf (x, 2); %! p3 = chi2cdf (x, 3); %! p4 = chi2cdf (x, 4); %! p5 = chi2cdf (x, 6); %! p6 = chi2cdf (x, 9); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", ... %! x, p4, "-c", x, p5, "-m", x, p6, "-y") %! grid on %! xlim ([0, 8]) %! legend ({"df = 1", "df = 2", "df = 3", ... %! "df = 4", "df = 6", "df = 9"}, "location", "southeast") %! title ("Chi-squared CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, p, u %! x = [-1, 0, 0.5, 1, 2]; %! p = [0, (1 - exp (-x(2:end) / 2))]; %! u = [1, 0, NaN, 0.3934693402873666, 0.6321205588285577]; %!assert (chi2cdf (x, 2 * ones (1,5)), p, eps) %!assert (chi2cdf (x, 2), p, eps) %!assert (chi2cdf (x, 2 * [1, 0, NaN, 1, 1]), [p(1), 1, NaN, p(4:5)], eps) %!assert (chi2cdf (x, 2 * [1, 0, NaN, 1, 1], "upper"), ... %! [p(1), 1, NaN, u(4:5)], eps) %!assert (chi2cdf ([x(1:2), NaN, x(4:5)], 2), [p(1:2), NaN, p(4:5)], eps) ## Test class of input preserved %!assert (chi2cdf ([x, NaN], 2), [p, NaN], eps) %!assert (chi2cdf (single ([x, NaN]), 2), single ([p, NaN]), eps ("single")) %!assert (chi2cdf ([x, NaN], single (2)), single ([p, NaN]), eps ("single")) ## Test input validation %!error chi2cdf () %!error chi2cdf (1) %!error chi2cdf (1, 2, 3, 4) %!error chi2cdf (1, 2, 3) %!error chi2cdf (1, 2, "uper") %!error ... %! chi2cdf (ones (3), ones (2)) %!error ... %! chi2cdf (ones (2), ones (3)) %!error chi2cdf (i, 2) %!error chi2cdf (2, i) statistics-release-1.7.3/inst/dist_fun/chi2inv.m000066400000000000000000000074461475240274700216710ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} chi2inv (@var{p}, @var{df}) ## ## Inverse of the chi-squared cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the chi-squared distribution with @var{df} degrees of freedom. The size of ## @var{x} is the common size of @var{p} and @var{df}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## Further information about the chi-squared distribution can be found at ## @url{https://en.wikipedia.org/wiki/Chi-squared_distribution} ## ## @seealso{chi2cdf, chi2pdf, chi2rnd, chi2stat} ## @end deftypefn function x = chi2inv (p, df) ## Check for valid number of input arguments if (nargin < 2) error ("chi2inv: function called with too few input arguments."); endif ## Check for common size of P and DF if (! isscalar (p) || ! isscalar (df)) [retval, p, df] = common_size (p, df); if (retval > 0) error ("chi2inv: P and DF must be of common size or scalars."); endif endif ## Check for P and DF being reals if (iscomplex (p) || iscomplex (df)) error ("chi2inv: P and DF must not be complex."); endif ## Compute chi-squared iCDF x = gaminv (p, df/2, 2); endfunction %!demo %! ## Plot various iCDFs from the chi-squared distribution %! p = 0.001:0.001:0.999; %! x1 = chi2inv (p, 1); %! x2 = chi2inv (p, 2); %! x3 = chi2inv (p, 3); %! x4 = chi2inv (p, 4); %! x5 = chi2inv (p, 6); %! x6 = chi2inv (p, 9); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", ... %! p, x4, "-c", p, x5, "-m", p, x6, "-y") %! grid on %! ylim ([0, 8]) %! legend ({"df = 1", "df = 2", "df = 3", ... %! "df = 4", "df = 6", "df = 9"}, "location", "northwest") %! title ("Chi-squared iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.3934693402873666 1 2]; %!assert (chi2inv (p, 2*ones (1,5)), [NaN 0 1 Inf NaN], 5*eps) %!assert (chi2inv (p, 2), [NaN 0 1 Inf NaN], 5*eps) %!assert (chi2inv (p, 2*[0 1 NaN 1 1]), [NaN 0 NaN Inf NaN], 5*eps) %!assert (chi2inv ([p(1:2) NaN p(4:5)], 2), [NaN 0 NaN Inf NaN], 5*eps) ## Test class of input preserved %!assert (chi2inv ([p, NaN], 2), [NaN 0 1 Inf NaN NaN], 5*eps) %!assert (chi2inv (single ([p, NaN]), 2), single ([NaN 0 1 Inf NaN NaN]), 5*eps ("single")) %!assert (chi2inv ([p, NaN], single (2)), single ([NaN 0 1 Inf NaN NaN]), 5*eps ("single")) ## Test input validation %!error chi2inv () %!error chi2inv (1) %!error chi2inv (1,2,3) %!error ... %! chi2inv (ones (3), ones (2)) %!error ... %! chi2inv (ones (2), ones (3)) %!error chi2inv (i, 2) %!error chi2inv (2, i) statistics-release-1.7.3/inst/dist_fun/chi2pdf.m000066400000000000000000000071411475240274700216360ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} chi2pdf (@var{x}, @var{df}) ## ## Chi-squared probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the chi-squared distribution with @var{df} degrees of freedom. The size ## of @var{y} is the common size of @var{x} and @var{df}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Further information about the chi-squared distribution can be found at ## @url{https://en.wikipedia.org/wiki/Chi-squared_distribution} ## ## @seealso{chi2cdf, chi2pdf, chi2rnd, chi2stat} ## @end deftypefn function y = chi2pdf (x, df) ## Check for valid number of input arguments if (nargin < 2) error ("chi2pdf: function called with too few input arguments."); endif ## Check for common size of X and DF if (! isscalar (x) || ! isscalar (df)) [retval, x, df] = common_size (x, df); if (retval > 0) error ("chi2pdf: X and DF must be of common size or scalars."); endif endif ## Check for X and DF being reals if (iscomplex (x) || iscomplex (df)) error ("chi2pdf: X and DF must not be complex."); endif ## Compute chi-squared PDF y = gampdf (x, df/2, 2); endfunction %!demo %! ## Plot various PDFs from the chi-squared distribution %! x = 0:0.01:8; %! y1 = chi2pdf (x, 1); %! y2 = chi2pdf (x, 2); %! y3 = chi2pdf (x, 3); %! y4 = chi2pdf (x, 4); %! y5 = chi2pdf (x, 6); %! y6 = chi2pdf (x, 9); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", ... %! x, y4, "-c", x, y5, "-m", x, y6, "-y") %! grid on %! xlim ([0, 8]) %! ylim ([0, 0.5]) %! legend ({"df = 1", "df = 2", "df = 3", ... %! "df = 4", "df = 6", "df = 9"}, "location", "northeast") %! title ("Chi-squared PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 Inf]; %! y = [0, 1/2 * exp(-x(2:5)/2)]; %!assert (chi2pdf (x, 2*ones (1,5)), y) %!assert (chi2pdf (x, 2), y) %!assert (chi2pdf (x, 2*[1 0 NaN 1 1]), [y(1) NaN NaN y(4:5)]) %!assert (chi2pdf ([x, NaN], 2), [y, NaN]) ## Test class of input preserved %!assert (chi2pdf (single ([x, NaN]), 2), single ([y, NaN])) %!assert (chi2pdf ([x, NaN], single (2)), single ([y, NaN])) ## Test input validation %!error chi2pdf () %!error chi2pdf (1) %!error chi2pdf (1,2,3) %!error ... %! chi2pdf (ones (3), ones (2)) %!error ... %! chi2pdf (ones (2), ones (3)) %!error chi2pdf (i, 2) %!error chi2pdf (2, i) statistics-release-1.7.3/inst/dist_fun/chi2rnd.m000066400000000000000000000127751475240274700216610ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} chi2rnd (@var{df}) ## @deftypefnx {statistics} {@var{r} =} chi2rnd (@var{df}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} chi2rnd (@var{df}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} chi2rnd (@var{df}, [@var{sz}]) ## ## Random arrays from the chi-squared distribution. ## ## @code{@var{r} = chi2rnd (@var{df})} returns an array of random numbers chosen ## from the chi-squared distribution with @var{df} degrees of freedom. The size ## of @var{r} is the size of @var{df}. ## ## When called with a single size argument, @code{chi2rnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the chi-squared distribution can be found at ## @url{https://en.wikipedia.org/wiki/Chi-squared_distribution} ## ## @seealso{chi2cdf, chi2inv, chi2pdf, chi2stat} ## @end deftypefn function r = chi2rnd (df, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("chi2rnd: function called with too few input arguments."); endif ## Check for DF being reals if (iscomplex (df)) error ("chi2rnd: DF must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (df); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["chi2rnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("chi2rnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameter match requested dimensions in size if (! isscalar (df) && ! isequal (size (df), sz)) error ("chi2rnd: DF must be scalar or of size SZ."); endif ## Check for class type if (isa (df, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from chi-squared distribution if (isscalar (df)) if ((df > 0) && (df < Inf)) r = 2 * randg (df/2, sz, cls); else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (df > 0) | (df < Inf); r(k) = 2 * randg (df(k)/2, cls); endif endfunction ## Test output %!assert (size (chi2rnd (2)), [1, 1]) %!assert (size (chi2rnd (ones (2,1))), [2, 1]) %!assert (size (chi2rnd (ones (2,2))), [2, 2]) %!assert (size (chi2rnd (1, 3)), [3, 3]) %!assert (size (chi2rnd (1, [4 1])), [4, 1]) %!assert (size (chi2rnd (1, 4, 1)), [4, 1]) %!assert (size (chi2rnd (1, 4, 1)), [4, 1]) %!assert (size (chi2rnd (1, 4, 1, 5)), [4, 1, 5]) %!assert (size (chi2rnd (1, 0, 1)), [0, 1]) %!assert (size (chi2rnd (1, 1, 0)), [1, 0]) %!assert (size (chi2rnd (1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (chi2rnd (2)), "double") %!assert (class (chi2rnd (single (2))), "single") %!assert (class (chi2rnd (single ([2 2]))), "single") ## Test input validation %!error chi2rnd () %!error chi2rnd (i) %!error ... %! chi2rnd (1, -1) %!error ... %! chi2rnd (1, 1.2) %!error ... %! chi2rnd (1, ones (2)) %!error ... %! chi2rnd (1, [2 -1 2]) %!error ... %! chi2rnd (1, [2 0 2.5]) %!error ... %! chi2rnd (ones (2), ones (2)) %!error ... %! chi2rnd (1, 2, -1, 5) %!error ... %! chi2rnd (1, 2, 1.5, 5) %!error chi2rnd (ones (2,2), 3) %!error chi2rnd (ones (2,2), [3, 2]) %!error chi2rnd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/copulacdf.m000066400000000000000000000225701475240274700222620ustar00rootroot00000000000000## Copyright (C) 2008 Arno Onken ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} copulacdf (@var{family}, @var{x}, @var{theta}) ## @deftypefnx {statistics} {@var{p} =} copulacdf ('t', @var{x}, @var{theta}, @var{df}) ## ## Copula family cumulative distribution functions (CDF). ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{family} is the copula family name. Currently, @var{family} can ## be @code{'Gaussian'} for the Gaussian family, @code{'t'} for the ## Student's t family, @code{'Clayton'} for the Clayton family, ## @code{'Gumbel'} for the Gumbel-Hougaard family, @code{'Frank'} for ## the Frank family, @code{'AMH'} for the Ali-Mikhail-Haq family, or ## @code{'FGM'} for the Farlie-Gumbel-Morgenstern family. ## ## @item ## @var{x} is the support where each row corresponds to an observation. ## ## @item ## @var{theta} is the parameter of the copula. For the Gaussian and ## Student's t copula, @var{theta} must be a correlation matrix. For ## bivariate copulas @var{theta} can also be a correlation coefficient. ## For the Clayton family, the Gumbel-Hougaard family, the Frank family, ## and the Ali-Mikhail-Haq family, @var{theta} must be a vector with the ## same number of elements as observations in @var{x} or be scalar. For ## the Farlie-Gumbel-Morgenstern family, @var{theta} must be a matrix of ## coefficients for the Farlie-Gumbel-Morgenstern polynomial where each ## row corresponds to one set of coefficients for an observation in ## @var{x}. A single row is expanded. The coefficients are in binary ## order. ## ## @item ## @var{df} is the degrees of freedom for the Student's t family. ## @var{df} must be a vector with the same number of elements as ## observations in @var{x} or be scalar. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{p} is the cumulative distribution of the copula at each row of ## @var{x} and corresponding parameter @var{theta}. ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## x = [0.2:0.2:0.6; 0.2:0.2:0.6]; ## theta = [1; 2]; ## p = copulacdf ("Clayton", x, theta) ## @end group ## ## @group ## x = [0.2:0.2:0.6; 0.2:0.1:0.4]; ## theta = [0.2, 0.1, 0.1, 0.05]; ## p = copulacdf ("FGM", x, theta) ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Roger B. Nelsen. @cite{An Introduction to Copulas}. Springer, ## New York, second edition, 2006. ## @end enumerate ## ## @seealso{copulapdf, copularnd} ## @end deftypefn function p = copulacdf (family, x, theta, df) ## Check arguments if (nargin != 3 && (nargin != 4 || ! strcmpi (family, "t"))) print_usage (); endif if (! ischar (family)) error (strcar (["copulacdf: family must be one of 'Gaussian',"], ... [" 't', 'Clayton', 'Gumbel', 'Frank', 'AMH', and 'FGM'."])); endif if (! isempty (x) && ! ismatrix (x)) error ("copulacdf: X must be a numeric matrix."); endif [n, d] = size (x); lower_family = lower (family); ## Check family and copula parameters switch (lower_family) case {"gaussian", "t"} ## Family with a covariance matrix if (d == 2 && isscalar (theta)) ## Expand a scalar to a correlation matrix theta = [1, theta; theta, 1]; endif if (any (size (theta) != [d, d]) || any (diag (theta) != 1) || ... any (any (theta != theta')) || min (eig (theta)) <= 0) error ("copulacdf: THETA must be a correlation matrix."); endif if (nargin == 4) ## Student's t family if (! isscalar (df) && (! isvector (df) || length (df) != n)) error (strcar (["copulacdf: DF must be a vector with the same"], ... [" number of rows as X or be scalar."])); endif df = df(:); endif case {"clayton", "gumbel", "frank", "amh"} ## Archimedian one parameter family if (! isvector (theta) || (! isscalar (theta) && length (theta) != n)) error (strcat (["copulacdf: THETA must be a vector with the same"], ... [" number of rows as X or be scalar."])); endif theta = theta(:); if (n > 1 && isscalar (theta)) theta = repmat (theta, n, 1); endif case {"fgm"} ## Exponential number of parameters if (! ismatrix (theta) || size (theta, 2) != (2 .^ d - d - 1) || ... (size (theta, 1) != 1 && size (theta, 1) != n)) error (strcat (["copulacdf: THETA must be a row vector of length"], ... [" 2^d-d-1 or a matrix of size N x (2^d-d-1)."])); endif if (n > 1 && size (theta, 1) == 1) theta = repmat (theta, n, 1); endif otherwise error ("copulacdf: unknown copula family '%s'.", family); endswitch if (n == 0) ## Input is empty p = zeros (0, 1); else ## Truncate input to unit hypercube x(x < 0) = 0; x(x > 1) = 1; ## Compute the cumulative distribution function according to family switch (lower_family) case {"gaussian"} ## The Gaussian family p = mvncdf (norminv (x), zeros (1, d), theta); ## No parameter bounds check k = []; case {"t"} ## The Student's t family p = mvtcdf (tinv (x, df), theta, df); ## No parameter bounds check k = []; case {"clayton"} ## The Clayton family p = exp (-log (max (sum (x .^ (repmat (-theta, 1, d)), 2) ... - d + 1, 0)) ./ theta); ## Product copula at columns where theta == 0 k = find (theta == 0); if (any (k)) p(k) = prod (x(k, :), 2); endif ## Check bounds if (d > 2) k = find (! (theta >= 0) | ! (theta < inf)); else k = find (! (theta >= -1) | ! (theta < inf)); endif case {"gumbel"} ## The Gumbel-Hougaard family p = exp (-(sum ((-log (x)) .^ repmat (theta, 1, d), 2)) ... .^ (1 ./ theta)); ## Check bounds k = find (! (theta >= 1) | ! (theta < inf)); case {"frank"} ## The Frank family p = -log (1 + (prod (expm1 (repmat (-theta, 1, d) .* x), 2)) ./ ... (expm1 (-theta) .^ (d - 1))) ./ theta; ## Product copula at columns where theta == 0 k = find (theta == 0); if (any (k)) p(k) = prod (x(k, :), 2); endif ## Check bounds if (d > 2) k = find (! (theta > 0) | ! (theta < inf)); else k = find (! (theta > -inf) | ! (theta < inf)); endif case {"amh"} ## The Ali-Mikhail-Haq family p = (theta - 1) ./ (theta - prod ((1 + repmat (theta, 1, d) ... .* (x - 1)) ./ x, 2)); ## Check bounds if (d > 2) k = find (! (theta >= 0) | ! (theta < 1)); else k = find (! (theta >= -1) | ! (theta < 1)); endif case {"fgm"} ## The Farlie-Gumbel-Morgenstern family ## All binary combinations bcomb = logical (floor (mod (((0:(2 .^ d - 1))' * 2 .^ ... ((1 - d):0)), 2))); ecomb = ones (size (bcomb)); ecomb(bcomb) = -1; ## Summation over all combinations of order >= 2 bcomb = bcomb(sum (bcomb, 2) >= 2, end:-1:1); ## Linear constraints matrix ac = zeros (size (ecomb, 1), size (bcomb, 1)); ## Matrix to compute p ap = zeros (size (x, 1), size (bcomb, 1)); for i = 1:size (bcomb, 1) ac(:, i) = -prod (ecomb(:, bcomb(i, :)), 2); ap(:, i) = prod (1 - x(:, bcomb(i, :)), 2); endfor p = prod (x, 2) .* (1 + sum (ap .* theta, 2)); ## Check linear constraints k = false (n, 1); for i = 1:n k(i) = any (ac * theta(i, :)' > 1); endfor endswitch ## Out of bounds parameters if (any (k)) p(k) = NaN; endif endif endfunction ## Test output %!test %! x = [0.2:0.2:0.6; 0.2:0.2:0.6]; %! theta = [1; 2]; %! p = copulacdf ("Clayton", x, theta); %! expected_p = [0.1395; 0.1767]; %! assert (p, expected_p, 0.001); %!test %! x = [0.2:0.2:0.6; 0.2:0.2:0.6]; %! p = copulacdf ("Gumbel", x, 2); %! expected_p = [0.1464; 0.1464]; %! assert (p, expected_p, 0.001); %!test %! x = [0.2:0.2:0.6; 0.2:0.2:0.6]; %! theta = [1; 2]; %! p = copulacdf ("Frank", x, theta); %! expected_p = [0.0699; 0.0930]; %! assert (p, expected_p, 0.001); %!test %! x = [0.2:0.2:0.6; 0.2:0.2:0.6]; %! theta = [0.3; 0.7]; %! p = copulacdf ("AMH", x, theta); %! expected_p = [0.0629; 0.0959]; %! assert (p, expected_p, 0.001); %!test %! x = [0.2:0.2:0.6; 0.2:0.1:0.4]; %! theta = [0.2, 0.1, 0.1, 0.05]; %! p = copulacdf ("FGM", x, theta); %! expected_p = [0.0558; 0.0293]; %! assert (p, expected_p, 0.001); statistics-release-1.7.3/inst/dist_fun/copulapdf.m000066400000000000000000000144261475240274700223000ustar00rootroot00000000000000## Copyright (C) 2008 Arno Onken ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} copulapdf (@var{family}, @var{x}, @var{theta}) ## ## Copula family probability density functions (PDF). ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{family} is the copula family name. Currently, @var{family} can ## be @code{'Clayton'} for the Clayton family, @code{'Gumbel'} for the ## Gumbel-Hougaard family, @code{'Frank'} for the Frank family, or ## @code{'AMH'} for the Ali-Mikhail-Haq family. ## ## @item ## @var{x} is the support where each row corresponds to an observation. ## ## @item ## @var{theta} is the parameter of the copula. The elements of ## @var{theta} must be greater than or equal to @code{-1} for the ## Clayton family, greater than or equal to @code{1} for the ## Gumbel-Hougaard family, arbitrary for the Frank family, and greater ## than or equal to @code{-1} and lower than @code{1} for the ## Ali-Mikhail-Haq family. Moreover, @var{theta} must be non-negative ## for dimensions greater than @code{2}. @var{theta} must be a column ## vector with the same number of rows as @var{x} or be scalar. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{y} is the probability density of the copula at each row of ## @var{x} and corresponding parameter @var{theta}. ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## x = [0.2:0.2:0.6; 0.2:0.2:0.6]; ## theta = [1; 2]; ## y = copulapdf ("Clayton", x, theta) ## @end group ## ## @group ## y = copulapdf ("Gumbel", x, 2) ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Roger B. Nelsen. @cite{An Introduction to Copulas}. Springer, ## New York, second edition, 2006. ## @end enumerate ## ## @seealso{copulacdf, copularnd} ## @end deftypefn function y = copulapdf (family, x, theta) ## Check arguments if (nargin != 3) print_usage (); endif if (! ischar (family)) error (strcat (["copulapdf: family must be one of 'Clayton',"], ... [" 'Gumbel', 'Frank', and 'AMH'."])); endif if (! isempty (x) && ! ismatrix (x)) error ("copulapdf: X must be a numeric matrix."); endif [n, d] = size (x); if (! isvector (theta) || (! isscalar (theta) && size (theta, 1) != n)) error (strcat (["copulapdf: THETA must be a column vector with the"], ... [" same number of rows as X or be scalar."])); endif if (n == 0) ## Input is empty y = zeros (0, 1); else if (n > 1 && isscalar (theta)) theta = repmat (theta, n, 1); endif ## Truncate input to unit hypercube x(x < 0) = 0; x(x > 1) = 1; ## Compute the cumulative distribution function according to family lowerarg = lower (family); if (strcmp (lowerarg, "clayton")) ## The Clayton family log_cdf = -log (max (sum (x .^ (repmat (-theta, 1, d)), 2) ... - d + 1, 0)) ./ theta; y = prod (repmat (theta, 1, d) .* repmat (0:(d - 1), n, 1) + 1, 2) ... .* exp ((1 + theta .* d) .* log_cdf - ... (theta + 1) .* sum (log (x), 2)); ## Product copula at columns where theta == 0 k = find (theta == 0); if (any (k)) y(k) = 1; endif ## Check theta if (d > 2) k = find (! (theta >= 0) | ! (theta < inf)); else k = find (! (theta >= -1) | ! (theta < inf)); endif elseif (strcmp (lowerarg, "gumbel")) ## The Gumbel-Hougaard family g = sum ((-log (x)) .^ repmat (theta, 1, d), 2); c = exp (-g .^ (1 ./ theta)); y = ((prod (-log (x), 2)) .^ (theta - 1)) ./ prod (x, 2) .* c .* ... (g .^ (2 ./ theta - 2) + (theta - 1) .* g .^ (1 ./ theta - 2)); ## Check theta k = find (! (theta >= 1) | ! (theta < inf)); elseif (strcmp (lowerarg, "frank")) ## The Frank family if (d != 2) error ("copulapdf: Frank copula PDF implemented as bivariate only."); endif y = (theta .* exp (theta .* (1 + sum (x, 2))) .* (exp (theta) - 1)) ./ ... (exp (theta) - exp (theta + theta .* x(:, 1)) + ... exp (theta .* sum (x, 2)) - exp (theta + theta .* x(:, 2))) .^ 2; ## Product copula at columns where theta == 0 k = find (theta == 0); if (any (k)) y(k) = 1; endif ## Check theta k = find (! (theta > -inf) | ! (theta < inf)); elseif (strcmp (lowerarg, "amh")) ## The Ali-Mikhail-Haq family if (d != 2) error (strcat (["copulapdf: Ali-Mikhail-Haq copula PDF"], ... [" implemented as bivariate only."])); endif z = theta .* prod (x - 1, 2) - 1; y = (theta .* (1 - sum (x, 2) - prod (x, 2) - z) - 1) ./ (z .^ 3); ## Check theta k = find (! (theta >= -1) | ! (theta < 1)); else error ("copulapdf: unknown copula family '%s'.", family); endif if (any (k)) y(k) = NaN; endif endif endfunction ## Test output %!test %! x = [0.2:0.2:0.6; 0.2:0.2:0.6]; %! theta = [1; 2]; %! y = copulapdf ("Clayton", x, theta); %! expected_p = [0.9872; 0.7295]; %! assert (y, expected_p, 0.001); %!test %! x = [0.2:0.2:0.6; 0.2:0.2:0.6]; %! y = copulapdf ("Gumbel", x, 2); %! expected_p = [0.9468; 0.9468]; %! assert (y, expected_p, 0.001); %!test %! x = [0.2, 0.6; 0.2, 0.6]; %! theta = [1; 2]; %! y = copulapdf ("Frank", x, theta); %! expected_p = [0.9378; 0.8678]; %! assert (y, expected_p, 0.001); %!test %! x = [0.2, 0.6; 0.2, 0.6]; %! theta = [0.3; 0.7]; %! y = copulapdf ("AMH", x, theta); %! expected_p = [0.9540; 0.8577]; %! assert (y, expected_p, 0.001); statistics-release-1.7.3/inst/dist_fun/copularnd.m000066400000000000000000000201141475240274700223010ustar00rootroot00000000000000## Copyright (C) 2012 Arno Onken ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{r} =} copularnd (@var{family}, @var{theta}, @var{n}) ## @deftypefnx {Function File} {@var{r} =} copularnd (@var{family}, @var{theta}, @var{n}, @var{d}) ## @deftypefnx {Function File} {@var{r} =} copularnd ('t', @var{theta}, @var{df}, @var{n}) ## ## Random arrays from the copula family distributions. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{family} is the copula family name. Currently, @var{family} can be ## @code{'Gaussian'} for the Gaussian family, @code{'t'} for the Student's t ## family, or @code{'Clayton'} for the Clayton family. ## ## @item ## @var{theta} is the parameter of the copula. For the Gaussian and Student's t ## copula, @var{theta} must be a correlation matrix. For bivariate copulas ## @var{theta} can also be a correlation coefficient. For the Clayton family, ## @var{theta} must be a vector with the same number of elements as samples to ## be generated or be scalar. ## ## @item ## @var{df} is the degrees of freedom for the Student's t family. @var{df} must ## be a vector with the same number of elements as samples to be generated or ## be scalar. ## ## @item ## @var{n} is the number of rows of the matrix to be generated. @var{n} must be ## a non-negative integer and corresponds to the number of samples to be ## generated. ## ## @item ## @var{d} is the number of columns of the matrix to be generated. @var{d} must ## be a positive integer and corresponds to the dimension of the copula. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{r} is a matrix of random samples from the copula with @var{n} samples ## of distribution dimension @var{d}. ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## theta = 0.5; ## r = copularnd ("Gaussian", theta); ## @end group ## ## @group ## theta = 0.5; ## df = 2; ## r = copularnd ("t", theta, df); ## @end group ## ## @group ## theta = 0.5; ## n = 2; ## r = copularnd ("Clayton", theta, n); ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Roger B. Nelsen. @cite{An Introduction to Copulas}. Springer, New York, ## second edition, 2006. ## @end enumerate ## @end deftypefn function r = copularnd (family, theta, df, n) ## Check arguments if (nargin < 2) print_usage (); endif if (! ischar (family)) error ("copularnd: family must be one of 'Gaussian', 't', and 'Clayton'."); endif lower_family = lower (family); ## Check family and copula parameters switch (lower_family) case {"gaussian"} ## Gaussian family if (isscalar (theta)) ## Expand a scalar to a correlation matrix theta = [1, theta; theta, 1]; endif if (! ismatrix (theta) || any (diag (theta) != 1) || ... any (any (theta != theta')) || min (eig (theta)) <= 0) error ("copularnd: THETA must be a correlation matrix."); endif if (nargin > 3) d = n; if (! isscalar (d) || d != size (theta, 1)) error ("copularnd: D must correspond to dimension of theta."); endif else d = size (theta, 1); endif if (nargin < 3) n = 1; else n = df; if (! isscalar (n) || (n < 0) || round (n) != n) error ("copularnd: N must be a non-negative integer."); endif endif case {"t"} ## Student's t family if (nargin < 3) print_usage (); endif if (isscalar (theta)) ## Expand a scalar to a correlation matrix theta = [1, theta; theta, 1]; endif if (! ismatrix (theta) || any (diag (theta) != 1) || ... any (any (theta != theta')) || min (eig (theta)) <= 0) error ("copularnd: THETA must be a correlation matrix."); endif if (! isscalar (df) && (! isvector (df) || length (df) != n)) error (strcat (["copularnd: DF must be a vector with the same"], ... [" number of rows as r or be scalar."])); endif df = df(:); if (nargin < 4) n = 1; else if (! isscalar (n) || (n < 0) || round (n) != n) error ("copularnd: N must be a non-negative integer."); endif endif case {"clayton"} ## Archimedian one parameter family if (nargin < 4) ## Default is bivariate d = 2; else d = n; if (! isscalar (d) || (d < 2) || round (d) != d) error ("copularnd: D must be an integer greater than 1."); endif endif if (nargin < 3) ## Default is one sample n = 1; else n = df; if (! isscalar (n) || (n < 0) || round (n) != n) error ("copularnd: N must be a non-negative integer."); endif endif if (! isvector (theta) || (! isscalar (theta) && size (theta, 1) != n)) error (strcaty (["copularnd: THETA must be a column vector with"], ... [" the number of rows equal to N or be scalar."])); endif if (n > 1 && isscalar (theta)) theta = repmat (theta, n, 1); endif otherwise error ("copularnd: unknown copula family '%s'.", family); endswitch if (n == 0) ## Input is empty r = zeros (0, d); else ## Draw random samples according to family switch (lower_family) case {"gaussian"} ## The Gaussian family r = normcdf (mvnrnd (zeros (1, d), theta, n), 0, 1); ## No parameter bounds check k = []; case {"t"} ## The Student's t family r = tcdf (mvtrnd (theta, df, n), df); ## No parameter bounds check k = []; case {"clayton"} ## The Clayton family u = rand (n, d); if (d == 2) r = zeros (n, 2); ## Conditional distribution method for the bivariate case which also ## works for theta < 0 r(:, 1) = u(:, 1); r(:, 2) = (1 + u(:, 1) .^ (-theta) .* (u(:, 2) .^ ... (-theta ./ (1 + theta)) - 1)) .^ (-1 ./ theta); else ## Apply the algorithm by Marshall and Olkin: ## Frailty distribution for Clayton copula is gamma y = randg (1 ./ theta, n, 1); r = (1 - log (u) ./ repmat (y, 1, d)) .^ (-1 ./ repmat (theta, 1, d)); endif k = find (theta == 0); if (any (k)) ## Product copula at columns k r(k, :) = u(k, :); endif ## Continue argument check if (d == 2) k = find (! (theta >= -1) | ! (theta < inf)); else k = find (! (theta >= 0) | ! (theta < inf)); endif endswitch ## Out of bounds parameters if (any (k)) r(k, :) = NaN; endif endif endfunction ## Test output %!test %! theta = 0.5; %! r = copularnd ("Gaussian", theta); %! assert (size (r), [1, 2]); %! assert (all ((r >= 0) & (r <= 1))); %!test %! theta = 0.5; %! df = 2; %! r = copularnd ("t", theta, df); %! assert (size (r), [1, 2]); %! assert (all ((r >= 0) & (r <= 1))); %!test %! theta = 0.5; %! r = copularnd ("Clayton", theta); %! assert (size (r), [1, 2]); %! assert (all ((r >= 0) & (r <= 1))); %!test %! theta = 0.5; %! n = 2; %! r = copularnd ("Clayton", theta, n); %! assert (size (r), [n, 2]); %! assert (all ((r >= 0) & (r <= 1))); %!test %! theta = [1; 2]; %! n = 2; %! d = 3; %! r = copularnd ("Clayton", theta, n, d); %! assert (size (r), [n, d]); %! assert (all ((r >= 0) & (r <= 1))); statistics-release-1.7.3/inst/dist_fun/evcdf.m000066400000000000000000000214741475240274700214130ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} evcdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} evcdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} evcdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} evcdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} evcdf (@var{x}, @var{mu}, @var{sigma}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} evcdf (@var{x}, @var{mu}, @var{sigma}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} evcdf (@dots{}, @qcode{"upper"}) ## ## Extreme value cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the extreme value distribution (also known as the Gumbel or the type ## I generalized extreme value distribution) at the values in @var{x} with ## location parameter @var{mu} and scale parameter @var{sigma}. The size of ## @var{p} is the common size of @var{x}, @var{mu} and @var{sigma}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## Default values are @var{mu} = 0 and @var{sigma} = 1. ## ## When called with three output arguments, i.e. @qcode{[@var{p}, @var{plo}, ## @var{pup}]}, @code{evcdf} computes the confidence bounds for @var{p} when the ## input parameters @var{mu} and @var{sigma} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{plo} and @var{pup} are arrays of the same size as ## @var{p} containing the lower and upper confidence bounds. ## ## @code{[@dots{}] = evcdf (@dots{}, "upper")} computes the upper tail ## probability of the extreme value distribution with parameters @var{x0} and ## @var{gamma}, at the values in @var{x}. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling minima. For modeling maxima, use the alternative ## Gumbel CDF, @code{gumbelcdf}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evinv, evpdf, evrnd, evfit, evlike, evstat, gumbelcdf} ## @end deftypefn function [varargout] = evcdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 6) error ("evcdf: invalid number of input arguments."); endif ## Check for 'upper' flag if (nargin > 1 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 1 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("evcdf: invalid argument for upper tail."); else uflag = false; endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) mu = varargin{1}; else mu = 0; endif if (numel (varargin) > 1) sigma = varargin{2}; else sigma = 1; endif if (numel (varargin) > 2) pcov = varargin{3}; ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("evcdf: invalid size of covariance matrix."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("evcdf: covariance matrix is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 3) alpha = varargin{4}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("evcdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma)) [err, x, mu, sigma] = common_size (x, mu, sigma); if (err > 0) error ("evcdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("evcdf: X, MU, and SIGMA must not be complex."); endif ## Return NaNs for out of range parameters. sigma(sigma <= 0) = NaN; ## Compute extreme value cdf z = (x - mu) ./ sigma; if (uflag) p = exp (-exp (z)); else p = -expm1 (-exp (z)); endif ## Check for appropriate class if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output varargout{1} = cast (p, is_class); if (nargout > 1) plo = NaN (size (z), is_class); pup = NaN (size (z), is_class); endif ## Check sigma if (isscalar (sigma)) if (sigma > 0) sigma_p = true (size (z)); else if (nargout == 3) varargout{2} = plo; varargout{3} = pup; endif return; endif else sigma_p = sigma > 0; endif ## Compute confidence bounds (if requested) if (nargout >= 2) zvar = (pcov(1,1) + 2 * pcov(1,2) * z(sigma_p) + ... pcov(2,2) * z(sigma_p) .^ 2) ./ (sigma .^ 2); if (any (zvar < 0)) error ("evcdf: bad covariance matrix."); endif normz = -probit (alpha / 2); halfwidth = normz * sqrt (zvar); zlo = z(sigma_p) - halfwidth; zup = z(sigma_p) + halfwidth; if (uflag) plo(sigma_p) = exp (-exp (zup)); pup(sigma_p) = exp (-exp (zlo)); else plo(sigma_p) = -expm1 (-exp (zlo)); pup(sigma_p) = -expm1 (-exp (zup)); endif varargout{2} = plo; varargout{3} = pup; endif endfunction %!demo %! ## Plot various CDFs from the extreme value distribution %! x = -10:0.01:10; %! p1 = evcdf (x, 0.5, 2); %! p2 = evcdf (x, 1.0, 2); %! p3 = evcdf (x, 1.5, 3); %! p4 = evcdf (x, 3.0, 4); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! legend ({"μ = 0.5, σ = 2", "μ = 1.0, σ = 2", ... %! "μ = 1.5, σ = 3", "μ = 3.0, σ = 4"}, "location", "southeast") %! title ("Extreme value CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-Inf, 1, 2, Inf]; %! y = [0, 0.6321, 0.9340, 1]; %!assert (evcdf (x, ones (1,4), ones (1,4)), y, 1e-4) %!assert (evcdf (x, 1, ones (1,4)), y, 1e-4) %!assert (evcdf (x, ones (1,4), 1), y, 1e-4) %!assert (evcdf (x, [0, -Inf, NaN, Inf], 1), [0, 1, NaN, NaN], 1e-4) %!assert (evcdf (x, 1, [Inf, NaN, -1, 0]), [NaN, NaN, NaN, NaN], 1e-4) %!assert (evcdf ([x(1:2), NaN, x(4)], 1, 1), [y(1:2), NaN, y(4)], 1e-4) %!assert (evcdf (x, "upper"), [1, 0.0660, 0.0006, 0], 1e-4) ## Test class of input preserved %!assert (evcdf ([x, NaN], 1, 1), [y, NaN], 1e-4) %!assert (evcdf (single ([x, NaN]), 1, 1), single ([y, NaN]), 1e-4) %!assert (evcdf ([x, NaN], single (1), 1), single ([y, NaN]), 1e-4) %!assert (evcdf ([x, NaN], 1, single (1)), single ([y, NaN]), 1e-4) ## Test input validation %!error evcdf () %!error evcdf (1,2,3,4,5,6,7) %!error evcdf (1, 2, 3, 4, "uper") %!error ... %! evcdf (ones (3), ones (2), ones (2)) %!error evcdf (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = evcdf (1, 2, 3) %!error [p, plo, pup] = ... %! evcdf (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! evcdf (1, 2, 3, [1, 0; 0, 1], 1.22) %!error [p, plo, pup] = ... %! evcdf (1, 2, 3, [1, 0; 0, 1], "alpha", "upper") %!error evcdf (i, 2, 2) %!error evcdf (2, i, 2) %!error evcdf (2, 2, i) %!error ... %! [p, plo, pup] = evcdf (1, 2, 3, [1, 0; 0, -inf], 0.04) statistics-release-1.7.3/inst/dist_fun/evinv.m000066400000000000000000000165401475240274700214510ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} evinv (@var{p}) ## @deftypefnx {statistics} {@var{x} =} evinv (@var{p}, @var{mu}) ## @deftypefnx {statistics} {@var{x} =} evinv (@var{p}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {[@var{x}, @var{xlo}, @var{xup}] =} evinv (@var{p}, @var{mu}, @var{sigma}, @var{pcov}) ## @deftypefnx {statistics} {[@var{x}, @var{xlo}, @var{xup}] =} evinv (@var{p}, @var{mu}, @var{sigma}, @var{pcov}, @var{alpha}) ## ## Inverse of the extreme value cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the extreme value distribution (also known as the Gumbel or the type I ## generalized extreme value distribution) with location parameter @var{mu} and ## scale parameter @var{sigma}. The size of @var{x} is the common size of ## @var{p}, @var{mu} and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Default values are @var{mu} = 0 and @var{sigma} = 1. ## ## When called with three output arguments, i.e. @qcode{[@var{x}, @var{xlo}, ## @var{xup}]}, @code{evinv} computes the confidence bounds for @var{x} when the ## input parameters @var{mu} and @var{sigma} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{xlo} and @var{xup} are arrays of the same size as ## @var{x} containing the lower and upper confidence bounds. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling minima. For modeling maxima, use the alternative ## Gumbel iCDF, @code{gumbelinv}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evcdf, evpdf, evrnd, evfit, evlike, evstat, gumbelinv} ## @end deftypefn function [x, xlo, xup] = evinv (p, mu, sigma, pcov, alpha) ## Check for valid number of input arguments if (nargin < 1 || nargin > 5) error ("evinv: invalid number of input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif if (nargin < 3) sigma = 1; endif ## Check if PCOV is provided when confidence bounds are requested if (nargout > 2) if (nargin < 4) error ("evinv: covariance matrix is required for confidence bounds."); endif ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("evinv: invalid size of covariance matrix."); endif ## Check for valid alpha value if (nargin < 5) alpha = 0.05; elseif (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("evinv: invalid value for alpha."); endif endif ## Check for common size of P, MU, and SIGMA if (! isscalar (p) || ! isscalar (mu) || ! isscalar (sigma)) [err, p, mu, sigma] = common_size (p, mu, sigma); if (err > 0) error ("evinv: P, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for P, MU, and SIGMA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma)) error ("evinv: P, MU, and SIGMA must not be complex."); endif ## Check for appropriate class if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single")); is_class = "single"; else is_class = "double"; endif ## Compute inverse of type 1 extreme value cdf k = (eps <= p & p < 1); if (all (k(:))) q = log (-log (1 - p)); else q = zeros (size (p), is_class); q(k) = log (-log (1 - p(k))); ## Return -Inf for p = 0 and Inf for p = 1 q(p < eps) = -Inf; q(p == 1) = Inf; ## Return NaN for out of range values of P q(p < 0 | 1 < p | isnan (p)) = NaN; endif ## Return NaN for out of range values of SIGMA sigma(sigma <= 0) = NaN; x = sigma .* q + mu; ## Compute confidence bounds if requested. if (nargout >= 2) xvar = pcov(1,1) + 2 * pcov(1,2) * q + pcov(2,2) * q .^ 2; if (any (xvar < 0)) error ("evinv: bad covariance matrix."); endif z = -norminv (alpha / 2); halfwidth = z * sqrt (xvar); xlo = x - halfwidth; xup = x + halfwidth; endif endfunction %!demo %! ## Plot various iCDFs from the extreme value distribution %! p = 0.001:0.001:0.999; %! x1 = evinv (p, 0.5, 2); %! x2 = evinv (p, 1.0, 2); %! x3 = evinv (p, 1.5, 3); %! x4 = evinv (p, 3.0, 4); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([-10, 10]) %! legend ({"μ = 0.5, σ = 2", "μ = 1.0, σ = 2", ... %! "μ = 1.5, σ = 3", "μ = 3.0, σ = 4"}, "location", "northwest") %! title ("Extreme value iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, x %! p = [0, 0.05, 0.5 0.95]; %! x = [-Inf, -2.9702, -0.3665, 1.0972]; %!assert (evinv (p), x, 1e-4) %!assert (evinv (p, zeros (1,4), ones (1,4)), x, 1e-4) %!assert (evinv (p, 0, ones (1,4)), x, 1e-4) %!assert (evinv (p, zeros (1,4), 1), x, 1e-4) %!assert (evinv (p, [0, -Inf, NaN, Inf], 1), [-Inf, -Inf, NaN, Inf], 1e-4) %!assert (evinv (p, 0, [Inf, NaN, -1, 0]), [-Inf, NaN, NaN, NaN], 1e-4) %!assert (evinv ([p(1:2), NaN, p(4)], 0, 1), [x(1:2), NaN, x(4)], 1e-4) ## Test class of input preserved %!assert (evinv ([p, NaN], 0, 1), [x, NaN], 1e-4) %!assert (evinv (single ([p, NaN]), 0, 1), single ([x, NaN]), 1e-4) %!assert (evinv ([p, NaN], single (0), 1), single ([x, NaN]), 1e-4) %!assert (evinv ([p, NaN], 0, single (1)), single ([x, NaN]), 1e-4) ## Test input validation %!error evinv () %!error evinv (1,2,3,4,5,6) %!error ... %! evinv (ones (3), ones (2), ones (2)) %!error ... %! [p, plo, pup] = evinv (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = evinv (1, 2, 3) %!error [p, plo, pup] = ... %! evinv (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! evinv (1, 2, 3, [1, 0; 0, 1], 1.22) %!error evinv (i, 2, 2) %!error evinv (2, i, 2) %!error evinv (2, 2, i) %!error ... %! [p, plo, pup] = evinv (1, 2, 3, [-1, -10; -Inf, -Inf], 0.04) statistics-release-1.7.3/inst/dist_fun/evpdf.m000066400000000000000000000103401475240274700214160ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} evpdf (@var{x}) ## @deftypefnx {statistics} {@var{y} =} evpdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{y} =} evpdf (@var{x}, @var{mu}, @var{sigma}) ## ## Extreme value probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the extreme value distribution (also known as the Gumbel or the type I ## generalized extreme value distribution) with location parameter @var{mu} and ## scale parameter @var{sigma}. The size of @var{y} is the common size of ## @var{x}, @var{mu} and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Default values are @var{mu} = 0 and @var{sigma} = 1. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling minima. For modeling maxima, use the alternative ## Gumbel iCDF, @code{gumbelinv}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evcdf, evinv, evrnd, evfit, evlike, evstat, gumbelpdf} ## @end deftypefn function y = evpdf (x, mu, sigma) ## Check for valid number of input arguments if (nargin < 1) error ("evpdf: function called with too few input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif if (nargin < 3) sigma = 1; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma)) [err, x, mu, sigma] = common_size (x, mu, sigma); if (err > 0) error ("evpdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("evpdf: X, MU, and SIGMA must not be complex."); endif ## Return NaNs for out of range parameters sigma(sigma <= 0) = NaN; ## Compute pdf of type 1 extreme value distribution z = (x - mu) ./ sigma; y = exp (z - exp (z)) ./ sigma; ## Force 0 for extreme right tail, instead of getting exp (Inf - Inf) = NaN y(z == Inf) = 0; endfunction %!demo %! ## Plot various PDFs from the Extreme value distribution %! x = -10:0.001:10; %! y1 = evpdf (x, 0.5, 2); %! y2 = evpdf (x, 1.0, 2); %! y3 = evpdf (x, 1.5, 3); %! y4 = evpdf (x, 3.0, 4); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! ylim ([0, 0.2]) %! legend ({"μ = 0.5, σ = 2", "μ = 1.0, σ = 2", ... %! "μ = 1.5, σ = 3", "μ = 3.0, σ = 4"}, "location", "northeast") %! title ("Extreme value PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y0, y1 %! x = [-5, 0, 1, 2, 3]; %! y0 = [0.0067, 0.3679, 0.1794, 0.0046, 0]; %! y1 = [0.0025, 0.2546, 0.3679, 0.1794, 0.0046]; %!assert (evpdf (x), y0, 1e-4) %!assert (evpdf (x, zeros (1,5), ones (1,5)), y0, 1e-4) %!assert (evpdf (x, ones (1,5), ones (1,5)), y1, 1e-4) ## Test input validation %!error evpdf () %!error ... %! evpdf (ones (3), ones (2), ones (2)) %!error evpdf (i, 2, 2) %!error evpdf (2, i, 2) %!error evpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/evrnd.m000066400000000000000000000151771475240274700214450ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} evrnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} evrnd (@var{mu}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} evrnd (@var{mu}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} evrnd (@var{mu}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the extreme value distribution. ## ## @code{@var{r} = evrnd (@var{mu}, @var{sigma})} returns an array of random ## numbers chosen from the extreme value distribution (also known as the Gumbel ## or the type I generalized extreme value distribution) with location ## parameter @var{mu} and scale parameter @var{sigma}. The size of @var{r} is ## the common size of @var{mu} and @var{sigma}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{evrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling minima. For modeling maxima, use the alternative ## Gumbel iCDF, @code{gumbelinv}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evcdf, evinv, evpdf, evfit, evlike, evstat} ## @end deftypefn function r = evrnd (mu, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("evrnd: function called with too few input arguments."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("evrnd: MU and SIGMA must be of common size or scalars."); endif endif ## Check for MU and SIGMA being reals if (iscomplex (mu) || iscomplex (sigma)) error ("evrnd: MU and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["evrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("evrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("evrnd: MU and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single")) cls = "single"; else cls = "double"; endif ## Return NaNs for out of range values of SIGMA sigma(sigma < 0) = NaN; ## Generate uniform random values, and apply the extreme value inverse CDF. r = log (-log (rand (sz, cls))) .* sigma + mu; endfunction ## Test output %!assert (size (evrnd (1, 1)), [1 1]) %!assert (size (evrnd (1, ones (2,1))), [2, 1]) %!assert (size (evrnd (1, ones (2,2))), [2, 2]) %!assert (size (evrnd (ones (2,1), 1)), [2, 1]) %!assert (size (evrnd (ones (2,2), 1)), [2, 2]) %!assert (size (evrnd (1, 1, 3)), [3, 3]) %!assert (size (evrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (evrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (evrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (evrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (evrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (evrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (evrnd (1, 1)), "double") %!assert (class (evrnd (1, single (1))), "single") %!assert (class (evrnd (1, single ([1, 1]))), "single") %!assert (class (evrnd (single (1), 1)), "single") %!assert (class (evrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error evrnd () %!error evrnd (1) %!error ... %! evrnd (ones (3), ones (2)) %!error ... %! evrnd (ones (2), ones (3)) %!error evrnd (i, 2, 3) %!error evrnd (1, i, 3) %!error ... %! evrnd (1, 2, -1) %!error ... %! evrnd (1, 2, 1.2) %!error ... %! evrnd (1, 2, ones (2)) %!error ... %! evrnd (1, 2, [2 -1 2]) %!error ... %! evrnd (1, 2, [2 0 2.5]) %!error ... %! evrnd (1, 2, 2, -1, 5) %!error ... %! evrnd (1, 2, 2, 1.5, 5) %!error ... %! evrnd (2, ones (2), 3) %!error ... %! evrnd (2, ones (2), [3, 2]) %!error ... %! evrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/expcdf.m000066400000000000000000000203071475240274700215670ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} expcdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} expcdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} expcdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} expcdf (@var{x}, @var{mu}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} expcdf (@var{x}, @var{mu}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} expcdf (@dots{}, @qcode{"upper"}) ## ## Exponential cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the exponential distribution with mean parameter @var{mu}. The size ## of @var{p} is the common size of @var{x} and @var{mu}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Default value is @var{mu} = 1. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## When called with three output arguments, i.e. @qcode{[@var{p}, @var{plo}, ## @var{pup}]}, @code{expcdf} computes the confidence bounds for @var{p} when ## the input parameter @var{mu} is an estimate. In such case, @var{pcov}, a ## scalar value with the variance of the estimated parameter @var{mu}, is ## necessary. Optionally, @var{alpha}, which has a default value of 0.05, ## specifies the @qcode{100 * (1 - @var{alpha})} percent confidence bounds. ## @var{plo} and @var{pup} are arrays of the same size as @var{p} containing the ## lower and upper confidence bounds. ## ## @code{[@dots{}] = expcdf (@dots{}, "upper")} computes the upper tail ## probability of the exponential distribution with parameter @var{mu}, at the ## values in @var{x}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expinv, exppdf, exprnd, expfit, explike, expstat} ## @end deftypefn function [varargout] = expcdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 5) error ("expcdf: invalid number of input arguments."); endif ## Check for "upper" flag if (nargin > 1 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 1 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("expcdf: invalid argument for upper tail."); else uflag = false; endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) mu = varargin{1}; else mu = 1; endif if (numel (varargin) > 1) pcov = varargin{2}; ## Check for variance being a scalar if (! isscalar (pcov)) error ("expcdf: invalid size of variance, PCOV must be a scalar."); endif if (pcov < 0) error ("expcdf: variance, PCOV, cannot be negative."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("expcdf: variance, PCOV, is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 2) alpha = varargin{3}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) != 1 || alpha <= 0 || alpha >= 1) error ("expcdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X and MU if (! isscalar (x) || ! isscalar (mu)) [err, x, mu] = common_size (x, mu); if (err > 0) error ("expcdf: X and MU must be of common size or scalars."); endif endif ## Check for X and MU being reals if (iscomplex (x) || iscomplex (mu)) error ("expcdf: X and MU must not be complex."); endif ## Check for appropriate class if (isa (x, "single") || isa (mu, "single")); is_class = "single"; else is_class = "double"; endif ## Return NaNs for out of range parameters. mu(mu <= 0) = NaN; ## Compute P value for exponential cdf z = x ./ mu; ## Force 0 for negative X z(z < 0) = 0; ## Check uflag if (uflag) p = exp (-z); else p = -expm1 (-z); endif ## Prepare output varargout{1} = cast (p, is_class); if (nargout > 1) plo = NaN (size (z), is_class); pup = NaN (size (z), is_class); endif ## Compute confidence bounds (if requested) if (nargout >= 2) ## Convert to log scale log_z = log (z); norm_z = -probit (alpha / 2); halfwidth = norm_z * sqrt (pcov ./ (mu .^ 2)); zlo = log_z - halfwidth; zup = log_z + halfwidth; ## Convert to original scale if (uflag) plo = exp (-exp (zup)); pup = exp (-exp (zlo)); else plo = - expm1 (-exp (zlo)); pup = - expm1 (-exp (zup)); endif ## Prepare output varargout{2} = plo; varargout{3} = pup; endif endfunction %!demo %! ## Plot various CDFs from the exponential distribution %! x = 0:0.01:5; %! p1 = expcdf (x, 2/3); %! p2 = expcdf (x, 1.0); %! p3 = expcdf (x, 2.0); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r") %! grid on %! legend ({"μ = 2/3", "μ = 1", "μ = 2"}, "location", "southeast") %! title ("Exponential CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, p %! x = [-1 0 0.5 1 Inf]; %! p = [0, 1 - exp(-x(2:end)/2)]; %!assert (expcdf (x, 2 * ones (1, 5)), p, 1e-16) %!assert (expcdf (x, 2), p, 1e-16) %!assert (expcdf (x, 2 * [1, 0, NaN, 1, 1]), [0, NaN, NaN, p(4:5)], 1e-16) %!assert (expcdf ([x, NaN], 2), [p, NaN], 1e-16) ## Test class of input preserved %!assert (expcdf (single ([x, NaN]), 2), single ([p, NaN])) %!assert (expcdf ([x, NaN], single (2)), single ([p, NaN])) ## Test values against MATLAB output %!test %! [p, plo, pup] = expcdf (1, 2, 3); %! assert (p, 0.39346934028737, 1e-14); %! assert (plo, 0.08751307220484, 1e-14); %! assert (pup, 0.93476821257933, 1e-14); %!test %! [p, plo, pup] = expcdf (1, 2, 2, 0.1); %! assert (p, 0.39346934028737, 1e-14); %! assert (plo, 0.14466318041675, 1e-14); %! assert (pup, 0.79808291849140, 1e-14); %!test %! [p, plo, pup] = expcdf (1, 2, 2, 0.1, "upper"); %! assert (p, 0.60653065971263, 1e-14); %! assert (plo, 0.20191708150860, 1e-14); %! assert (pup, 0.85533681958325, 1e-14); ## Test input validation %!error expcdf () %!error expcdf (1, 2 ,3 ,4 ,5, 6) %!error expcdf (1, 2, 3, 4, "uper") %!error ... %! expcdf (ones (3), ones (2)) %!error ... %! expcdf (2, 3, [1, 2]) %!error ... %! [p, plo, pup] = expcdf (1, 2) %!error [p, plo, pup] = ... %! expcdf (1, 2, 3, 0) %!error [p, plo, pup] = ... %! expcdf (1, 2, 3, 1.22) %!error [p, plo, pup] = ... %! expcdf (1, 2, 3, "alpha", "upper") %!error expcdf (i, 2) %!error expcdf (2, i) %!error ... %! [p, plo, pup] = expcdf (1, 2, -1, 0.04) statistics-release-1.7.3/inst/dist_fun/expinv.m000066400000000000000000000160251475240274700216310ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} expinv (@var{p}) ## @deftypefnx {statistics} {@var{x} =} expinv (@var{p}, @var{mu}) ## @deftypefnx {statistics} {[@var{x}, @var{xlo}, @var{xup}] =} expinv (@var{p}, @var{mu}, @var{pcov}) ## @deftypefnx {statistics} {[@var{x}, @var{xlo}, @var{xup}] =} expinv (@var{p}, @var{mu}, @var{pcov}, @var{alpha}) ## ## Inverse of the exponential cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the exponential distribution with mean @var{mu}. The size of @var{x} is the ## common size of @var{p} and @var{mu}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Default value is @var{mu} = 1. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## When called with three output arguments, i.e. @qcode{[@var{x}, @var{xlo}, ## @var{xup}]}, @code{expinv} computes the confidence bounds for @var{x} when ## the input parameter @var{mu} is an estimate. In such case, @var{pcov}, a ## scalar value with the variance of the estimated parameter @var{mu}, is ## necessary. Optionally, @var{alpha}, which has a default value of 0.05, ## specifies the @qcode{100 * (1 - @var{alpha})} percent confidence bounds. ## @var{xlo} and @var{xup} are arrays of the same size as @var{x} containing the ## lower and upper confidence bounds. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expcdf, exppdf, exprnd, expfit, explike, expstat} ## @end deftypefn function [varargout] = expinv (p, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 4) error ("expinv: invalid number of input arguments."); endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) mu = varargin{1}; else mu = 1; endif if (numel (varargin) > 1) pcov = varargin{2}; ## Check for variance being a scalar if (! isscalar (pcov)) error ("expinv: invalid size of variance, PCOV must be a scalar."); endif if (pcov < 0) error ("expinv: variance, PCOV, cannot be negative."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("expinv: variance, PCOV, is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 2) alpha = varargin{3}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) != 1 || alpha <= 0 || alpha >= 1) error ("expinv: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of P and MU if (! isscalar (p) || ! isscalar (mu)) [retval, p, mu] = common_size (p, mu); if (retval > 0) error ("expinv: P and MU must be of common size or scalars."); endif endif ## Check for P and MU being reals if (iscomplex (p) || iscomplex (mu)) error ("expinv: P and MU must not be complex."); endif ## Check for appropriate class if (isa (p, "single") || isa (mu, "single")); is_class = "single"; else is_class = "double"; endif ## Create output matrix if (isa (p, "single") || isa (mu, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Handle edge cases k = (p == 1) & (mu > 0); x(k) = Inf; ## Handle valid cases k = (p >= 0) & (p < 1) & (mu > 0); if (isscalar (mu)) x(k) = - mu * log (1 - p(k)); else x(k) = - mu(k) .* log (1 - p(k)); endif ## Prepare output varargout{1} = cast (x, is_class); if (nargout > 1) xlo = NaN (size (z), is_class); xup = NaN (size (z), is_class); endif ## Compute confidence bounds (if requested) if (nargout >= 2) ## Convert to log scale log_x = log (x); z = -probit (alpha / 2); halfwidth = z * sqrt (pcov ./ (mu.^2)); ## Convert to original scale xlo = exp (log_x - halfwidth); xup = exp (log_x + halfwidth); ## Prepare output varargout{2} = plo; varargout{3} = pup; endif endfunction %!demo %! ## Plot various iCDFs from the exponential distribution %! p = 0.001:0.001:0.999; %! x1 = expinv (p, 2/3); %! x2 = expinv (p, 1.0); %! x3 = expinv (p, 2.0); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r") %! grid on %! ylim ([0, 5]) %! legend ({"μ = 2/3", "μ = 1", "μ = 2"}, "location", "northwest") %! title ("Exponential iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.3934693402873666 1 2]; %!assert (expinv (p, 2*ones (1,5)), [NaN 0 1 Inf NaN], eps) %!assert (expinv (p, 2), [NaN 0 1 Inf NaN], eps) %!assert (expinv (p, 2*[1 0 NaN 1 1]), [NaN NaN NaN Inf NaN], eps) %!assert (expinv ([p(1:2) NaN p(4:5)], 2), [NaN 0 NaN Inf NaN], eps) ## Test class of input preserved %!assert (expinv ([p, NaN], 2), [NaN 0 1 Inf NaN NaN], eps) %!assert (expinv (single ([p, NaN]), 2), single ([NaN 0 1 Inf NaN NaN]), eps) %!assert (expinv ([p, NaN], single (2)), single ([NaN 0 1 Inf NaN NaN]), eps) ## Test input validation %!error expinv () %!error expinv (1, 2 ,3 ,4 ,5) %!error ... %! expinv (ones (3), ones (2)) %!error ... %! expinv (2, 3, [1, 2]) %!error ... %! [x, xlo, xup] = expinv (1, 2) %!error [x, xlo, xup] = ... %! expinv (1, 2, 3, 0) %!error [x, xlo, xup] = ... %! expinv (1, 2, 3, 1.22) %!error [x, xlo, xup] = ... %! expinv (1, 2, 3, [0.05, 0.1]) %!error expinv (i, 2) %!error expinv (2, i) %!error ... %! [x, xlo, xup] = expinv (1, 2, -1, 0.04) statistics-release-1.7.3/inst/dist_fun/exppdf.m000066400000000000000000000101241475240274700216000ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} exppdf (@var{x}) ## @deftypefnx {statistics} {@var{y} =} exppdf (@var{x}, @var{mu}) ## ## Exponential probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the exponential distribution with mean parameter @var{mu}. The size of ## @var{y} is the common size of @var{x} and @var{mu}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## Default value for @var{mu} = 1. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expcdf, expinv, exprnd, expfit, explike, expstat} ## @end deftypefn function y = exppdf (x, mu) ## Check for valid number of input arguments if (nargin < 1) error ("exppdf: function called with too few input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif ## Check for common size of X and MU if (! isscalar (x) || ! isscalar (mu)) [retval, x, mu] = common_size (x, mu); if (retval > 0) error ("exppdf: X and MU must be of common size or scalars."); endif endif ## Check for X and MU being reals if (iscomplex (x) || iscomplex (mu)) error ("exppdf: X and MU must not be complex."); endif ## Check for appropriate class if (isa (x, "single") || isa (mu, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif k = isnan (x) | !(mu > 0); y(k) = NaN; k = (x >= 0) & (x < Inf) & (mu > 0); if (isscalar (mu)) y(k) = exp (-x(k) / mu) / mu; else y(k) = exp (-x(k) ./ mu(k)) ./ mu(k); endif endfunction %!demo %! ## Plot various PDFs from the exponential distribution %! x = 0:0.01:5; %! y1 = exppdf (x, 2/3); %! y2 = exppdf (x, 1.0); %! y3 = exppdf (x, 2.0); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r") %! grid on %! ylim ([0, 1.5]) %! legend ({"μ = 2/3", "μ = 1", "μ = 2"}, "location", "northeast") %! title ("Exponential PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x,y %! x = [-1 0 0.5 1 Inf]; %! y = gampdf (x, 1, 2); %!assert (exppdf (x, 2*ones (1,5)), y) %!assert (exppdf (x, 2*[1 0 NaN 1 1]), [y(1) NaN NaN y(4:5)]) %!assert (exppdf ([x, NaN], 2), [y, NaN]) ## Test class of input preserved %!assert (exppdf (single ([x, NaN]), 2), single ([y, NaN])) %!assert (exppdf ([x, NaN], single (2)), single ([y, NaN])) ## Test input validation %!error exppdf () %!error exppdf (1,2,3) %!error ... %! exppdf (ones (3), ones (2)) %!error ... %! exppdf (ones (2), ones (3)) %!error exppdf (i, 2) %!error exppdf (2, i) statistics-release-1.7.3/inst/dist_fun/exprnd.m000066400000000000000000000134461475240274700216240ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} exprnd (@var{mu}) ## @deftypefnx {statistics} {@var{r} =} exprnd (@var{mu}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} exprnd (@var{mu}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} exprnd (@var{mu}, [@var{sz}]) ## ## Random arrays from the exponential distribution. ## ## @code{@var{r} = exprnd (@var{mu})} returns an array of random numbers chosen ## from the exponential distribution with mean parameter @var{mu}. The size of ## @var{r} is the size of @var{mu}. ## ## When called with a single size argument, @code{exprnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expcdf, expinv, exppdf, expfit, explike, expstat} ## @end deftypefn function r = exprnd (mu, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("exprnd: function called with too few input arguments."); endif ## Check for MU being real if (iscomplex (mu)) error ("exprnd: MU must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (mu); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["exprnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("exprnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("exprnd: MU must be scalar or of size SZ."); endif ## Check for class type if (isa (mu, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from exponential distribution if (isscalar (mu)) if ((mu > 0) && (mu < Inf)) r = rande (sz, cls) * mu; else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (mu > 0) & (mu < Inf); r(k) = rande (sum (k(:)), 1, cls) .* mu(k)(:); endif endfunction ## Test output %!assert (size (exprnd (2)), [1, 1]) %!assert (size (exprnd (ones (2,1))), [2, 1]) %!assert (size (exprnd (ones (2,2))), [2, 2]) %!assert (size (exprnd (1, 3)), [3, 3]) %!assert (size (exprnd (1, [4 1])), [4, 1]) %!assert (size (exprnd (1, 4, 1)), [4, 1]) %!assert (size (exprnd (1, 4, 1)), [4, 1]) %!assert (size (exprnd (1, 4, 1, 5)), [4, 1, 5]) %!assert (size (exprnd (1, 0, 1)), [0, 1]) %!assert (size (exprnd (1, 1, 0)), [1, 0]) %!assert (size (exprnd (1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (exprnd (2)), "double") %!assert (class (exprnd (single (2))), "single") %!assert (class (exprnd (single ([2 2]))), "single") ## Test input validation %!error exprnd () %!error exprnd (i) %!error ... %! exprnd (1, -1) %!error ... %! exprnd (1, 1.2) %!error ... %! exprnd (1, ones (2)) %!error ... %! exprnd (1, [2 -1 2]) %!error ... %! exprnd (1, [2 0 2.5]) %!error ... %! exprnd (ones (2), ones (2)) %!error ... %! exprnd (1, 2, -1, 5) %!error ... %! exprnd (1, 2, 1.5, 5) %!error exprnd (ones (2,2), 3) %!error exprnd (ones (2,2), [3, 2]) %!error exprnd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/fcdf.m000066400000000000000000000147671475240274700212350ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} fcdf (@var{x}, @var{df1}, @var{df2}) ## @deftypefnx {statistics} {@var{p} =} fcdf (@var{x}, @var{df1}, @var{df2}, @qcode{"upper"}) ## ## @math{F}-cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the @math{F}-distribution with @var{df1} and @var{df2} degrees of ## freedom. The size of @var{p} is the common size of @var{x}, @var{df1}, and ## @var{df2}. A scalar input functions as a constant matrix of the same size as ## the other inputs. ## ## @code{@var{p} = fcdf (@var{x}, @var{df1}, @var{df2}, "upper")} computes the ## upper tail probability of the @math{F}-distribution with @var{df1} and ## @var{df2} degrees of freedom, at the values in @var{x}. ## ## Further information about the @math{F}-distribution can be found at ## @url{https://en.wikipedia.org/wiki/F-distribution} ## ## @seealso{finv, fpdf, frnd, fstat} ## @end deftypefn function p = fcdf (x, df1, df2, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("fcdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin > 3 && strcmpi (uflag, "upper")) notnan = ! isnan (x); x(notnan) = 1 ./ max (0, x(notnan)); tmp=df1; df1=df2; df2=tmp; elseif (nargin > 3 && ! strcmpi (uflag, "upper")) error ("fcdf: invalid argument for upper tail."); endif ## Check for common size of X, DF1, and DF2 if (! isscalar (x) || ! isscalar (df1) || ! isscalar (df2)) [err, x, df1, df2] = common_size (x, df1, df2); if (err > 0) error ("fcdf: X, DF1, and DF2 must be of common size or scalars."); endif endif ## Check for X, DF1, and DF2 being reals if (iscomplex (x) || iscomplex (df1) || iscomplex (df2)) error ("fcdf: X, DF1, and DF2 must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df1, "single") || isa (df2, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Check X for NaNs while DFs <= 0 and make P = NaNs make_nan = (df1 <= 0 | df2 <= 0 | isnan(x) | isnan(df1) | isnan(df2)); p(make_nan) = NaN; ## Check remaining valid X for Inf values and make P = 1 is_inf = (x == Inf) & ! make_nan; if any (is_inf(:)) p(is_inf) = 1; make_nan = (make_nan | is_inf); endif ## Compute P when X > 0. k = find(x > 0 & ! make_nan & isfinite(df1) & isfinite(df2)); if (any (k)) k1 = (df2(k) <= x(k) .* df1(k)); if (any (k1)) kk = k(k1); xx = df2(kk) ./ (df2(kk) + x(kk) .* df1(kk)); p(kk) = betainc (xx, df2(kk)/2, df1(kk)/2, "upper"); end if (any (! k1)) kk = k(! k1); num = df1(kk) .* x(kk); xx = num ./ (num + df2(kk)); p(kk) = betainc (xx, df1(kk)/2, df2(kk)/2, "lower"); endif endif if any(~isfinite(df1(:)) | ~isfinite(df2(:))) k = find (x > 0 & ! make_nan & isfinite (df1) & ! isfinite (df2) & df2 > 0); if (any (k)) p(k) = gammainc (df1(k) .* x(k) ./ 2, df1(k) ./ 2, "lower"); end k = find (x > 0 & ! make_nan & ! isfinite (df1) & df1 > 0 & isfinite (df2)); if (any (k)) p(k) = gammainc (df2(k) ./ x(k) ./ 2, df2(k) ./ 2, "upper"); end k = find (x > 0 & ! make_nan & ! isfinite (df1) & df1 > 0 & ... ! isfinite (df2) & df2 > 0); if (any (k)) if (nargin >= 4 && x(k) == 1) p(k) = 0; else p(k) = (x(k)>=1); end endif endif endfunction %!demo %! ## Plot various CDFs from the F distribution %! x = 0.01:0.01:4; %! p1 = fcdf (x, 1, 2); %! p2 = fcdf (x, 2, 1); %! p3 = fcdf (x, 5, 2); %! p4 = fcdf (x, 10, 1); %! p5 = fcdf (x, 100, 100); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-m") %! grid on %! legend ({"df1 = 1, df2 = 2", "df1 = 2, df2 = 1", ... %! "df1 = 5, df2 = 2", "df1 = 10, df2 = 1", ... %! "df1 = 100, df2 = 100"}, "location", "southeast") %! title ("F CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1, 0, 0.5, 1, 2, Inf]; %! y = [0, 0, 1/3, 1/2, 2/3, 1]; %!assert (fcdf (x, 2*ones (1,6), 2*ones (1,6)), y, eps) %!assert (fcdf (x, 2, 2*ones (1,6)), y, eps) %!assert (fcdf (x, 2*ones (1,6), 2), y, eps) %!assert (fcdf (x, [0 NaN Inf 2 2 2], 2), [NaN NaN 0.1353352832366127 y(4:6)], eps) %!assert (fcdf (x, 2, [0 NaN Inf 2 2 2]), [NaN NaN 0.3934693402873666 y(4:6)], eps) %!assert (fcdf ([x(1:2) NaN x(4:6)], 2, 2), [y(1:2) NaN y(4:6)], eps) ## Test class of input preserved %!assert (fcdf ([x, NaN], 2, 2), [y, NaN], eps) %!assert (fcdf (single ([x, NaN]), 2, 2), single ([y, NaN]), eps ("single")) %!assert (fcdf ([x, NaN], single (2), 2), single ([y, NaN]), eps ("single")) %!assert (fcdf ([x, NaN], 2, single (2)), single ([y, NaN]), eps ("single")) ## Test input validation %!error fcdf () %!error fcdf (1) %!error fcdf (1, 2) %!error fcdf (1, 2, 3, 4) %!error fcdf (1, 2, 3, "tail") %!error ... %! fcdf (ones (3), ones (2), ones (2)) %!error ... %! fcdf (ones (2), ones (3), ones (2)) %!error ... %! fcdf (ones (2), ones (2), ones (3)) %!error fcdf (i, 2, 2) %!error fcdf (2, i, 2) %!error fcdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/finv.m000066400000000000000000000122611475240274700212600ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} finv (@var{p}, @var{df1}, @var{df2}) ## ## Inverse of the @math{F}-cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the @math{F}-distribution with @var{df1} and @var{df2} degrees of freedom. ## The size of @var{x} is the common size of @var{p}, @var{df1}, and @var{df2}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## Further information about the @math{F}-distribution can be found at ## @url{https://en.wikipedia.org/wiki/F-distribution} ## ## @seealso{fcdf, fpdf, frnd, fstat} ## @end deftypefn function x = finv (p, df1, df2) ## Check for valid number of input arguments if (nargin < 3) error ("finv: function called with too few input arguments."); endif ## Check for common size of P, DF1, and DF2 if (! isscalar (p) || ! isscalar (df1) || ! isscalar (df2)) [retval, p, df1, df2] = common_size (p, df1, df2); if (retval > 0) error ("finv: P, DF1, and DF2 must be of common size or scalars."); endif endif ## Check for P, DF1, and DF2 being reals if (iscomplex (p) || iscomplex (df1) || iscomplex (df2)) error ("finv: P, DF1, and DF2 must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (df1, "single") || isa (df2, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif k = (p == 1) & (df1 > 0) & (df1 < Inf) & (df2 > 0) & (df2 < Inf); x(k) = Inf; ## Limit df2 to 1e6 unless it is Inf k = (df2 > 1e6) & (df2 < Inf); df2(k) = 1e6; k = (p >= 0) & (p < 1) & (df1 > 0) & (df1 < Inf) & (df2 > 0) & (df2 < Inf); if (isscalar (df1) && isscalar (df2)) x(k) = ((1 ./ betainv (1 - p(k), df2/2, df1/2) - 1) * df2 / df1); else x(k) = ((1 ./ betainv (1 - p(k), df2(k)/2, df1(k)/2) - 1) .* df2(k) ./ df1(k)); endif ## Handle case when DF2 is infinite k = (p >= 0) & (p < 1) & (df1 > 0) & (df1 < Inf) & (df2 == Inf); x(k) = chi2inv (p(k), df1(k)) ./ df1(k); endfunction %!demo %! ## Plot various iCDFs from the F distribution %! p = 0.001:0.001:0.999; %! x1 = finv (p, 1, 1); %! x2 = finv (p, 2, 1); %! x3 = finv (p, 5, 2); %! x4 = finv (p, 10, 1); %! x5 = finv (p, 100, 100); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-m") %! grid on %! ylim ([0, 4]) %! legend ({"df1 = 1, df2 = 2", "df1 = 2, df2 = 1", ... %! "df1 = 5, df2 = 2", "df1 = 10, df2 = 1", ... %! "df1 = 100, df2 = 100"}, "location", "northwest") %! title ("F iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (finv (p, 2*ones (1,5), 2*ones (1,5)), [NaN 0 1 Inf NaN]) %!assert (finv (p, 2, 2*ones (1,5)), [NaN 0 1 Inf NaN]) %!assert (finv (p, 2*ones (1,5), 2), [NaN 0 1 Inf NaN]) %!assert (finv (p, [2 -Inf NaN Inf 2], 2), [NaN NaN NaN NaN NaN]) %!assert (finv (p, 2, [2 -Inf NaN Inf 2]), [NaN NaN NaN NaN NaN]) %!assert (finv ([p(1:2) NaN p(4:5)], 2, 2), [NaN 0 NaN Inf NaN]) ## Test for bug #66034 (savannah) %!assert (finv (0.025, 10, 1e6), 0.3247, 1e-4) %!assert (finv (0.025, 10, 1e7), 0.3247, 1e-4) %!assert (finv (0.025, 10, 1e10), 0.3247, 1e-4) %!assert (finv (0.025, 10, 1e255), 0.3247, 1e-4) %!assert (finv (0.025, 10, Inf), 0.3247, 1e-4) ## Test class of input preserved %!assert (finv ([p, NaN], 2, 2), [NaN 0 1 Inf NaN NaN]) %!assert (finv (single ([p, NaN]), 2, 2), single ([NaN 0 1 Inf NaN NaN])) %!assert (finv ([p, NaN], single (2), 2), single ([NaN 0 1 Inf NaN NaN])) %!assert (finv ([p, NaN], 2, single (2)), single ([NaN 0 1 Inf NaN NaN])) ## Test input validation %!error finv () %!error finv (1) %!error finv (1,2) %!error ... %! finv (ones (3), ones (2), ones (2)) %!error ... %! finv (ones (2), ones (3), ones (2)) %!error ... %! finv (ones (2), ones (2), ones (3)) %!error finv (i, 2, 2) %!error finv (2, i, 2) %!error finv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/fpdf.m000066400000000000000000000121031475240274700212300ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} fpdf (@var{x}, @var{df1}, @var{df2}) ## ## @math{F}-probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the @math{F}-distribution with @var{df1} and @var{df2} degrees of freedom. ## The size of @var{y} is the common size of @var{x}, @var{df1}, and @var{df2}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## Further information about the @math{F}-distribution can be found at ## @url{https://en.wikipedia.org/wiki/F-distribution} ## ## @seealso{fcdf, finv, frnd, fstat} ## @end deftypefn function y = fpdf (x, df1, df2) ## Check for valid number of input arguments if (nargin < 3) error ("fpdf: function called with too few input arguments."); endif ## Check for common size of X, DF1, and DF2 if (! isscalar (x) ||! isscalar (df1) || ! isscalar (df2)) [retval, x, df1, df2] = common_size (x, df1, df2); if (retval > 0) error ("fpdf: X, DF1, and DF2 must be of common size or scalars."); endif endif ## Check for X, DF1, and DF2 being reals if (iscomplex (x) || iscomplex (df1) || iscomplex (df2)) error ("fpdf: X, DF1, and DF2 must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df1, "single") || isa (df2, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif k = isnan (x) | !(df1 > 0) | !(df1 < Inf) | !(df2 > 0) | !(df2 < Inf); y(k) = NaN; k = (x > 0) & (x < Inf) & (df1 > 0) & (df1 < Inf) & (df2 > 0) & (df2 < Inf); if (isscalar (df1) && isscalar (df2)) tmp = df1 / df2 * x(k); y(k) = (exp ((df1/2 - 1) * log (tmp) ... - ((df1 + df2) / 2) * log (1 + tmp)) ... * (df1 / df2) ./ beta (df1/2, df2/2)); else tmp = df1(k) .* x(k) ./ df2(k); y(k) = (exp ((df1(k)/2 - 1) .* log (tmp) ... - ((df1(k) + df2(k)) / 2) .* log (1 + tmp)) ... .* (df1(k) ./ df2(k)) ./ beta (df1(k)/2, df2(k)/2)); endif endfunction %!demo %! ## Plot various PDFs from the F distribution %! x = 0.01:0.01:4; %! y1 = fpdf (x, 1, 1); %! y2 = fpdf (x, 2, 1); %! y3 = fpdf (x, 5, 2); %! y4 = fpdf (x, 10, 1); %! y5 = fpdf (x, 100, 100); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-m") %! grid on %! ylim ([0, 2.5]) %! legend ({"df1 = 1, df2 = 2", "df1 = 2, df2 = 1", ... %! "df1 = 5, df2 = 2", "df1 = 10, df2 = 1", ... %! "df1 = 100, df2 = 100"}, "location", "northeast") %! title ("F PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2]; %! y = [0 0 4/9 1/4 1/9]; %!assert (fpdf (x, 2*ones (1,5), 2*ones (1,5)), y, eps) %!assert (fpdf (x, 2, 2*ones (1,5)), y, eps) %!assert (fpdf (x, 2*ones (1,5), 2), y, eps) %!assert (fpdf (x, [0 NaN Inf 2 2], 2), [NaN NaN NaN y(4:5)], eps) %!assert (fpdf (x, 2, [0 NaN Inf 2 2]), [NaN NaN NaN y(4:5)], eps) %!assert (fpdf ([x, NaN], 2, 2), [y, NaN], eps) %!test #F (x, 1, df1) == T distribution (sqrt (x), df1) / sqrt (x) %! rand ("seed", 1234); # for reproducibility %! xr = rand (10,1); %! xr = xr(x > 0.1 & x < 0.9); %! yr = tpdf (sqrt (xr), 2) ./ sqrt (xr); %! assert (fpdf (xr, 1, 2), yr, 5*eps); ## Test class of input preserved %!assert (fpdf (single ([x, NaN]), 2, 2), single ([y, NaN]), eps ("single")) %!assert (fpdf ([x, NaN], single (2), 2), single ([y, NaN]), eps ("single")) %!assert (fpdf ([x, NaN], 2, single (2)), single ([y, NaN]), eps ("single")) ## Test input validation %!error fpdf () %!error fpdf (1) %!error fpdf (1,2) %!error ... %! fpdf (ones (3), ones (2), ones (2)) %!error ... %! fpdf (ones (2), ones (3), ones (2)) %!error ... %! fpdf (ones (2), ones (2), ones (3)) %!error fpdf (i, 2, 2) %!error fpdf (2, i, 2) %!error fpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/frnd.m000066400000000000000000000147101475240274700212500ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} frnd (@var{df1}, @var{df2}) ## @deftypefnx {statistics} {@var{r} =} frnd (@var{df1}, @var{df2}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} frnd (@var{df1}, @var{df2}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} frnd (@var{df1}, @var{df2}, [@var{sz}]) ## ## Random arrays from the @math{F}-distribution. ## ## @code{@var{r} = frnd (@var{df1}, @var{df2})} returns an array of random ## numbers chosen from the @math{F}-distribution with @var{df1} and @var{df2} ## degrees of freedom. The size of @var{r} is the common size of @var{df1} and ## @var{df2}. A scalar input functions as a constant matrix of the same size as ## the other inputs. ## ## When called with a single size argument, @code{frnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the @math{F}-distribution can be found at ## @url{https://en.wikipedia.org/wiki/F-distribution} ## ## @seealso{fcdf, finv, fpdf, fstat} ## @end deftypefn function r = frnd (df1, df2, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("frnd: function called with too few input arguments."); endif ## Check for common size of DF1 and DF2 if (! isscalar (df1) || ! isscalar (df2)) [retval, df1, df2] = common_size (df1, df2); if (retval > 0) error ("frnd: DF1 and DF2 must be of common size or scalars."); endif endif ## Check for DF1 and DF2 being reals if (iscomplex (df1) || iscomplex (df2)) error ("frnd: DF1 and DF2 must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (df1); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["frnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("frnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (df1) && ! isequal (size (df1), sz)) error ("frnd: DF1 and DF2 must be scalars or of size SZ."); endif ## Check for class type if (isa (df1, "single") || isa (df2, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from F distribution if (isscalar (df1) && isscalar (df2)) if ((df1 > 0) && (df1 < Inf) && (df2 > 0) && (df2 < Inf)) r = df2/df1 * randg (df1/2, sz, cls) ./ randg (df2/2, sz, cls); else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (df1 > 0) & (df1 < Inf) & (df2 > 0) & (df2 < Inf); r(k) = df2(k) ./ df1(k) .* randg (df1(k)/2, cls) ./ randg (df2(k)/2, cls); endif endfunction ## Test output %!assert (size (frnd (1, 1)), [1 1]) %!assert (size (frnd (1, ones (2,1))), [2, 1]) %!assert (size (frnd (1, ones (2,2))), [2, 2]) %!assert (size (frnd (ones (2,1), 1)), [2, 1]) %!assert (size (frnd (ones (2,2), 1)), [2, 2]) %!assert (size (frnd (1, 1, 3)), [3, 3]) %!assert (size (frnd (1, 1, [4, 1])), [4, 1]) %!assert (size (frnd (1, 1, 4, 1)), [4, 1]) %!assert (size (frnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (frnd (1, 1, 0, 1)), [0, 1]) %!assert (size (frnd (1, 1, 1, 0)), [1, 0]) %!assert (size (frnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (frnd (1, 1)), "double") %!assert (class (frnd (1, single (1))), "single") %!assert (class (frnd (1, single ([1, 1]))), "single") %!assert (class (frnd (single (1), 1)), "single") %!assert (class (frnd (single ([1, 1]), 1)), "single") ## Test input validation %!error frnd () %!error frnd (1) %!error ... %! frnd (ones (3), ones (2)) %!error ... %! frnd (ones (2), ones (3)) %!error frnd (i, 2, 3) %!error frnd (1, i, 3) %!error ... %! frnd (1, 2, -1) %!error ... %! frnd (1, 2, 1.2) %!error ... %! frnd (1, 2, ones (2)) %!error ... %! frnd (1, 2, [2 -1 2]) %!error ... %! frnd (1, 2, [2 0 2.5]) %!error ... %! frnd (1, 2, 2, -1, 5) %!error ... %! frnd (1, 2, 2, 1.5, 5) %!error ... %! frnd (2, ones (2), 3) %!error ... %! frnd (2, ones (2), [3, 2]) %!error ... %! frnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/gamcdf.m000066400000000000000000000306221475240274700215400ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} gamcdf (@var{x}, @var{a}) ## @deftypefnx {statistics} {@var{p} =} gamcdf (@var{x}, @var{a}, @var{b}) ## @deftypefnx {statistics} {@var{p} =} gamcdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} gamcdf (@var{x}, @var{a}, @var{b}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} gamcdf (@var{x}, @var{a}, @var{b}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} gamcdf (@dots{}, @qcode{"upper"}) ## ## Gamma cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Gamma distribution with shape parameter @var{a} and scale ## parameter @var{b}. When called with only one parameter, then @var{b} ## defaults to 1. The size of @var{p} is the common size of @var{x}, @var{a}, ## and @var{b}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## When called with three output arguments, i.e. @qcode{[@var{p}, @var{plo}, ## @var{pup}]}, @code{gamcdf} computes the confidence bounds for @var{p} when ## the input parameters @var{a} and @var{b} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{plo} and @var{pup} are arrays of the same size as ## @var{p} containing the lower and upper confidence bounds. ## ## @code{[@dots{}] = gamcdf (@dots{}, "upper")} computes the upper tail ## probability of the Gamma distribution with parameters @var{a} and ## @var{b}, at the values in @var{x}. ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gaminv, gampdf, gamrnd, gamfit, gamlike, gamstat} ## @end deftypefn function [varargout] = gamcdf (x, varargin) ## Check for valid number of input arguments if (nargin < 2 || nargin > 6) error ("gamcdf: invalid number of input arguments."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 2 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("gamcdf: invalid argument for upper tail."); elseif (nargin > 2 && isempty (varargin{end})) uflag = false; varargin(end) = []; else uflag = false; endif ## Get extra arguments (if they exist) or add defaults a = varargin{1}; if (numel (varargin) > 1) b = varargin{2}; else b = 1; endif if (numel (varargin) > 2) pcov = varargin{3}; ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("gamcdf: invalid size of covariance matrix."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("gamcdf: covariance matrix is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 3) alpha = varargin{4}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("gamcdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X, A, and B if (! isscalar (x) || ! isscalar (a) || ! isscalar (b)) [err, x, a, b] = common_size (x, a, b); if (err > 0) error ("gamcdf: X, A, and B must be of common size or scalars."); endif endif ## Check for X, A, and B being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b)) error ("gamcdf: X, A, and B must not be complex."); endif ## Prepare parameters so that gammainc returns NaN for out of range parameters a(a < 0) = NaN; b(b < 0) = NaN; ## Prepare data so that gammainc returns 0 for negative X x(x < 0) = 0; ## Compute gammainc z = x ./ b; if (uflag) p = gammainc (z, a, "upper"); ## Fix NaNs to gammainc output when a == NaN p(isnan (a)) = NaN; else p = gammainc (z, a); ## Fix NaNs to gammainc output when a == NaN p(isnan (a)) = NaN; endif ## Check for appropriate class if (isa (x, "single") || isa (a, "single") || isa (b, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output varargout{1} = cast (p, is_class); if (nargout > 1) plo = NaN (size (z), is_class); pup = NaN (size (z), is_class); endif ## Compute confidence bounds (if requested) if (nargout >= 2) ## Approximate the variance of p on the logit scale logitp = log (p ./ (1 - p)); dp = 1 ./ (p .* (1 - p)); dk = dgammainc (z, a) .* dp; dt = -exp (a .* log (z) - z - gammaln (a) - log (b)) .* dp; varLogitp = pcov(1,1) .* dk .^ 2 + 2 .* pcov(1,2) .* dk .* dt + ... pcov(2,2) .* dt .^ 2; if (any (varLogitp(:) < 0)) error ("gamcdf: bad covariance matrix."); endif ## Use a normal approximation on the logit scale, then transform back to ## the original CDF scale halfwidth = -norminv (alpha / 2) * sqrt (varLogitp); explogitplo = exp (logitp - halfwidth); explogitpup = exp (logitp + halfwidth); plo = explogitplo ./ (1 + explogitplo); pup = explogitpup ./ (1 + explogitpup); varargout{2} = plo; varargout{3} = pup; endif endfunction ## Compute 1st derivative of the incomplete Gamma function function dy = dgammainc (x, a) ## Initialize return variables dy = nan (size (x)); ## Use approximation for A > 2^20 ulim = 2^20; is_lim = find (a > ulim); if (! isempty (is_lim)) x(is_lim) = max (ulim - 1/3 + sqrt (ulim ./ a(is_lim)) .* ... (x(is_lim) - (a(is_lim) - 1/3)), 0); a(is_lim) = ulim; endif ## For x < a+1 is_lo = find (x < a + 1 & x != 0); if (! isempty (is_lo)) x_lo = x(is_lo); k_lo = a(is_lo); k_1 = k_lo; step = 1; d1st = 0; stsum = step; d1sum = d1st; while norm (step, "inf") >= 100 * eps (norm (stsum, "inf")) k_1 += 1; step = step .* x_lo ./ k_1; d1st = (d1st .* x_lo - step) ./ k_1; stsum = stsum + step; d1sum = d1sum + d1st; endwhile fklo = exp (-x_lo + k_lo .* log (x_lo) - gammaln (k_lo + 1)); ## Compute 1st derivative dlogfklo = (log (x_lo) - psi (k_lo + 1)); d1fklo = fklo .* dlogfklo; d1y_lo = d1fklo .* stsum + fklo .* d1sum; dy(is_lo) = d1y_lo; endif ## For x >= a+1 is_hi = find (x >= a+1); if (! isempty (is_hi)) x_hi = x(is_hi); k_hi = a(is_hi); zc = 0; k0 = 0; k1 = k_hi; x0 = 1; x1 = x_hi; d1k0 = 0; d1k1 = 1; d1x0 = 0; d1x1 = 0; kx = k_hi ./ x_hi; d1kx = 1 ./ x_hi; d2kx = 0; start = 1; while norm (d2kx - start, "Inf") > 100 * eps (norm (d2kx, "Inf")) rescale = 1 ./ x1; zc += 1; n_k = zc - k_hi; d1k0 = (d1k1 + d1k0 .* n_k - k0) .* rescale; d1x0 = (d1x1 + d1x0 .* n_k - x0) .* rescale; k0 = (k1 + k0 .* n_k) .* rescale; x0 = 1 + (x0 .* n_k) .* rescale; nrescale = zc .* rescale; d1k1 = d1k0 .* x_hi + d1k1 .* nrescale; d1x1 = d1x0 .* x_hi + d1x1 .* nrescale; k1 = k0 .* x_hi + k1 .* nrescale; x1 = x0 .* x_hi + zc; start = d2kx; kx = k1 ./ x1; d1kx = (d1k1 - kx .* d1x1) ./ x1; endwhile fkhi = exp (-x_hi + k_hi .* log (x_hi) - gammaln (k_hi+1)); ## Compute 1st derivative dlogfkhi = (log (x_hi) - psi (k_hi + 1)); d1fkhi = fkhi .* dlogfkhi; d1y_hi = d1fkhi .* kx + fkhi .* d1kx; dy(is_hi) = -d1y_hi; endif ## Handle x == 0 is_x0 = find (x == 0); if (! isempty (is_x0)) dy(is_x0) = 0; endif ## Handle a == 0 is_k0 = find (a == 0); if (! isempty (is_k0)) is_k0x0 = find (a == 0 & x == 0); dy(is_k0x0) = -Inf; endif endfunction %!demo %! ## Plot various CDFs from the Gamma distribution %! x = 0:0.01:20; %! p1 = gamcdf (x, 1, 2); %! p2 = gamcdf (x, 2, 2); %! p3 = gamcdf (x, 3, 2); %! p4 = gamcdf (x, 5, 1); %! p5 = gamcdf (x, 9, 0.5); %! p6 = gamcdf (x, 7.5, 1); %! p7 = gamcdf (x, 0.5, 1); %! plot (x, p1, "-r", x, p2, "-g", x, p3, "-y", x, p4, "-m", ... %! x, p5, "-k", x, p6, "-b", x, p7, "-c") %! grid on %! legend ({"α = 1, β = 2", "α = 2, β = 2", "α = 3, β = 2", ... %! "α = 5, β = 1", "α = 9, β = 0.5", "α = 7.5, β = 1", ... %! "α = 0.5, β = 1"}, "location", "southeast") %! title ("Gamma CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y, u %! x = [-1, 0, 0.5, 1, 2, Inf]; %! y = [0, gammainc(x(2:end), 1)]; %! u = [0, NaN, NaN, 1, 0.1353352832366127, 0]; %!assert (gamcdf (x, ones (1,6), ones (1,6)), y, eps) %!assert (gamcdf (x, ones (1,6), ones (1,6), []), y, eps) %!assert (gamcdf (x, 1, ones (1,6)), y, eps) %!assert (gamcdf (x, ones (1,6), 1), y, eps) %!assert (gamcdf (x, [0, -Inf, NaN, Inf, 1, 1], 1), [1, NaN, NaN, 0, y(5:6)], eps) %!assert (gamcdf (x, [0, -Inf, NaN, Inf, 1, 1], 1, "upper"), u, eps) %!assert (gamcdf (x, 1, [0, -Inf, NaN, Inf, 1, 1]), [NaN, NaN, NaN, 0, y(5:6)], eps) %!assert (gamcdf ([x(1:2), NaN, x(4:6)], 1, 1), [y(1:2), NaN, y(4:6)], eps) ## Test class of input preserved %!assert (gamcdf ([x, NaN], 1, 1), [y, NaN]) %!assert (gamcdf (single ([x, NaN]), 1, 1), single ([y, NaN]), eps ("single")) %!assert (gamcdf ([x, NaN], single (1), 1), single ([y, NaN]), eps ("single")) %!assert (gamcdf ([x, NaN], 1, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error gamcdf () %!error gamcdf (1) %!error gamcdf (1, 2, 3, 4, 5, 6, 7) %!error gamcdf (1, 2, 3, "uper") %!error gamcdf (1, 2, 3, 4, 5, "uper") %!error gamcdf (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = gamcdf (1, 2, 3) %!error ... %! [p, plo, pup] = gamcdf (1, 2, 3, "upper") %!error [p, plo, pup] = ... %! gamcdf (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! gamcdf (1, 2, 3, [1, 0; 0, 1], 1.22) %!error [p, plo, pup] = ... %! gamcdf (1, 2, 3, [1, 0; 0, 1], "alpha", "upper") %!error ... %! gamcdf (ones (3), ones (2), ones (2)) %!error ... %! gamcdf (ones (2), ones (3), ones (2)) %!error ... %! gamcdf (ones (2), ones (2), ones (3)) %!error gamcdf (i, 2, 2) %!error gamcdf (2, i, 2) %!error gamcdf (2, 2, i) %!error ... %! [p, plo, pup] = gamcdf (1, 2, 3, [1, 0; 0, -inf], 0.04) statistics-release-1.7.3/inst/dist_fun/gaminv.m000066400000000000000000000146211475240274700216010ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} gaminv (@var{p}, @var{a}, @var{b}) ## ## Inverse of the Gamma cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Gamma distribution with shape parameter @var{a} and scale parameter ## @var{b}. The size of @var{x} is the common size of @var{p}, @var{a}, ## and @var{b}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gamcdf, gampdf, gamrnd, gamfit, gamlike, gamstat} ## @end deftypefn function x = gaminv (p, a, b) ## Check for valid number of input arguments if (nargin < 3) error ("gaminv: function called with too few input arguments."); endif ## Check for common size of P, Α, and Β if (! isscalar (p) || ! isscalar (a) || ! isscalar (b)) [retval, p, a, b] = common_size (p, a, b); if (retval > 0) error ("gaminv: P, Α, and Β must be of common size or scalars."); endif endif ## Check for P, Α, and Β being reals if (iscomplex (p) || iscomplex (a) || iscomplex (b)) error ("gaminv: P, Α, and Β must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (a, "single") || isa (b, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif ## Force NaNs for out of range parameters is_nan = ((p < 0) | (p > 1) | isnan (p) ... | ! (a > 0) | ! (a < Inf) | ! (b > 0) | ! (b < Inf)); x(is_nan) = NaN; ## Handle edge cases is_inf = (p == 1) & (a > 0) & (a < Inf) & (b > 0) & (b < Inf); x(is_inf) = Inf; ## Handle all other valid cases is_valid = find ((p > 0) & (p < 1) & (a > 0) & ... (a < Inf) & (b > 0) & (b < Inf)); if (! isempty (is_valid)) if (! isscalar (a) || ! isscalar (b)) a = a(is_valid); b = b(is_valid); y = a .* b; else y = a * b * ones (size (is_valid)); endif p = p(is_valid); ## Call GAMMAINCINV to find a root of GAMMAINC q = gammaincinv (p, a); tol = sqrt (eps (ones (1, 1, class(q)))); check_cdf = ((abs (gammainc (q, a) - p) ./ p) > tol); ## Check for any cdf being far off from tolerance if (any (check_cdf(:))) warning ("gaminv: calculation failed to converge for some values."); endif x(is_valid) = q .* b; endif endfunction %!demo %! ## Plot various iCDFs from the Gamma distribution %! p = 0.001:0.001:0.999; %! x1 = gaminv (p, 1, 2); %! x2 = gaminv (p, 2, 2); %! x3 = gaminv (p, 3, 2); %! x4 = gaminv (p, 5, 1); %! x5 = gaminv (p, 9, 0.5); %! x6 = gaminv (p, 7.5, 1); %! x7 = gaminv (p, 0.5, 1); %! plot (p, x1, "-r", p, x2, "-g", p, x3, "-y", p, x4, "-m", ... %! p, x5, "-k", p, x6, "-b", p, x7, "-c") %! ylim ([0, 20]) %! grid on %! legend ({"α = 1, β = 2", "α = 2, β = 2", "α = 3, β = 2", ... %! "α = 5, β = 1", "α = 9, β = 0.5", "α = 7.5, β = 1", ... %! "α = 0.5, β = 1"}, "location", "northwest") %! title ("Gamma iCDF") %! xlabel ("probability") %! ylabel ("x") ## Test output %!shared p %! p = [-1 0 0.63212055882855778 1 2]; %!assert (gaminv (p, ones (1,5), ones (1,5)), [NaN 0 1 Inf NaN], eps) %!assert (gaminv (p, 1, ones (1,5)), [NaN 0 1 Inf NaN], eps) %!assert (gaminv (p, ones (1,5), 1), [NaN 0 1 Inf NaN], eps) %!assert (gaminv (p, [1 -Inf NaN Inf 1], 1), [NaN NaN NaN NaN NaN]) %!assert (gaminv (p, 1, [1 -Inf NaN Inf 1]), [NaN NaN NaN NaN NaN]) %!assert (gaminv ([p(1:2) NaN p(4:5)], 1, 1), [NaN 0 NaN Inf NaN]) %!assert (gaminv ([p(1:2) NaN p(4:5)], 1, 1), [NaN 0 NaN Inf NaN]) ## Test for accuracy when p is small. Results compared to Matlab %!assert (gaminv (1e-16, 1, 1), 1e-16, eps) %!assert (gaminv (1e-16, 1, 2), 2e-16, eps) %!assert (gaminv (1e-20, 3, 5), 1.957434012161815e-06, eps) %!assert (gaminv (1e-15, 1, 1), 1e-15, eps) %!assert (gaminv (1e-35, 1, 1), 1e-35, eps) ## Test class of input preserved %!assert (gaminv ([p, NaN], 1, 1), [NaN 0 1 Inf NaN NaN], eps) %!assert (gaminv (single ([p, NaN]), 1, 1), single ([NaN 0 1 Inf NaN NaN]), ... %! eps ("single")) %!assert (gaminv ([p, NaN], single (1), 1), single ([NaN 0 1 Inf NaN NaN]), ... %! eps ("single")) %!assert (gaminv ([p, NaN], 1, single (1)), single ([NaN 0 1 Inf NaN NaN]), ... %! eps ("single")) ## Test input validation %!error gaminv () %!error gaminv (1) %!error gaminv (1,2) %!error ... %! gaminv (ones (3), ones (2), ones (2)) %!error ... %! gaminv (ones (2), ones (3), ones (2)) %!error ... %! gaminv (ones (2), ones (2), ones (3)) %!error gaminv (i, 2, 2) %!error gaminv (2, i, 2) %!error gaminv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/gampdf.m000066400000000000000000000127301475240274700215550ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} gampdf (@var{x}, @var{a}, @var{b}) ## ## Gamma probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Gamma distribution with shape parameter @var{a} and scale parameter ## @var{b}. The size of @var{y} is the common size of @var{x}, @var{a} and ## @var{b}. A scalar input functions as a constant matrix of the same size ## as the other inputs. ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gamcdf, gaminv, gamrnd, gamfit, gamlike, gamstat} ## @end deftypefn function y = gampdf (x, a, b) ## Check for valid number of input arguments if (nargin < 3) error ("gampdf: function called with too few input arguments."); endif ## Check for common size of X, A, and B if (! isscalar (a) || ! isscalar (b)) [retval, x, a, b] = common_size (x, a, b); if (retval > 0) error ("gampdf: X, A, and B must be of common size or scalars."); endif endif ## Check for X, A, and B being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b)) error ("gampdf: X, A, and B must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Force NaNs for out of range parameters is_nan = ! (a > 0) | ! (b > 0) | isnan (x); y(is_nan) = NaN; ## Handle all other valid cases v = (x >= 0) & (a > 0) & (a <= 1) & (b > 0); if (isscalar (a) && isscalar (b)) y(v) = (x(v) .^ (a - 1)) ... .* exp (- x(v) / b) / gamma (a) / (b ^ a); else y(v) = (x(v) .^ (a(v) - 1)) ... .* exp (- x(v) ./ b(v)) ./ gamma (a(v)) ./ (b(v) .^ a(v)); endif v = (x >= 0) & (a > 1) & (b > 0); if (isscalar (a) && isscalar (b)) y(v) = exp (- a * log (b) + (a-1) * log (x(v)) - x(v) / b - gammaln (a)); else y(v) = exp (- a(v) .* log (b(v)) + (a(v)-1) .* log (x(v)) - x(v) ./ b(v) - gammaln (a(v))); endif endfunction %!demo %! ## Plot various PDFs from the Gamma distribution %! x = 0:0.01:20; %! y1 = gampdf (x, 1, 2); %! y2 = gampdf (x, 2, 2); %! y3 = gampdf (x, 3, 2); %! y4 = gampdf (x, 5, 1); %! y5 = gampdf (x, 9, 0.5); %! y6 = gampdf (x, 7.5, 1); %! y7 = gampdf (x, 0.5, 1); %! plot (x, y1, "-r", x, y2, "-g", x, y3, "-y", x, y4, "-m", ... %! x, y5, "-k", x, y6, "-b", x, y7, "-c") %! grid on %! ylim ([0,0.5]) %! legend ({"α = 1, β = 2", "α = 2, β = 2", "α = 3, β = 2", ... %! "α = 5, β = 1", "α = 9, β = 0.5", "α = 7.5, β = 1", ... %! "α = 0.5, β = 1"}, "location", "northeast") %! title ("Gamma PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 Inf]; %! y = [0 exp(-x(2:end))]; %!assert (gampdf (x, ones (1,5), ones (1,5)), y) %!assert (gampdf (x, 1, ones (1,5)), y) %!assert (gampdf (x, ones (1,5), 1), y) %!assert (gampdf (x, [0 -Inf NaN Inf 1], 1), [NaN NaN NaN NaN y(5)]) %!assert (gampdf (x, 1, [0 -Inf NaN Inf 1]), [NaN NaN NaN 0 y(5)]) %!assert (gampdf ([x, NaN], 1, 1), [y, NaN]) ## Test class of input preserved %!assert (gampdf (single ([x, NaN]), 1, 1), single ([y, NaN])) %!assert (gampdf ([x, NaN], single (1), 1), single ([y, NaN])) %!assert (gampdf ([x, NaN], 1, single (1)), single ([y, NaN])) ## Test input validation %!error gampdf () %!error gampdf (1) %!error gampdf (1,2) %!error ... %! gampdf (ones (3), ones (2), ones (2)) %!error ... %! gampdf (ones (2), ones (3), ones (2)) %!error ... %! gampdf (ones (2), ones (2), ones (3)) %!error gampdf (i, 2, 2) %!error gampdf (2, i, 2) %!error gampdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/gamrnd.m000066400000000000000000000156151475240274700215740ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} gamrnd (@var{a}, @var{b}) ## @deftypefnx {statistics} {@var{r} =} gamrnd (@var{a}, @var{b}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} gamrnd (@var{a}, @var{b}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} gamrnd (@var{a}, @var{b}, [@var{sz}]) ## ## Random arrays from the Gamma distribution. ## ## @code{@var{r} = gamrnd (@var{a}, @var{b})} returns an array of random ## numbers chosen from the Gamma distribution with shape parameter @var{a} and ## scale parameter @var{b}. The size of @var{r} is the common size of ## @var{a} and @var{b}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## When called with a single size argument, @code{gamrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gamcdf, gaminv, gampdf, gamfit, gamlike, gamstat} ## @end deftypefn function r = gamrnd (a, b, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("gamrnd: function called with too few input arguments."); endif ## Check for common size of A and B if (! isscalar (a) || ! isscalar (b)) [retval, a, b] = common_size (a, b); if (retval > 0) error ("gamrnd: A and B must be of common size or scalars."); endif endif ## Check for A and B being reals if (iscomplex (a) || iscomplex (b)) error ("gamrnd: A and B must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (a); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["gamrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("gamrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (a) && ! isequal (size (a), sz)) error ("gamrnd: A and B must be scalars or of size SZ."); endif ## Check for class type if (isa (a, "single") || isa (b, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Gamma distribution if (isscalar (a) && isscalar (b)) if ((a > 0) && (a < Inf) && (b > 0) && (b < Inf)) r = b * randg (a, sz, cls); else r = NaN (sz, cls); endif else r = NaN (sz, cls); valid = (a > 0) & (a < Inf) & (b > 0) & (b < Inf); r(valid) = b(valid) .* randg (a(valid), cls); endif endfunction ## Test output %!assert (size (gamrnd (1, 1)), [1 1]) %!assert (size (gamrnd (1, ones (2,1))), [2, 1]) %!assert (size (gamrnd (1, ones (2,2))), [2, 2]) %!assert (size (gamrnd (ones (2,1), 1)), [2, 1]) %!assert (size (gamrnd (ones (2,2), 1)), [2, 2]) %!assert (size (gamrnd (1, 1, 3)), [3, 3]) %!assert (size (gamrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (gamrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (gamrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (gamrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (gamrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (gamrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (gamrnd (1, 1)), "double") %!assert (class (gamrnd (1, single (1))), "single") %!assert (class (gamrnd (1, single ([1, 1]))), "single") %!assert (class (gamrnd (single (1), 1)), "single") %!assert (class (gamrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error gamrnd () %!error gamrnd (1) %!error ... %! gamrnd (ones (3), ones (2)) %!error ... %! gamrnd (ones (2), ones (3)) %!error gamrnd (i, 2, 3) %!error gamrnd (1, i, 3) %!error ... %! gamrnd (1, 2, -1) %!error ... %! gamrnd (1, 2, 1.2) %!error ... %! gamrnd (1, 2, ones (2)) %!error ... %! gamrnd (1, 2, [2 -1 2]) %!error ... %! gamrnd (1, 2, [2 0 2.5]) %!error ... %! gamrnd (1, 2, 2, -1, 5) %!error ... %! gamrnd (1, 2, 2, 1.5, 5) %!error ... %! gamrnd (2, ones (2), 3) %!error ... %! gamrnd (2, ones (2), [3, 2]) %!error ... %! gamrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/geocdf.m000066400000000000000000000124351475240274700215500ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} geocdf (@var{x}, @var{ps}) ## @deftypefnx {statistics} {@var{p} =} geocdf (@var{x}, @var{ps}, @qcode{"upper"}) ## ## Geometric cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the geometric distribution with probability of success parameter ## @var{ps}. The size of @var{p} is the common size of @var{x} and @var{ps}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## @code{@var{p} = geocdf (@var{x}, @var{ps}, "upper")} computes the upper tail ## probability of the geometric distribution with parameter @var{ps}, at the ## values in @var{x}. ## ## The geometric distribution models the number of failures (@var{x}) of a ## Bernoulli trial with probability @var{ps} before the first success. ## ## Further information about the geometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Geometric_distribution} ## ## @seealso{geoinv, geopdf, geornd, geofit, geostat} ## @end deftypefn function p = geocdf (x, ps, uflag) ## Check for valid number of input arguments if (nargin < 2) error ("geocdf: function called with too few input arguments."); endif ## Check for common size of X and PS if (! isscalar (x) || ! isscalar (ps)) [retval, x, ps] = common_size (x, ps); if (retval > 0) error ("geocdf: X and PS must be of common size or scalars."); endif endif ## Check for X and PS being reals if (iscomplex (x) || iscomplex (ps)) error ("geocdf: X and PS must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (ps, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Return NaN for out of range parameters k = isnan (x) | ! (ps >= 0) | ! (ps <= 1); p(k) = NaN; ## Return 1 for valid range parameters when X = Inf k = (x == Inf) & (ps >= 0) & (ps <= 1); p(k) = 1; ## Return 0 for X < 0 x(x < 0) = -1; ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("geocdf: invalid argument for upper tail."); else uflag = false; endif ## Get valid instances k = (x >= 0) & (x < Inf) & (x == fix (x)) & (ps > 0) & (ps <= 1); ## Compute CDF if (uflag) if (any (k)) p(k) = betainc (ps(k), 1, (x(k)) + 1, "upper"); endif else if (isscalar (ps)) p(k) = 1 - ((1 - ps) .^ (x(k) + 1)); else p(k) = 1 - ((1 - ps(k)) .^ (x(k) + 1)); endif endif endfunction %!demo %! ## Plot various CDFs from the geometric distribution %! x = 0:10; %! p1 = geocdf (x, 0.2); %! p2 = geocdf (x, 0.5); %! p3 = geocdf (x, 0.7); %! plot (x, p1, "*b", x, p2, "*g", x, p3, "*r") %! grid on %! xlim ([0, 10]) %! legend ({"ps = 0.2", "ps = 0.5", "ps = 0.7"}, "location", "southeast") %! title ("Geometric CDF") %! xlabel ("values in x (number of failures)") %! ylabel ("probability") ## Test output %!test %! p = geocdf ([1, 2, 3, 4], 0.25); %! assert (p(1), 0.4375000000, 1e-14); %! assert (p(2), 0.5781250000, 1e-14); %! assert (p(3), 0.6835937500, 1e-14); %! assert (p(4), 0.7626953125, 1e-14); %!test %! p = geocdf ([1, 2, 3, 4], 0.25, "upper"); %! assert (p(1), 0.5625000000, 1e-14); %! assert (p(2), 0.4218750000, 1e-14); %! assert (p(3), 0.3164062500, 1e-14); %! assert (p(4), 0.2373046875, 1e-14); %!shared x, p %! x = [-1 0 1 Inf]; %! p = [0 0.5 0.75 1]; %!assert (geocdf (x, 0.5*ones (1,4)), p) %!assert (geocdf (x, 0.5), p) %!assert (geocdf (x, 0.5*[-1 NaN 4 1]), [NaN NaN NaN p(4)]) %!assert (geocdf ([x(1:2) NaN x(4)], 0.5), [p(1:2) NaN p(4)]) ## Test class of input preserved %!assert (geocdf ([x, NaN], 0.5), [p, NaN]) %!assert (geocdf (single ([x, NaN]), 0.5), single ([p, NaN])) %!assert (geocdf ([x, NaN], single (0.5)), single ([p, NaN])) ## Test input validation %!error geocdf () %!error geocdf (1) %!error ... %! geocdf (ones (3), ones (2)) %!error ... %! geocdf (ones (2), ones (3)) %!error geocdf (i, 2) %!error geocdf (2, i) %!error geocdf (2, 3, "tail") %!error geocdf (2, 3, 5) statistics-release-1.7.3/inst/dist_fun/geoinv.m000066400000000000000000000101121475240274700215760ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} geoinv (@var{p}, @var{ps}) ## ## Inverse of the geometric cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the geometric distribution with probability of success parameter @var{ps}. ## The size of @var{x} is the common size of @var{p} and @var{ps}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## The geometric distribution models the number of failures (@var{p}) of a ## Bernoulli trial with probability @var{ps} before the first success. ## ## Further information about the geometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Geometric_distribution} ## ## @seealso{geocdf, geopdf, geornd, geofit, geostat} ## @end deftypefn function x = geoinv (p, ps) ## Check for valid number of input arguments if (nargin < 2) error ("geoinv: function called with too few input arguments."); endif ## Check for common size of P and PS if (! isscalar (ps) || ! isscalar (ps)) [retval, p, ps] = common_size (p, ps); if (retval > 0) error ("geoinv: P and PS must be of common size or scalars."); endif endif ## Check for P and PS being reals if (iscomplex (p) || iscomplex (ps)) error ("geoinv: P and PS must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (ps, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Handle edge cases k = (p == 1) & (ps >= 0) & (ps <= 1); x(k) = Inf; ## Get valid instances k = (p >= 0) & (p < 1) & (ps > 0) & (ps <= 1); ## Compute iCDF if (isscalar (ps)) x(k) = max (ceil (log (1 - p(k)) / log (1 - ps)) - 1, 0); else x(k) = max (ceil (log (1 - p(k)) ./ log (1 - ps(k))) - 1, 0); endif endfunction %!demo %! ## Plot various iCDFs from the geometric distribution %! p = 0.001:0.001:0.999; %! x1 = geoinv (p, 0.2); %! x2 = geoinv (p, 0.5); %! x3 = geoinv (p, 0.7); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r") %! grid on %! ylim ([0, 10]) %! legend ({"ps = 0.2", "ps = 0.5", "ps = 0.7"}, "location", "northwest") %! title ("Geometric iCDF") %! xlabel ("probability") %! ylabel ("values in x (number of failures)") ## Test output %!shared p %! p = [-1 0 0.75 1 2]; %!assert (geoinv (p, 0.5*ones (1,5)), [NaN 0 1 Inf NaN]) %!assert (geoinv (p, 0.5), [NaN 0 1 Inf NaN]) %!assert (geoinv (p, 0.5*[1 -1 NaN 4 1]), [NaN NaN NaN NaN NaN]) %!assert (geoinv ([p(1:2) NaN p(4:5)], 0.5), [NaN 0 NaN Inf NaN]) ## Test class of input preserved %!assert (geoinv ([p, NaN], 0.5), [NaN 0 1 Inf NaN NaN]) %!assert (geoinv (single ([p, NaN]), 0.5), single ([NaN 0 1 Inf NaN NaN])) %!assert (geoinv ([p, NaN], single (0.5)), single ([NaN 0 1 Inf NaN NaN])) ## Test input validation %!error geoinv () %!error geoinv (1) %!error ... %! geoinv (ones (3), ones (2)) %!error ... %! geoinv (ones (2), ones (3)) %!error ... %! geoinv (i, 2) %!error ... %! geoinv (2, i) statistics-release-1.7.3/inst/dist_fun/geopdf.m000066400000000000000000000072641475240274700215710ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} geopdf (@var{x}, @var{ps}) ## ## Geometric probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the geometric distribution with probability of success parameter @var{ps}. ## The size of @var{y} is the common size of @var{x} and @var{ps}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## The geometric distribution models the number of failures (@var{x}) of a ## Bernoulli trial with probability @var{ps} before the first success. ## ## Further information about the geometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Geometric_distribution} ## ## @seealso{geocdf, geoinv, geornd, geofit, geostat} ## @end deftypefn function y = geopdf (x, ps) ## Check for valid number of input arguments if (nargin < 2) error ("geopdf: function called with too few input arguments."); endif ## Check for common size of X and PS if (! isscalar (x) || ! isscalar (ps)) [retval, x, ps] = common_size (x, ps); if (retval > 0) error ("geopdf: X and PS must be of common size or scalars."); endif endif ## Check for X and PS being reals if (iscomplex (x) || iscomplex (ps)) error ("geopdf: X and PS must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (ps, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Return NaN for out of range parameters k = isnan (x) | (x == Inf) | !(ps >= 0) | !(ps <= 1); y(k) = NaN; ## Get valid instances k = (x >= 0) & (x < Inf) & (x == fix (x)) & (ps > 0) & (ps <= 1); ## Compute CDF if (isscalar (ps)) y(k) = ps * ((1 - ps) .^ x(k)); else y(k) = ps(k) .* ((1 - ps(k)) .^ x(k)); endif endfunction %!demo %! ## Plot various PDFs from the geometric distribution %! x = 0:10; %! y1 = geopdf (x, 0.2); %! y2 = geopdf (x, 0.5); %! y3 = geopdf (x, 0.7); %! plot (x, y1, "*b", x, y2, "*g", x, y3, "*r") %! grid on %! ylim ([0, 0.8]) %! legend ({"ps = 0.2", "ps = 0.5", "ps = 0.7"}, "location", "northeast") %! title ("Geometric PDF") %! xlabel ("values in x (number of failures)") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 1 Inf]; %! y = [0, 1/2, 1/4, NaN]; %!assert (geopdf (x, 0.5*ones (1,4)), y) %!assert (geopdf (x, 0.5), y) %!assert (geopdf (x, 0.5*[-1 NaN 4 1]), [NaN NaN NaN y(4)]) %!assert (geopdf ([x, NaN], 0.5), [y, NaN]) ## Test class of input preserved %!assert (geopdf (single ([x, NaN]), 0.5), single ([y, NaN]), 5*eps ("single")) %!assert (geopdf ([x, NaN], single (0.5)), single ([y, NaN]), 5*eps ("single")) ## Test input validation %!error geopdf () %!error geopdf (1) %!error geopdf (1,2,3) %!error geopdf (ones (3), ones (2)) %!error geopdf (ones (2), ones (3)) %!error geopdf (i, 2) %!error geopdf (2, i) statistics-release-1.7.3/inst/dist_fun/geornd.m000066400000000000000000000132551475240274700216000ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} geornd (@var{ps}) ## @deftypefnx {statistics} {@var{r} =} geornd (@var{ps}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} geornd (@var{ps}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} geornd (@var{ps}, [@var{sz}]) ## ## Random arrays from the geometric distribution. ## ## @code{@var{r} = geornd (@var{ps})} returns an array of random numbers chosen ## from the Birnbaum-Saunders distribution with probability of success parameter ## @var{ps}. The size of @var{r} is the size of @var{ps}. ## ## When called with a single size argument, @code{geornd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## The geometric distribution models the number of failures (@var{x}) of a ## Bernoulli trial with probability @var{ps} before the first success. ## ## Further information about the geometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Geometric_distribution} ## ## @seealso{geocdf, geoinv, geopdf, geofit, geostat} ## @end deftypefn function r = geornd (ps, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("geornd: function called with too few input arguments."); endif ## Check for PS being reals if (iscomplex (ps)) error ("geornd: PS must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (ps); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["geornd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("geornd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameter match requested dimensions in size if (! isscalar (ps) && ! isequal (size (ps), sz)) error ("geornd: PS must be scalar or of size SZ."); endif ## Check for class type if (isa (ps, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from geometric distribution if (isscalar (ps)) if (ps > 0 && ps < 1); r = floor (- rande (sz, cls) ./ log (1 - ps)); elseif (ps == 0) r = Inf (sz, cls); elseif (ps == 1) r = zeros (sz, cls); elseif (ps < 0 || ps > 1) r = NaN (sz, cls); endif else r = floor (- rande (sz, cls) ./ log (1 - ps)); k = ! (ps >= 0) | ! (ps <= 1); r(k) = NaN; k = (ps == 0); r(k) = Inf; endif endfunction ## Test output %!assert (size (geornd (0.5)), [1, 1]) %!assert (size (geornd (0.5*ones (2,1))), [2, 1]) %!assert (size (geornd (0.5*ones (2,2))), [2, 2]) %!assert (size (geornd (0.5, 3)), [3, 3]) %!assert (size (geornd (0.5, [4 1])), [4, 1]) %!assert (size (geornd (0.5, 4, 1)), [4, 1]) ## Test class of input preserved %!assert (class (geornd (0.5)), "double") %!assert (class (geornd (single (0.5))), "single") %!assert (class (geornd (single ([0.5 0.5]))), "single") %!assert (class (geornd (single (0))), "single") %!assert (class (geornd (single (1))), "single") ## Test input validation %!error geornd () %!error geornd (i) %!error ... %! geornd (1, -1) %!error ... %! geornd (1, 1.2) %!error ... %! geornd (1, ones (2)) %!error ... %! geornd (1, [2 -1 2]) %!error ... %! geornd (1, [2 0 2.5]) %!error ... %! geornd (ones (2), ones (2)) %!error ... %! geornd (1, 2, -1, 5) %!error ... %! geornd (1, 2, 1.5, 5) %!error geornd (ones (2,2), 3) %!error geornd (ones (2,2), [3, 2]) %!error geornd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/gevcdf.m000066400000000000000000000165141475240274700215610ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} gevcdf (@var{x}, @var{k}, @var{sigma}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} gevcdf (@var{x}, @var{k}, @var{sigma}, @var{mu}, @qcode{"upper"}) ## ## Generalized extreme value (GEV) cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the GEV distribution with shape parameter @var{k}, scale parameter ## @var{sigma}, and location parameter @var{mu}. The size of @var{p} is the ## common size of @var{x}, @var{k}, @var{sigma}, and @var{mu}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## @code{[@dots{}] = gevcdf (@var{x}, @var{k}, @var{sigma}, @var{mu}, "upper")} ## computes the upper tail probability of the GEV distribution with parameters ## @var{k}, @var{sigma}, and @var{mu}, at the values in @var{x}. ## ## When @qcode{@var{k} < 0}, the GEV is the type III extreme value distribution. ## When @qcode{@var{k} > 0}, the GEV distribution is the type II, or Frechet, ## extreme value distribution. If @var{W} has a Weibull distribution as ## computed by the @code{wblcdf} function, then @qcode{-@var{W}} has a type III ## extreme value distribution and @qcode{1/@var{W}} has a type II extreme value ## distribution. In the limit as @var{k} approaches @qcode{0}, the GEV is the ## mirror image of the type I extreme value distribution as computed by the ## @code{evcdf} function. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevinv, gevpdf, gevrnd, gevfit, gevlike, gevstat} ## @end deftypefn function p = gevcdf (x, k, sigma, mu, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("gevcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 4) if (! strcmpi (uflag, "upper")) error ("gevcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, K, SIGMA, and MU if (! isscalar (x) || ! isscalar (k) || ! isscalar (sigma) || ! isscalar (mu)) [err, x, k, sigma, mu] = common_size (x, k, sigma, mu); if (err > 0) error ("gevcdf: X, K, SIGMA, and MU must be of common size or scalars."); endif endif ## Check for X, K, SIGMA, and MU being reals if (iscomplex (x) || iscomplex (k) || iscomplex (sigma) || iscomplex (mu)) error ("gevcdf: X, K, SIGMA, and MU must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (k, "single") ... || isa (sigma, "single") || isa (mu, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output p = zeros (size (x), is_class); ## Return NaN for out of range parameter SIGMA. sigma(sigma <= 0) = NaN; ## Calculate z z = (x - mu) ./ sigma; ## Process k == 0 k_0 = (abs(k) < eps); if (uflag) p(k_0) = -expm1 (-exp (-z(k_0))); else p(k_0) = exp (-exp (-z(k_0))); endif ## Process k != 0 k_0 = ! k_0; t = z .* k; if (uflag) p(k_0) = -expm1 (-exp (-(1 ./ k(k_0)) .* log1p (t(k_0)))); else p(k_0) = exp (-exp (-(1 ./ k(k_0)) .* log1p (t(k_0)))); endif ## Return 0 or 1 for 1 + k.*(x-mu)/sigma > 0 k_1 = k_0 & (t<=-1); t(k_1) = 0; if uflag == true p(k_1) = (k(k_1) >= 0); else p(k_1) = (k(k_1) < 0); endif endfunction %!demo %! ## Plot various CDFs from the generalized extreme value distribution %! x = -1:0.001:10; %! p1 = gevcdf (x, 1, 1, 1); %! p2 = gevcdf (x, 0.5, 1, 1); %! p3 = gevcdf (x, 1, 1, 5); %! p4 = gevcdf (x, 1, 2, 5); %! p5 = gevcdf (x, 1, 5, 5); %! p6 = gevcdf (x, 1, 0.5, 5); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", ... %! x, p4, "-c", x, p5, "-m", x, p6, "-k") %! grid on %! xlim ([-1, 10]) %! legend ({"k = 1, σ = 1, μ = 1", "k = 0.5, σ = 1, μ = 1", ... %! "k = 1, σ = 1, μ = 5", "k = 1, σ = 2, μ = 5", ... %! "k = 1, σ = 5, μ = 5", "k = 1, σ = 0.5, μ = 5"}, ... %! "location", "southeast") %! title ("Generalized extreme value CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!test %! x = 0:0.5:2.5; %! sigma = 1:6; %! k = 1; %! mu = 0; %! p = gevcdf (x, k, sigma, mu); %! expected_p = [0.36788, 0.44933, 0.47237, 0.48323, 0.48954, 0.49367]; %! assert (p, expected_p, 0.001); %!test %! x = -0.5:0.5:2.5; %! sigma = 0.5; %! k = 1; %! mu = 0; %! p = gevcdf (x, k, sigma, mu); %! expected_p = [0, 0.36788, 0.60653, 0.71653, 0.77880, 0.81873, 0.84648]; %! assert (p, expected_p, 0.001); %!test # check for continuity for k near 0 %! x = 1; %! sigma = 0.5; %! k = -0.03:0.01:0.03; %! mu = 0; %! p = gevcdf (x, k, sigma, mu); %! expected_p = [0.88062, 0.87820, 0.87580, 0.87342, 0.87107, 0.86874, 0.86643]; %! assert (p, expected_p, 0.001); ## Test input validation %!error gevcdf () %!error gevcdf (1) %!error gevcdf (1, 2) %!error gevcdf (1, 2, 3) %!error ... %! gevcdf (1, 2, 3, 4, 5, 6) %!error gevcdf (1, 2, 3, 4, "tail") %!error gevcdf (1, 2, 3, 4, 5) %!error ... %! gevcdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! gevcdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! gevcdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! gevcdf (ones (2), ones (2), ones(2), ones(3)) %!error gevcdf (i, 2, 3, 4) %!error gevcdf (1, i, 3, 4) %!error gevcdf (1, 2, i, 4) %!error gevcdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/gevinv.m000066400000000000000000000135771475240274700216270ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} gevinv (@var{p}, @var{k}, @var{sigma}, @var{mu}) ## ## Inverse of the generalized extreme value (GEV) cumulative distribution ## function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the GEV distribution with shape parameter @var{k}, scale parameter ## @var{sigma}, and location parameter @var{mu}. The size of @var{p} is the ## common size of @var{x}, @var{k}, @var{sigma}, and @var{mu}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## When @qcode{@var{k} < 0}, the GEV is the type III extreme value distribution. ## When @qcode{@var{k} > 0}, the GEV distribution is the type II, or Frechet, ## extreme value distribution. If @var{W} has a Weibull distribution as ## computed by the @code{wblcdf} function, then @qcode{-@var{W}} has a type III ## extreme value distribution and @qcode{1/@var{W}} has a type II extreme value ## distribution. In the limit as @var{k} approaches @qcode{0}, the GEV is the ## mirror image of the type I extreme value distribution as computed by the ## @code{evcdf} function. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevcdf, gevpdf, gevrnd, gevfit, gevlike, gevstat} ## @end deftypefn function x = gevinv (p, k, sigma, mu) ## Check for valid number of input arguments if (nargin < 4) error ("gevinv: function called with too few input arguments."); endif ## Check for common size of P, K, SIGMA, and MU [retval, p, k, sigma, mu] = common_size (p, k, sigma, mu); if (retval > 0) error ("gevinv: P, K, SIGMA, and MU must be of common size or scalars."); endif ## Check for P, K, SIGMA, and MU being reals if (iscomplex (p) || iscomplex (k) || iscomplex (sigma) || iscomplex (mu)) error ("gevinv: P, K, SIGMA, and MU must not be complex."); endif is_neginf = p == 0; is_posinf = p == 1; is_nan = p < 0 | p > 1 | isnan (p); x = p; llP = log (-log (p)); kllP = k .* llP; ## Use the Taylor series expansion of the exponential to ## avoid roundoff error or dividing by zero when k is small ii = (abs(kllP) < 1E-4); x(ii) = mu(ii) - sigma(ii) .* llP(ii) .* (1 - kllP(ii) .* (1 - kllP(ii))); x(~ii) = mu(~ii) + (sigma(~ii) ./ k(~ii)) .* (exp(-kllP(~ii)) - 1); x(is_neginf) = -Inf; x(is_posinf) = Inf; x(is_nan) = NaN; endfunction %!demo %! ## Plot various iCDFs from the generalized extreme value distribution %! p = 0.001:0.001:0.999; %! x1 = gevinv (p, 1, 1, 1); %! x2 = gevinv (p, 0.5, 1, 1); %! x3 = gevinv (p, 1, 1, 5); %! x4 = gevinv (p, 1, 2, 5); %! x5 = gevinv (p, 1, 5, 5); %! x6 = gevinv (p, 1, 0.5, 5); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", ... %! p, x4, "-c", p, x5, "-m", p, x6, "-k") %! grid on %! ylim ([-1, 10]) %! legend ({"k = 1, σ = 1, μ = 1", "k = 0.5, σ = 1, μ = 1", ... %! "k = 1, σ = 1, μ = 5", "k = 1, σ = 2, μ = 5", ... %! "k = 1, σ = 5, μ = 5", "k = 1, σ = 0.5, μ = 5"}, ... %! "location", "northwest") %! title ("Generalized extreme value iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!test %! p = 0.1:0.1:0.9; %! k = 0; %! sigma = 1; %! mu = 0; %! x = gevinv (p, k, sigma, mu); %! c = gevcdf(x, k, sigma, mu); %! assert (c, p, 0.001); %!test %! p = 0.1:0.1:0.9; %! k = 1; %! sigma = 1; %! mu = 0; %! x = gevinv (p, k, sigma, mu); %! c = gevcdf(x, k, sigma, mu); %! assert (c, p, 0.001); %!test %! p = 0.1:0.1:0.9; %! k = 0.3; %! sigma = 1; %! mu = 0; %! x = gevinv (p, k, sigma, mu); %! c = gevcdf(x, k, sigma, mu); %! assert (c, p, 0.001); ## Test input validation %!error gevinv () %!error gevinv (1) %!error gevinv (1, 2) %!error gevinv (1, 2, 3) %!error ... %! gevinv (ones (3), ones (2), ones(2), ones(2)) %!error ... %! gevinv (ones (2), ones (3), ones(2), ones(2)) %!error ... %! gevinv (ones (2), ones (2), ones(3), ones(2)) %!error ... %! gevinv (ones (2), ones (2), ones(2), ones(3)) %!error gevinv (i, 2, 3, 4) %!error gevinv (1, i, 3, 4) %!error gevinv (1, 2, i, 4) %!error gevinv (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/gevpdf.m000066400000000000000000000137301475240274700215730ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} gevpdf (@var{x}, @var{k}, @var{sigma}, @var{mu}) ## ## Generalized extreme value (GEV) probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the GEV distribution with shape parameter @var{k}, scale parameter ## @var{sigma}, and location parameter @var{mu}. The size of @var{y} is the ## common size of @var{x}, @var{k}, @var{sigma}, and @var{mu}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## When @qcode{@var{k} < 0}, the GEV is the type III extreme value distribution. ## When @qcode{@var{k} > 0}, the GEV distribution is the type II, or Frechet, ## extreme value distribution. If @var{W} has a Weibull distribution as ## computed by the @code{wblcdf} function, then @qcode{-@var{W}} has a type III ## extreme value distribution and @qcode{1/@var{W}} has a type II extreme value ## distribution. In the limit as @var{k} approaches @qcode{0}, the GEV is the ## mirror image of the type I extreme value distribution as computed by the ## @code{evcdf} function. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevcdf, gevinv, gevrnd, gevfit, gevlike, gevstat} ## @end deftypefn function y = gevpdf (x, k, sigma, mu) ## Check for valid number of input arguments if (nargin < 4) error ("gevpdf: function called with too few input arguments."); endif ## Check for common size of X, K, SIGMA, and MU [retval, x, k, sigma, mu] = common_size (x, k, sigma, mu); if (retval > 0) error ("gevpdf: X, K, SIGMA, and MU must be of common size or scalars."); endif ## Check for X, K, SIGMA, and MU being reals if (iscomplex (x) || iscomplex (k) || iscomplex (sigma) || iscomplex (mu)) error ("gevpdf: X, K, SIGMA, and MU must not be complex."); endif z = 1 + k .* (x - mu) ./ sigma; ## Calculate generalized extreme value PDF y = exp(-(z .^ (-1 ./ k))) .* (z .^ (-1 - 1 ./ k)) ./ sigma; y(z <= 0) = 0; ## Use a different formula if k is very close to zero inds = (abs (k) < (eps^0.7)); if (any (inds)) z = (mu(inds) - x(inds)) ./ sigma(inds); y(inds) = exp (z - exp (z)) ./ sigma(inds); endif endfunction %!demo %! ## Plot various PDFs from the generalized extreme value distribution %! x = -1:0.001:10; %! y1 = gevpdf (x, 1, 1, 1); %! y2 = gevpdf (x, 0.5, 1, 1); %! y3 = gevpdf (x, 1, 1, 5); %! y4 = gevpdf (x, 1, 2, 5); %! y5 = gevpdf (x, 1, 5, 5); %! y6 = gevpdf (x, 1, 0.5, 5); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", ... %! x, y4, "-c", x, y5, "-m", x, y6, "-k") %! grid on %! xlim ([-1, 10]) %! ylim ([0, 1.1]) %! legend ({"k = 1, σ = 1, μ = 1", "k = 0.5, σ = 1, μ = 1", ... %! "k = 1, σ = 1, μ = 5", "k = 1, σ = 2, μ = 5", ... %! "k = 1, σ = 5, μ = 5", "k = 1, σ = 0.5, μ = 5"}, ... %! "location", "northeast") %! title ("Generalized extreme value PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!test %! x = 0:0.5:2.5; %! sigma = 1:6; %! k = 1; %! mu = 0; %! y = gevpdf (x, k, sigma, mu); %! expected_y = [0.367879 0.143785 0.088569 0.063898 0.049953 0.040997]; %! assert (y, expected_y, 0.001); %!test %! x = -0.5:0.5:2.5; %! sigma = 0.5; %! k = 1; %! mu = 0; %! y = gevpdf (x, k, sigma, mu); %! expected_y = [0 0.735759 0.303265 0.159229 0.097350 0.065498 0.047027]; %! assert (y, expected_y, 0.001); %!test # check for continuity for k near 0 %! x = 1; %! sigma = 0.5; %! k = -0.03:0.01:0.03; %! mu = 0; %! y = gevpdf (x, k, sigma, mu); %! expected_y = [0.23820 0.23764 0.23704 0.23641 0.23576 0.23508 0.23438]; %! assert (y, expected_y, 0.001); ## Test input validation %!error gevpdf () %!error gevpdf (1) %!error gevpdf (1, 2) %!error gevpdf (1, 2, 3) %!error ... %! gevpdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! gevpdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! gevpdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! gevpdf (ones (2), ones (2), ones(2), ones(3)) %!error gevpdf (i, 2, 3, 4) %!error gevpdf (1, i, 3, 4) %!error gevpdf (1, 2, i, 4) %!error gevpdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/gevrnd.m000066400000000000000000000172071475240274700216100ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} gevrnd (@var{k}, @var{sigma}, @var{mu}) ## @deftypefnx {statistics} {@var{r} =} gevrnd (@var{k}, @var{sigma}, @var{mu}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} gevrnd (@var{k}, @var{sigma}, @var{mu}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} gevrnd (@var{k}, @var{sigma}, @var{mu}, [@var{sz}]) ## ## Random arrays from the generalized extreme value (GEV) distribution. ## ## @code{@var{r} = gevrnd (@var{k}, @var{sigma}, @var{mu}} returns an array of ## random numbers chosen from the GEV distribution with shape parameter @var{k}, ## scale parameter @var{sigma}, and location parameter @var{mu}. The size of ## @var{r} is the common size of @var{k}, @var{sigma}, and @var{mu}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{gevrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## When @qcode{@var{k} < 0}, the GEV is the type III extreme value distribution. ## When @qcode{@var{k} > 0}, the GEV distribution is the type II, or Frechet, ## extreme value distribution. If @var{W} has a Weibull distribution as ## computed by the @code{wblcdf} function, then @qcode{-@var{W}} has a type III ## extreme value distribution and @qcode{1/@var{W}} has a type II extreme value ## distribution. In the limit as @var{k} approaches @qcode{0}, the GEV is the ## mirror image of the type I extreme value distribution as computed by the ## @code{evcdf} function. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevcdf, gevinv, gevpdf, gevfit, gevlike, gevstat} ## @end deftypefn function r = gevrnd (k, sigma, mu, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("gevrnd: function called with too few input arguments."); endif ## Check for common size of K, SIGMA, and MU if (! isscalar (k) || ! isscalar (sigma) || ! isscalar (mu)) [retval, k, sigma, mu] = common_size (k, sigma, mu); if (retval > 0) error ("gevrnd: K, SIGMA, and MU must be of common size or scalars."); endif endif ## Check for K, SIGMA, and MU being reals if (iscomplex (k) || iscomplex (sigma) || iscomplex (mu)) error ("gevrnd: K, SIGMA, and MU must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (k); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["gevrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("gevrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (!isscalar (k) && ! isequal (size (k), sz)) error ("gevrnd: K, SIGMA, and MU must be scalars or of size SZ."); endif ## Check for class type if (isa (k, "single") || isa (sigma, "single") || isa (mu, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Burr type XII distribution r = gevinv (rand(sz), k, sigma, mu); r = cast (r, cls); endfunction ## Test output %!assert(size (gevrnd (1,2,1)), [1, 1]); %!assert(size (gevrnd (ones(2,1), 2, 1)), [2, 1]); %!assert(size (gevrnd (ones(2,2), 2, 1)), [2, 2]); %!assert(size (gevrnd (1, 2*ones(2,1), 1)), [2, 1]); %!assert(size (gevrnd (1, 2*ones(2,2), 1)), [2, 2]); %!assert(size (gevrnd (1, 2, 1, 3)), [3, 3]); %!assert(size (gevrnd (1, 2, 1, [4 1])), [4, 1]); %!assert(size (gevrnd (1, 2, 1, 4, 1)), [4, 1]); ## Test class of input preserved %!assert (class (gevrnd (1,1,1)), "double") %!assert (class (gevrnd (single (1),1,1)), "single") %!assert (class (gevrnd (single ([1 1]),1,1)), "single") %!assert (class (gevrnd (1,single (1),1)), "single") %!assert (class (gevrnd (1,single ([1 1]),1)), "single") %!assert (class (gevrnd (1,1,single (1))), "single") %!assert (class (gevrnd (1,1,single ([1 1]))), "single") ## Test input validation %!error gevrnd () %!error gevrnd (1) %!error gevrnd (1, 2) %!error ... %! gevrnd (ones (3), ones (2), ones (2)) %!error ... %! gevrnd (ones (2), ones (3), ones (2)) %!error ... %! gevrnd (ones (2), ones (2), ones (3)) %!error gevrnd (i, 2, 3) %!error gevrnd (1, i, 3) %!error gevrnd (1, 2, i) %!error ... %! gevrnd (1, 2, 3, -1) %!error ... %! gevrnd (1, 2, 3, 1.2) %!error ... %! gevrnd (1, 2, 3, ones (2)) %!error ... %! gevrnd (1, 2, 3, [2 -1 2]) %!error ... %! gevrnd (1, 2, 3, [2 0 2.5]) %!error ... %! gevrnd (1, 2, 3, 2, -1, 5) %!error ... %! gevrnd (1, 2, 3, 2, 1.5, 5) %!error ... %! gevrnd (2, ones (2), 2, 3) %!error ... %! gevrnd (2, ones (2), 2, [3, 2]) %!error ... %! gevrnd (2, ones (2), 2, 3, 2) statistics-release-1.7.3/inst/dist_fun/gpcdf.m000066400000000000000000000266311475240274700214070ustar00rootroot00000000000000## Copyright (C) 1997-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} gpcdf (@var{x}, @var{k}, @var{sigma}, @var{theta}) ## @deftypefnx {statistics} {@var{p} =} gpcdf (@var{x}, @var{k}, @var{sigma}, @var{theta}, @qcode{"upper"}) ## ## Generalized Pareto cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the generalized Pareto distribution with shape parameter @var{k}, ## scale parameter @var{sigma}, and location parameter @var{theta}. The size of ## @var{p} is the common size of @var{x}, @var{k}, @var{sigma}, and @var{theta}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## @code{[@dots{}] = gpcdf(@var{x}, @var{k}, @var{sigma}, @var{theta}, "upper")} ## computes the upper tail probability of the generalized Pareto distribution ## with parameters @var{k}, @var{sigma}, and @var{theta}, at the values in ## @var{x}. ## ## When @qcode{@var{k} = 0} and @qcode{@var{theta} = 0}, the Generalized Pareto ## is equivalent to the exponential distribution. When @qcode{@var{k} > 0} and ## @code{@var{theta} = @var{k} / @var{k}} the Generalized Pareto is equivalent ## τπ the Pareto distribution. The mean of the Generalized Pareto is not finite ## when @qcode{@var{k} >= 1} and the variance is not finite when ## @qcode{@var{k} >= 1/2}. When @qcode{@var{k} >= 0}, the Generalized Pareto ## has positive density for @qcode{@var{x} > @var{theta}}, or, when ## @qcode{@var{theta} < 0}, for ## @qcode{0 <= (@var{x} - @var{theta}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpinv, gppdf, gprnd, gpfit, gplike, gpstat} ## @end deftypefn function p = gpcdf (x, k, sigma, theta, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("gpcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 4) if (! strcmpi (uflag, "upper")) error ("gpcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, K, SIGMA, and THETA if (! isscalar (x) || ! isscalar (k) || ! isscalar (sigma) || ! isscalar (theta)) [err, x, k, sigma, theta] = common_size (x, k, sigma, theta); if (err > 0) error ("gpcdf: X, K, SIGMA, and THETA must be of common size or scalars."); endif endif ## Check for X, K, SIGMA, and THETA being reals if (iscomplex (x) || iscomplex (k) || iscomplex (sigma) || iscomplex (theta)) error ("gpcdf: X, K, SIGMA, and THETA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (k, "single") ... || isa (sigma, "single") || isa (theta, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output p = zeros (size (x), is_class); ## Return NaNs for out of range values of sigma parameter sigma(sigma <= 0) = NaN; ## Calculate (x-theta)/sigma => 0 and force zero below that z = (x - theta) ./ sigma; z(z < 0) = 0; ## Compute cases for SHAPE == 0 kz = (abs (k) < eps (is_class)); if (uflag) p(kz) = exp (-z(kz)); else p(kz) = -expm1 (-z(kz)); endif ## For SHAPE < 0, calculate 0 <= x/sigma <= -1/k and force zero below that t = z .* k; kt = (t <= -1 & k < -eps (is_class)); t(kt) = 0; ## Compute cases for SHAPE != 0 kz = ! kz; if (uflag) p(kz) = exp ((-1 ./ k(kz)) .* log1p (t(kz))); else p(kz) = -expm1 ((-1 ./ k(kz)) .* log1p (t(kz))); endif if (uflag) p(kt) = 0; else p(kt) = 1; endif ## For SHAPE == NaN force p = NaN p(isnan (k)) = NaN; endfunction %!demo %! ## Plot various CDFs from the generalized Pareto distribution %! x = 0:0.001:5; %! p1 = gpcdf (x, 1, 1, 0); %! p2 = gpcdf (x, 5, 1, 0); %! p3 = gpcdf (x, 20, 1, 0); %! p4 = gpcdf (x, 1, 2, 0); %! p5 = gpcdf (x, 5, 2, 0); %! p6 = gpcdf (x, 20, 2, 0); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", ... %! x, p4, "-c", x, p5, "-m", x, p6, "-k") %! grid on %! xlim ([0, 5]) %! legend ({"k = 1, σ = 1, θ = 0", "k = 5, σ = 1, θ = 0", ... %! "k = 20, σ = 1, θ = 0", "k = 1, σ = 2, θ = 0", ... %! "k = 5, σ = 2, θ = 0", "k = 20, σ = 2, θ = 0"}, ... %! "location", "northwest") %! title ("Generalized Pareto CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y1, y1u, y2, y2u, y3, y3u %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! y1 = [0, 0, 0, 0.3934693402873666, 0.6321205588285577, 1]; %! y1u = [1, 1, 1, 0.6065306597126334, 0.3678794411714423, 0]; %! y2 = [0, 0, 0, 1/3, 1/2, 1]; %! y2u = [1, 1, 1, 2/3, 1/2, 0]; %! y3 = [0, 0, 0, 1/2, 1, 1]; %! y3u = [1, 1, 1, 1/2, 0, 0]; %!assert (gpcdf (x, zeros (1,6), ones (1,6), zeros (1,6)), y1, eps) %!assert (gpcdf (x, 0, 1, zeros (1,6)), y1, eps) %!assert (gpcdf (x, 0, ones (1,6), 0), y1, eps) %!assert (gpcdf (x, zeros (1,6), 1, 0), y1, eps) %!assert (gpcdf (x, 0, 1, 0), y1, eps) %!assert (gpcdf (x, 0, 1, [0, 0, 0, NaN, 0, 0]), [y1(1:3), NaN, y1(5:6)], eps) %!assert (gpcdf (x, 0, [1, 1, 1, NaN, 1, 1], 0), [y1(1:3), NaN, y1(5:6)], eps) %!assert (gpcdf (x, [0, 0, 0, NaN, 0, 0], 1, 0), [y1(1:3), NaN, y1(5:6)], eps) %!assert (gpcdf ([x(1:3), NaN, x(5:6)], 0, 1, 0), [y1(1:3), NaN, y1(5:6)], eps) %!assert (gpcdf (x, zeros (1,6), ones (1,6), zeros (1,6), "upper"), y1u, eps) %!assert (gpcdf (x, 0, 1, zeros (1,6), "upper"), y1u, eps) %!assert (gpcdf (x, 0, ones (1,6), 0, "upper"), y1u, eps) %!assert (gpcdf (x, zeros (1,6), 1, 0, "upper"), y1u, eps) %!assert (gpcdf (x, 0, 1, 0, "upper"), y1u, eps) %!assert (gpcdf (x, ones (1,6), ones (1,6), zeros (1,6)), y2, eps) %!assert (gpcdf (x, 1, 1, zeros (1,6)), y2, eps) %!assert (gpcdf (x, 1, ones (1,6), 0), y2, eps) %!assert (gpcdf (x, ones (1,6), 1, 0), y2, eps) %!assert (gpcdf (x, 1, 1, 0), y2, eps) %!assert (gpcdf (x, 1, 1, [0, 0, 0, NaN, 0, 0]), [y2(1:3), NaN, y2(5:6)], eps) %!assert (gpcdf (x, 1, [1, 1, 1, NaN, 1, 1], 0), [y2(1:3), NaN, y2(5:6)], eps) %!assert (gpcdf (x, [1, 1, 1, NaN, 1, 1], 1, 0), [y2(1:3), NaN, y2(5:6)], eps) %!assert (gpcdf ([x(1:3), NaN, x(5:6)], 1, 1, 0), [y2(1:3), NaN, y2(5:6)], eps) %!assert (gpcdf (x, ones (1,6), ones (1,6), zeros (1,6), "upper"), y2u, eps) %!assert (gpcdf (x, 1, 1, zeros (1,6), "upper"), y2u, eps) %!assert (gpcdf (x, 1, ones (1,6), 0, "upper"), y2u, eps) %!assert (gpcdf (x, ones (1,6), 1, 0, "upper"), y2u, eps) %!assert (gpcdf (x, 1, 1, 0, "upper"), y2u, eps) %!assert (gpcdf (x, 1, 1, [0, 0, 0, NaN, 0, 0], "upper"), ... %! [y2u(1:3), NaN, y2u(5:6)], eps) %!assert (gpcdf (x, 1, [1, 1, 1, NaN, 1, 1], 0, "upper"), ... %! [y2u(1:3), NaN, y2u(5:6)], eps) %!assert (gpcdf (x, [1, 1, 1, NaN, 1, 1], 1, 0, "upper"), ... %! [y2u(1:3), NaN, y2u(5:6)], eps) %!assert (gpcdf ([x(1:3), NaN, x(5:6)], 1, 1, 0, "upper"), ... %! [y2u(1:3), NaN, y2u(5:6)], eps) %!assert (gpcdf (x, -ones (1,6), ones (1,6), zeros (1,6)), y3, eps) %!assert (gpcdf (x, -1, 1, zeros (1,6)), y3, eps) %!assert (gpcdf (x, -1, ones (1,6), 0), y3, eps) %!assert (gpcdf (x, -ones (1,6), 1, 0), y3, eps) %!assert (gpcdf (x, -1, 1, 0), y3, eps) %!assert (gpcdf (x, -1, 1, [0, 0, 0, NaN, 0, 0]), [y3(1:3), NaN, y3(5:6)], eps) %!assert (gpcdf (x, -1, [1, 1, 1, NaN, 1, 1], 0), [y3(1:3), NaN, y3(5:6)], eps) %!assert (gpcdf (x, [-1, -1, -1, NaN, -1, -1], 1, 0), [y3(1:3), NaN, y3(5:6)], eps) %!assert (gpcdf ([x(1:3), NaN, x(5:6)], -1, 1, 0), [y3(1:3), NaN, y3(5:6)], eps) %!assert (gpcdf (x, -ones (1,6), ones (1,6), zeros (1,6), "upper"), y3u, eps) %!assert (gpcdf (x, -1, 1, zeros (1,6), "upper"), y3u, eps) %!assert (gpcdf (x, -1, ones (1,6), 0, "upper"), y3u, eps) %!assert (gpcdf (x, -ones (1,6), 1, 0, "upper"), y3u, eps) %!assert (gpcdf (x, -1, 1, 0, "upper"), y3u, eps) %!assert (gpcdf (x, -1, 1, [0, 0, 0, NaN, 0, 0], "upper"), ... %! [y3u(1:3), NaN, y3u(5:6)], eps) %!assert (gpcdf (x, -1, [1, 1, 1, NaN, 1, 1], 0, "upper"), ... %! [y3u(1:3), NaN, y3u(5:6)], eps) %!assert (gpcdf (x, [-1, -1, -1, NaN, -1, -1], 1, 0, "upper"), ... %! [y3u(1:3), NaN, y3u(5:6)], eps) %!assert (gpcdf ([x(1:3), NaN, x(5:6)], -1, 1, 0, "upper"), ... %! [y3u(1:3), NaN, y3u(5:6)], eps) ## Test class of input preserved %!assert (gpcdf (single ([x, NaN]), 0, 1, 0), single ([y1, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], 0, 1, single (0)), single ([y1, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], 0, single (1), 0), single ([y1, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], single (0), 1, 0), single ([y1, NaN]), eps("single")) %!assert (gpcdf (single ([x, NaN]), 1, 1, 0), single ([y2, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], 1, 1, single (0)), single ([y2, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], 1, single (1), 0), single ([y2, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], single (1), 1, 0), single ([y2, NaN]), eps("single")) %!assert (gpcdf (single ([x, NaN]), -1, 1, 0), single ([y3, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], -1, 1, single (0)), single ([y3, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], -1, single (1), 0), single ([y3, NaN]), eps("single")) %!assert (gpcdf ([x, NaN], single (-1), 1, 0), single ([y3, NaN]), eps("single")) ## Test input validation %!error gpcdf () %!error gpcdf (1) %!error gpcdf (1, 2) %!error gpcdf (1, 2, 3) %!error gpcdf (1, 2, 3, 4, "tail") %!error gpcdf (1, 2, 3, 4, 5) %!error ... %! gpcdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! gpcdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! gpcdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! gpcdf (ones (2), ones (2), ones(2), ones(3)) %!error gpcdf (i, 2, 3, 4) %!error gpcdf (1, i, 3, 4) %!error gpcdf (1, 2, i, 4) %!error gpcdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/gpinv.m000066400000000000000000000211471475240274700214440ustar00rootroot00000000000000## Copyright (C) 1997-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} gpinv (@var{p}, @var{k}, @var{sigma}, @var{theta}) ## ## Inverse of the generalized Pareto cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the generalized Pareto distribution with shape parameter @var{k}, scale ## parameter @var{sigma}, and location parameter @var{theta}. The size of ## @var{x} is the common size of @var{p}, @var{k}, @var{sigma}, and @var{theta}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## When @qcode{@var{k} = 0} and @qcode{@var{theta} = 0}, the Generalized Pareto ## is equivalent to the exponential distribution. When @qcode{@var{k} > 0} and ## @code{@var{theta} = @var{k} / @var{k}} the Generalized Pareto is equivalent ## to the Pareto distribution. The mean of the Generalized Pareto is not finite ## when @qcode{@var{k} >= 1} and the variance is not finite when ## @qcode{@var{k} >= 1/2}. When @qcode{@var{k} >= 0}, the Generalized Pareto ## has positive density for @qcode{@var{x} > @var{theta}}, or, when ## @qcode{@var{theta} < 0}, for ## @qcode{0 <= (@var{x} - @var{theta}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpcdf, gppdf, gprnd, gpfit, gplike, gpstat} ## @end deftypefn function x = gpinv (p, k, sigma, theta) ## Check for valid number of input arguments if (nargin < 4) error ("gpinv: function called with too few input arguments."); endif ## Check for common size of P, K, SIGMA, and THETA [retval, p, k, sigma, theta] = common_size (p, k, sigma, theta); if (retval > 0) error ("gpinv: P, K, SIGMA, and THETA must be of common size or scalars."); endif ## Check for P, K, SIGMA, and THETA being reals if (iscomplex (p) || iscomplex (k) || iscomplex (sigma) || iscomplex (theta)) error ("gpinv: P, K, SIGMA, and THETA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (theta, "single") ... || isa (sigma, "single") || isa (k, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif ## Return NaNs for out of range values of sigma parameter kx = isnan (p) | ! (0 <= p) | ! (p <= 1) ... | ! (-Inf < theta) | ! (theta < Inf) ... | ! (sigma > 0) | ! (sigma < Inf) ... | ! (-Inf < k) | ! (k < Inf); x(kx) = NaN; kx = (0 <= p) & (p <= 1) & (-Inf < theta) & (theta < Inf) ... & (sigma > 0) & (sigma < Inf) & (-Inf < k) & (k < Inf); if (isscalar (theta) && isscalar (sigma) && isscalar (k)) if (k == 0) x(kx) = -log(1 - p(kx)); x(kx) = sigma * x(kx) + theta; elseif (k > 0) x(kx) = (1 - p(kx)).^(-k) - 1; x(kx) = (sigma / k) * x(kx) + theta; elseif (k < 0) x(kx) = (1 - p(kx)).^(-k) - 1; x(kx) = (sigma / k) * x(kx) + theta; end else j = kx & (k == 0); if (any (j)) x(j) = -log (1 - p(j)); x(j) = sigma(j) .* x(j) + theta(j); endif j = kx & (k > 0); if (any (j)) x(j) = (1 - p(j)).^(-k(j)) - 1; x(j) = (sigma(j) ./ k(j)) .* x(j) + theta(j); endif j = kx & (k < 0); if (any (j)) x(j) = (1 - p(j)).^(-k(j)) - 1; x(j) = (sigma(j) ./ k(j)) .* x(j) + theta(j); endif endif endfunction %!demo %! ## Plot various iCDFs from the generalized Pareto distribution %! p = 0.001:0.001:0.999; %! x1 = gpinv (p, 1, 1, 0); %! x2 = gpinv (p, 5, 1, 0); %! x3 = gpinv (p, 20, 1, 0); %! x4 = gpinv (p, 1, 2, 0); %! x5 = gpinv (p, 5, 2, 0); %! x6 = gpinv (p, 20, 2, 0); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", ... %! p, x4, "-c", p, x5, "-m", p, x6, "-k") %! grid on %! ylim ([0, 5]) %! legend ({"k = 1, σ = 1, θ = 0", "k = 5, σ = 1, θ = 0", ... %! "k = 20, σ = 1, θ = 0", "k = 1, σ = 2, θ = 0", ... %! "k = 5, σ = 2, θ = 0", "k = 20, σ = 2, θ = 0"}, ... %! "location", "southeast") %! title ("Generalized Pareto iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, y1, y2, y3 %! p = [-1, 0, 1/2, 1, 2]; %! y1 = [NaN, 0, 0.6931471805599453, Inf, NaN]; %! y2 = [NaN, 0, 1, Inf, NaN]; %! y3 = [NaN, 0, 1/2, 1, NaN]; %!assert (gpinv (p, zeros (1,5), ones (1,5), zeros (1,5)), y1) %!assert (gpinv (p, 0, 1, zeros (1,5)), y1) %!assert (gpinv (p, 0, ones (1,5), 0), y1) %!assert (gpinv (p, zeros (1,5), 1, 0), y1) %!assert (gpinv (p, 0, 1, 0), y1) %!assert (gpinv (p, 0, 1, [0, 0, NaN, 0, 0]), [y1(1:2), NaN, y1(4:5)]) %!assert (gpinv (p, 0, [1, 1, NaN, 1, 1], 0), [y1(1:2), NaN, y1(4:5)]) %!assert (gpinv (p, [0, 0, NaN, 0, 0], 1, 0), [y1(1:2), NaN, y1(4:5)]) %!assert (gpinv ([p(1:2), NaN, p(4:5)], 0, 1, 0), [y1(1:2), NaN, y1(4:5)]) %!assert (gpinv (p, ones (1,5), ones (1,5), zeros (1,5)), y2) %!assert (gpinv (p, 1, 1, zeros (1,5)), y2) %!assert (gpinv (p, 1, ones (1,5), 0), y2) %!assert (gpinv (p, ones (1,5), 1, 0), y2) %!assert (gpinv (p, 1, 1, 0), y2) %!assert (gpinv (p, 1, 1, [0, 0, NaN, 0, 0]), [y2(1:2), NaN, y2(4:5)]) %!assert (gpinv (p, 1, [1, 1, NaN, 1, 1], 0), [y2(1:2), NaN, y2(4:5)]) %!assert (gpinv (p, [1, 1, NaN, 1, 1], 1, 0), [y2(1:2), NaN, y2(4:5)]) %!assert (gpinv ([p(1:2), NaN, p(4:5)], 1, 1, 0), [y2(1:2), NaN, y2(4:5)]) %!assert (gpinv (p, -ones (1,5), ones (1,5), zeros (1,5)), y3) %!assert (gpinv (p, -1, 1, zeros (1,5)), y3) %!assert (gpinv (p, -1, ones (1,5), 0), y3) %!assert (gpinv (p, -ones (1,5), 1, 0), y3) %!assert (gpinv (p, -1, 1, 0), y3) %!assert (gpinv (p, -1, 1, [0, 0, NaN, 0, 0]), [y3(1:2), NaN, y3(4:5)]) %!assert (gpinv (p, -1, [1, 1, NaN, 1, 1], 0), [y3(1:2), NaN, y3(4:5)]) %!assert (gpinv (p, -[1, 1, NaN, 1, 1], 1, 0), [y3(1:2), NaN, y3(4:5)]) %!assert (gpinv ([p(1:2), NaN, p(4:5)], -1, 1, 0), [y3(1:2), NaN, y3(4:5)]) ## Test class of input preserved %!assert (gpinv (single ([p, NaN]), 0, 1, 0), single ([y1, NaN])) %!assert (gpinv ([p, NaN], 0, 1, single (0)), single ([y1, NaN])) %!assert (gpinv ([p, NaN], 0, single (1), 0), single ([y1, NaN])) %!assert (gpinv ([p, NaN], single (0), 1, 0), single ([y1, NaN])) %!assert (gpinv (single ([p, NaN]), 1, 1, 0), single ([y2, NaN])) %!assert (gpinv ([p, NaN], 1, 1, single (0)), single ([y2, NaN])) %!assert (gpinv ([p, NaN], 1, single (1), 0), single ([y2, NaN])) %!assert (gpinv ([p, NaN], single (1), 1, 0), single ([y2, NaN])) %!assert (gpinv (single ([p, NaN]), -1, 1, 0), single ([y3, NaN])) %!assert (gpinv ([p, NaN], -1, 1, single (0)), single ([y3, NaN])) %!assert (gpinv ([p, NaN], -1, single (1), 0), single ([y3, NaN])) %!assert (gpinv ([p, NaN], single (-1), 1, 0), single ([y3, NaN])) ## Test input validation %!error gpinv () %!error gpinv (1) %!error gpinv (1, 2) %!error gpinv (1, 2, 3) %!error ... %! gpinv (ones (3), ones (2), ones(2), ones(2)) %!error ... %! gpinv (ones (2), ones (3), ones(2), ones(2)) %!error ... %! gpinv (ones (2), ones (2), ones(3), ones(2)) %!error ... %! gpinv (ones (2), ones (2), ones(2), ones(3)) %!error gpinv (i, 2, 3, 4) %!error gpinv (1, i, 3, 4) %!error gpinv (1, 2, i, 4) %!error gpinv (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/gppdf.m000066400000000000000000000217531475240274700214240ustar00rootroot00000000000000## Copyright (C) 1997-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} gppdf (@var{x}, @var{k}, @var{sigma}, @var{theta}) ## ## Generalized Pareto probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the generalized Pareto distribution with shape parameter @var{k}, scale ## parameter @var{sigma}, and location parameter @var{theta}. The size of ## @var{y} is the common size of @var{p}, @var{k}, @var{sigma}, and @var{theta}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## When @qcode{@var{k} = 0} and @qcode{@var{theta} = 0}, the Generalized Pareto ## is equivalent to the exponential distribution. When @qcode{@var{k} > 0} and ## @code{@var{theta} = @var{k} / @var{k}} the Generalized Pareto is equivalent ## to the Pareto distribution. The mean of the Generalized Pareto is not finite ## when @qcode{@var{k} >= 1} and the variance is not finite when ## @qcode{@var{k} >= 1/2}. When @qcode{@var{k} >= 0}, the Generalized Pareto ## has positive density for @qcode{@var{x} > @var{theta}}, or, when ## @qcode{@var{theta} < 0}, for ## @qcode{0 <= (@var{x} - @var{theta}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpcdf, gpinv, gprnd, gpfit, gplike, gpstat} ## @end deftypefn function y = gppdf (x, k, sigma, theta) ## Check for valid number of input arguments if (nargin < 4) error ("gppdf: function called with too few input arguments."); endif ## Check for common size of X, K, SIGMA, and THETA if (! isscalar (x) || ! isscalar (k) || ! isscalar (sigma) || ! isscalar (theta)) [err, x, k, sigma, theta] = common_size (x, k, sigma, theta); if (err > 0) error ("gppdf: X, K, SIGMA, and THETA must be of common size or scalars."); endif endif ## Check for X, K, SIGMA, and THETA being reals if (iscomplex (x) || iscomplex (k) || iscomplex (sigma) || iscomplex (theta)) error ("gppdf: X, K, SIGMA, and THETA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (theta, "single") || isa (sigma, "single") ... || isa (k, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Return NaNs for out of range values of sigma parameter ky = isnan (x) | ! (-Inf < theta) | ! (theta < Inf) | ... ! (sigma > 0) | ! (sigma < Inf) | ... ! (-Inf < k) | ! (k < Inf); y(ky) = NaN; ky = (-Inf < x) & (x < Inf) & (-Inf < theta) & (theta < Inf) & ... (sigma > 0) & (sigma < Inf) & (-Inf < k) & (k < Inf); if (isscalar (theta) && isscalar (sigma) && isscalar (k)) z = (x - theta) / sigma; j = ky & (k == 0) & (z >= 0); if (any (j)) y(j) = exp (-z(j)); endif j = ky & (k > 0) & (z >= 0); if (any (j)) y(j) = (k * z(j) + 1) .^ (-(k + 1) / k) ./ sigma; endif if (k < 0) j = ky & (k < 0) & (0 <= z) & (z <= -1. / k); if (any (j)) y(j) = (k * z(j) + 1) .^ (-(k + 1) / k) ./ sigma; endif endif else z = (x - theta) ./ sigma; j = ky & (k == 0) & (z >= 0); if (any (j)) y(j) = exp( -z(j)); endif j = ky & (k > 0) & (z >= 0); if (any (j)) y(j) = (k(j) .* z(j) + 1) .^ (-(k(j) + 1) ./ k(j)) ... ./ sigma(j); endif if (any (k < 0)) j = ky & (k < 0) & (0 <= z) & (z <= -1 ./ k); if (any (j)) y(j) = (k(j) .* z(j) + 1) .^ (-(k(j) + 1) ./ k(j)) ... ./ sigma(j); endif endif endif endfunction %!demo %! ## Plot various PDFs from the generalized Pareto distribution %! x = 0:0.001:5; %! y1 = gppdf (x, 1, 1, 0); %! y2 = gppdf (x, 5, 1, 0); %! y3 = gppdf (x, 20, 1, 0); %! y4 = gppdf (x, 1, 2, 0); %! y5 = gppdf (x, 5, 2, 0); %! y6 = gppdf (x, 20, 2, 0); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", ... %! x, y4, "-c", x, y5, "-m", x, y6, "-k") %! grid on %! xlim ([0, 5]) %! ylim ([0, 1]) %! legend ({"k = 1, σ = 1, θ = 0", "k = 5, σ = 1, θ = 0", ... %! "k = 20, σ = 1, θ = 0", "k = 1, σ = 2, θ = 0", ... %! "k = 5, σ = 2, θ = 0", "k = 20, σ = 2, θ = 0"}, ... %! "location", "northeast") %! title ("Generalized Pareto PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y1, y2, y3 %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! y1 = [0, 0, 1, 0.6065306597126334, 0.36787944117144233, 0]; %! y2 = [0, 0, 1, 4/9, 1/4, 0]; %! y3 = [0, 0, 1, 1, 1, 0]; %!assert (gppdf (x, zeros (1,6), ones (1,6), zeros (1,6)), y1, eps) %!assert (gppdf (x, 0, 1, zeros (1,6)), y1, eps) %!assert (gppdf (x, 0, ones (1,6), 0), y1, eps) %!assert (gppdf (x, zeros (1,6), 1, 0), y1, eps) %!assert (gppdf (x, 0, 1, 0), y1, eps) %!assert (gppdf (x, 0, 1, [0, 0, 0, NaN, 0, 0]), [y1(1:3), NaN, y1(5:6)]) %!assert (gppdf (x, 0, [1, 1, 1, NaN, 1, 1], 0), [y1(1:3), NaN, y1(5:6)]) %!assert (gppdf (x, [0, 0, 0, NaN, 0, 0], 1, 0), [y1(1:3), NaN, y1(5:6)]) %!assert (gppdf ([x(1:3), NaN, x(5:6)], 0, 1, 0), [y1(1:3), NaN, y1(5:6)]) %!assert (gppdf (x, ones (1,6), ones (1,6), zeros (1,6)), y2, eps) %!assert (gppdf (x, 1, 1, zeros (1,6)), y2, eps) %!assert (gppdf (x, 1, ones (1,6), 0), y2, eps) %!assert (gppdf (x, ones (1,6), 1, 0), y2, eps) %!assert (gppdf (x, 1, 1, 0), y2, eps) %!assert (gppdf (x, 1, 1, [0, 0, 0, NaN, 0, 0]), [y2(1:3), NaN, y2(5:6)]) %!assert (gppdf (x, 1, [1, 1, 1, NaN, 1, 1], 0), [y2(1:3), NaN, y2(5:6)]) %!assert (gppdf (x, [1, 1, 1, NaN, 1, 1], 1, 0), [y2(1:3), NaN, y2(5:6)]) %!assert (gppdf ([x(1:3), NaN, x(5:6)], 1, 1, 0), [y2(1:3), NaN, y2(5:6)]) %!assert (gppdf (x, -ones (1,6), ones (1,6), zeros (1,6)), y3, eps) %!assert (gppdf (x, -1, 1, zeros (1,6)), y3, eps) %!assert (gppdf (x, -1, ones (1,6), 0), y3, eps) %!assert (gppdf (x, -ones (1,6), 1, 0), y3, eps) %!assert (gppdf (x, -1, 1, 0), y3, eps) %!assert (gppdf (x, -1, 1, [0, 0, 0, NaN, 0, 0]), [y3(1:3), NaN, y3(5:6)]) %!assert (gppdf (x, -1, [1, 1, 1, NaN, 1, 1], 0), [y3(1:3), NaN, y3(5:6)]) %!assert (gppdf (x, [-1, -1, -1, NaN, -1, -1], 1, 0), [y3(1:3), NaN, y3(5:6)]) %!assert (gppdf ([x(1:3), NaN, x(5:6)], -1, 1, 0), [y3(1:3), NaN, y3(5:6)]) ## Test class of input preserved %!assert (gppdf (single ([x, NaN]), 0, 1, 0), single ([y1, NaN])) %!assert (gppdf ([x, NaN], 0, 1, single (0)), single ([y1, NaN])) %!assert (gppdf ([x, NaN], 0, single (1), 0), single ([y1, NaN])) %!assert (gppdf ([x, NaN], single (0), 1, 0), single ([y1, NaN])) %!assert (gppdf (single ([x, NaN]), 1, 1, 0), single ([y2, NaN])) %!assert (gppdf ([x, NaN], 1, 1, single (0)), single ([y2, NaN])) %!assert (gppdf ([x, NaN], 1, single (1), 0), single ([y2, NaN])) %!assert (gppdf ([x, NaN], single (1), 1, 0), single ([y2, NaN])) %!assert (gppdf (single ([x, NaN]), -1, 1, 0), single ([y3, NaN])) %!assert (gppdf ([x, NaN], -1, 1, single (0)), single ([y3, NaN])) %!assert (gppdf ([x, NaN], -1, single (1), 0), single ([y3, NaN])) %!assert (gppdf ([x, NaN], single (-1), 1, 0), single ([y3, NaN])) ## Test input validation %!error gpcdf () %!error gpcdf (1) %!error gpcdf (1, 2) %!error gpcdf (1, 2, 3) %!error ... %! gpcdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! gpcdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! gpcdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! gpcdf (ones (2), ones (2), ones(2), ones(3)) %!error gpcdf (i, 2, 3, 4) %!error gpcdf (1, i, 3, 4) %!error gpcdf (1, 2, i, 4) %!error gpcdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/gprnd.m000066400000000000000000000221411475240274700214260ustar00rootroot00000000000000## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} gprnd (@var{k}, @var{sigma}, @var{theta}) ## @deftypefnx {statistics} {@var{r} =} gprnd (@var{k}, @var{sigma}, @var{theta}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} gprnd (@var{k}, @var{sigma}, @var{theta}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} gprnd (@var{k}, @var{sigma}, @var{theta}, [@var{sz}]) ## ## Random arrays from the generalized Pareto distribution. ## ## @code{@var{r} = gprnd (@var{k}, @var{sigma}, @var{theta})} returns an array ## of random numbers chosen from the generalized Pareto distribution with shape ## parameter @var{k}, scale parameter @var{sigma}, and location parameter ## @var{theta}. The size of @var{r} is the common size of @var{k}, @var{sigma}, ## and @var{theta}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## When called with a single size argument, @code{gprnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## When @qcode{@var{k} = 0} and @qcode{@var{theta} = 0}, the Generalized Pareto ## is equivalent to the exponential distribution. When @qcode{@var{k} > 0} and ## @code{@var{theta} = @var{k} / @var{k}} the Generalized Pareto is equivalent ## to the Pareto distribution. The mean of the Generalized Pareto is not finite ## when @qcode{@var{k} >= 1} and the variance is not finite when ## @qcode{@var{k} >= 1/2}. When @qcode{@var{k} >= 0}, the Generalized Pareto ## has positive density for @qcode{@var{x} > @var{theta}}, or, when ## @qcode{@var{theta} < 0}, for ## @qcode{0 <= (@var{x} - @var{theta}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpcdf, gpinv, gppdf, gpfit, gplike, gpstat} ## @end deftypefn function r = gprnd (k, sigma, theta, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("gprnd: function called with too few input arguments."); endif ## Check for common size of K, SIGMA, and THETA if (! isscalar (k) || ! isscalar (sigma) || ! isscalar (theta)) [retval, k, sigma, theta] = common_size (k, sigma, theta); if (retval > 0) error ("gprnd: K, SIGMA, and THETA must be of common size or scalars."); endif endif ## Check for K, SIGMA, and THETA being reals if (iscomplex (k) || iscomplex (sigma) || iscomplex (theta)) error ("gprnd: K, SIGMA, and THETA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (k); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["gprnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("gprnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (!isscalar (k) && ! isequal (size (k), sz)) error ("gprnd: K, SIGMA, and THETA must be scalars or of size SZ."); endif ## Check for class type if (isa (k, "single") || isa (sigma, "single") || isa (theta, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from generalized Pareto distribution r = rand (sz, cls); ## Find valid parameters vr = (isfinite (r)) & (theta > -Inf) & (theta < Inf) ... & (sigma > 0) & (sigma < Inf) ... & (-Inf < k) & (k < Inf); ## Force invalid parameters to NaN r(! vr) = NaN; if (isscalar (k)) if (k == 0) r(vr) = theta - (sigma .* log (1 - r(vr))); else r(vr) = theta + ((sigma .* ((r(vr) .^ -k) - 1)) ./ k); endif else if (any (k == 0)) r(vr) = theta(vr) - (sigma(vr) .* log (1 - r(vr))); endif if (any (k < 0 | k > 0)) r(vr) = theta(vr) + ((sigma(vr) .* ((r(vr) .^ -k(vr)) - 1)) ./ k(vr)); endif endif endfunction ## Test output %!assert (size (gprnd (0, 1, 0)), [1, 1]) %!assert (size (gprnd (0, 1, zeros (2,1))), [2, 1]) %!assert (size (gprnd (0, 1, zeros (2,2))), [2, 2]) %!assert (size (gprnd (0, ones (2,1), 0)), [2, 1]) %!assert (size (gprnd (0, ones (2,2), 0)), [2, 2]) %!assert (size (gprnd (zeros (2,1), 1, 0)), [2, 1]) %!assert (size (gprnd (zeros (2,2), 1, 0)), [2, 2]) %!assert (size (gprnd (0, 1, 0, 3)), [3, 3]) %!assert (size (gprnd (0, 1, 0, [4 1])), [4, 1]) %!assert (size (gprnd (0, 1, 0, 4, 1)), [4, 1]) %!assert (size (gprnd (1,1,0)), [1, 1]) %!assert (size (gprnd (1, 1, zeros (2,1))), [2, 1]) %!assert (size (gprnd (1, 1, zeros (2,2))), [2, 2]) %!assert (size (gprnd (1, ones (2,1), 0)), [2, 1]) %!assert (size (gprnd (1, ones (2,2), 0)), [2, 2]) %!assert (size (gprnd (ones (2,1), 1, 0)), [2, 1]) %!assert (size (gprnd (ones (2,2), 1, 0)), [2, 2]) %!assert (size (gprnd (1, 1, 0, 3)), [3, 3]) %!assert (size (gprnd (1, 1, 0, [4 1])), [4, 1]) %!assert (size (gprnd (1, 1, 0, 4, 1)), [4, 1]) %!assert (size (gprnd (-1, 1, 0)), [1, 1]) %!assert (size (gprnd (-1, 1, zeros (2,1))), [2, 1]) %!assert (size (gprnd (1, -1, zeros (2,2))), [2, 2]) %!assert (size (gprnd (-1, ones (2,1), 0)), [2, 1]) %!assert (size (gprnd (-1, ones (2,2), 0)), [2, 2]) %!assert (size (gprnd (-ones (2,1), 1, 0)), [2, 1]) %!assert (size (gprnd (-ones (2,2), 1, 0)), [2, 2]) %!assert (size (gprnd (-1, 1, 0, 3)), [3, 3]) %!assert (size (gprnd (-1, 1, 0, [4, 1])), [4, 1]) %!assert (size (gprnd (-1, 1, 0, 4, 1)), [4, 1]) ## Test class of input preserved %!assert (class (gprnd (0, 1, 0)), "double") %!assert (class (gprnd (0, 1, single (0))), "single") %!assert (class (gprnd (0, 1, single ([0, 0]))), "single") %!assert (class (gprnd (0, single (1),0)), "single") %!assert (class (gprnd (0, single ([1, 1]),0)), "single") %!assert (class (gprnd (single (0), 1, 0)), "single") %!assert (class (gprnd (single ([0, 0]), 1, 0)), "single") ## Test input validation %!error gprnd () %!error gprnd (1) %!error gprnd (1, 2) %!error ... %! gprnd (ones (3), ones (2), ones (2)) %!error ... %! gprnd (ones (2), ones (3), ones (2)) %!error ... %! gprnd (ones (2), ones (2), ones (3)) %!error gprnd (i, 2, 3) %!error gprnd (1, i, 3) %!error gprnd (1, 2, i) %!error ... %! gprnd (1, 2, 3, -1) %!error ... %! gprnd (1, 2, 3, 1.2) %!error ... %! gprnd (1, 2, 3, ones (2)) %!error ... %! gprnd (1, 2, 3, [2 -1 2]) %!error ... %! gprnd (1, 2, 3, [2 0 2.5]) %!error ... %! gprnd (1, 2, 3, 2, -1, 5) %!error ... %! gprnd (1, 2, 3, 2, 1.5, 5) %!error ... %! gprnd (2, ones (2), 2, 3) %!error ... %! gprnd (2, ones (2), 2, [3, 2]) %!error ... %! gprnd (2, ones (2), 2, 3, 2) statistics-release-1.7.3/inst/dist_fun/gumbelcdf.m000066400000000000000000000221601475240274700222450ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} gumbelcdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} gumbelcdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} gumbelcdf (@var{x}, @var{mu}, @var{beta}) ## @deftypefnx {statistics} {@var{p} =} gumbelcdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} gumbelcdf (@var{x}, @var{mu}, @var{beta}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} gumbelcdf (@var{x}, @var{mu}, @var{beta}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} gumbelcdf (@dots{}, @qcode{"upper"}) ## ## Gumbel cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Gumbel distribution (also known as the extreme value or the type ## I generalized extreme value distribution) with location parameter @var{mu} ## and scale parameter @var{beta}. The size of @var{p} is the common size of ## @var{x}, @var{mu} and @var{beta}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Default values are @var{mu} = 0 and @var{beta} = 1. ## ## When called with three output arguments, i.e. @code{[@var{p}, @var{plo}, ## @var{pup}]}, @code{gumbelcdf} computes the confidence bounds for @var{p} when ## the input parameters @var{mu} and @var{beta} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{plo} and @var{pup} are arrays of the same size as ## @var{p} containing the lower and upper confidence bounds. ## ## @code{[@dots{}] = gumbelcdf (@dots{}, "upper")} computes the upper tail ## probability of the Gumbel distribution with parameters @var{mu} and ## @var{beta}, at the values in @var{x}. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling maxima. For modeling minima, use the alternative ## extreme value CDF, @code{evcdf}. ## ## @code{[@dots{}] = gumbelcdf (@dots{}, "upper")} computes the upper tail ## probability of the extreme value (Gumbel) distribution. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{gumbelinv, gumbelpdf, gumbelrnd, gumbelfit, gumbellike, gumbelstat, ## evcdf} ## @end deftypefn function [varargout] = gumbelcdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 6) error ("gumbelcdf: invalid number of input arguments."); endif ## Check for 'upper' flag if (nargin > 1 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 1 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("gumbelcdf: invalid argument for upper tail."); else uflag = false; endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) mu = varargin{1}; else mu = 0; endif if (numel (varargin) > 1) beta = varargin{2}; else beta = 1; endif if (numel (varargin) > 2) pcov = varargin{3}; ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("gumbelcdf: invalid size of covariance matrix."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("gumbelcdf: covariance matrix is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 3) alpha = varargin{4}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("gumbelcdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X, MU, and BETA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (beta)) [err, x, mu, beta] = common_size (x, mu, beta); if (err > 0) error ("gumbelcdf: X, MU, and BETA must be of common size or scalars."); endif endif ## Check for X, MU, and BETA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (beta)) error ("gumbelcdf: X, MU, and BETA must not be complex."); endif ## Return NaNs for out of range parameters. beta(beta <= 0) = NaN; ## Compute extreme value cdf z = (x - mu) ./ beta; if (uflag) p = -expm1 (-exp (-z)); else p = exp (-exp (-z)); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (beta, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output varargout{1} = cast (p, is_class); if (nargout > 1) plo = NaN (size (z), is_class); pup = NaN (size (z), is_class); endif ## Check beta if (isscalar (beta)) if (beta > 0) sigma_p = true (size (z)); else if (nargout == 3) varargout{2} = plo; varargout{3} = pup; endif return; endif else sigma_p = beta > 0; endif ## Compute confidence bounds (if requested) if (nargout >= 2) zvar = (pcov(1,1) + 2 * pcov(1,2) * z(sigma_p) + ... pcov(2,2) * z(sigma_p) .^ 2) ./ (beta .^ 2); if (any (zvar < 0)) error ("gumbelcdf: bad covariance matrix."); endif normz = -norminv (alpha / 2); halfwidth = normz * sqrt (zvar); zlo = z(sigma_p) - halfwidth; zup = z(sigma_p) + halfwidth; if (uflag) plo(sigma_p) = -expm1 (-exp (-zup)); pup(sigma_p) = -expm1 (-exp (-zlo)); else plo(sigma_p) = exp (-exp (-zlo)); pup(sigma_p) = exp (-exp (-zup)); endif varargout{2} = plo; varargout{3} = pup; endif endfunction %!demo %! ## Plot various CDFs from the Gumbel distribution %! x = -5:0.01:20; %! p1 = gumbelcdf (x, 0.5, 2); %! p2 = gumbelcdf (x, 1.0, 2); %! p3 = gumbelcdf (x, 1.5, 3); %! p4 = gumbelcdf (x, 3.0, 4); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! legend ({"μ = 0.5, β = 2", "μ = 1.0, β = 2", ... %! "μ = 1.5, β = 3", "μ = 3.0, β = 4"}, "location", "southeast") %! title ("Gumbel CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-Inf, 1, 2, Inf]; %! y = [0, 0.3679, 0.6922, 1]; %!assert (gumbelcdf (x, ones (1,4), ones (1,4)), y, 1e-4) %!assert (gumbelcdf (x, 1, ones (1,4)), y, 1e-4) %!assert (gumbelcdf (x, ones (1,4), 1), y, 1e-4) %!assert (gumbelcdf (x, [0, -Inf, NaN, Inf], 1), [0, 1, NaN, NaN], 1e-4) %!assert (gumbelcdf (x, 1, [Inf, NaN, -1, 0]), [NaN, NaN, NaN, NaN], 1e-4) %!assert (gumbelcdf ([x(1:2), NaN, x(4)], 1, 1), [y(1:2), NaN, y(4)], 1e-4) %!assert (gumbelcdf (x, "upper"), [1, 0.3078, 0.1266, 0], 1e-4) ## Test class of input preserved %!assert (gumbelcdf ([x, NaN], 1, 1), [y, NaN], 1e-4) %!assert (gumbelcdf (single ([x, NaN]), 1, 1), single ([y, NaN]), 1e-4) %!assert (gumbelcdf ([x, NaN], single (1), 1), single ([y, NaN]), 1e-4) %!assert (gumbelcdf ([x, NaN], 1, single (1)), single ([y, NaN]), 1e-4) ## Test input validation %!error gumbelcdf () %!error gumbelcdf (1,2,3,4,5,6,7) %!error gumbelcdf (1, 2, 3, 4, "uper") %!error ... %! gumbelcdf (ones (3), ones (2), ones (2)) %!error gumbelcdf (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = gumbelcdf (1, 2, 3) %!error [p, plo, pup] = ... %! gumbelcdf (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! gumbelcdf (1, 2, 3, [1, 0; 0, 1], 1.22) %!error [p, plo, pup] = ... %! gumbelcdf (1, 2, 3, [1, 0; 0, 1], "alpha", "upper") %!error gumbelcdf (i, 2, 2) %!error gumbelcdf (2, i, 2) %!error gumbelcdf (2, 2, i) %!error ... %! [p, plo, pup] = gumbelcdf (1, 2, 3, [1, 0; 0, -inf], 0.04) statistics-release-1.7.3/inst/dist_fun/gumbelinv.m000066400000000000000000000172301475240274700223070ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} gumbelinv (@var{p}) ## @deftypefnx {statistics} {@var{x} =} gumbelinv (@var{p}, @var{mu}) ## @deftypefnx {statistics} {@var{x} =} gumbelinv (@var{p}, @var{mu}, @var{beta}) ## @deftypefnx {statistics} {[@var{x}, @var{xlo}, @var{xup}] =} gumbelinv (@var{p}, @var{mu}, @var{beta}, @var{pcov}) ## @deftypefnx {statistics} {[@var{x}, @var{xlo}, @var{xup}] =} gumbelinv (@var{p}, @var{mu}, @var{beta}, @var{pcov}, @var{alpha}) ## ## Inverse of the Gumbel cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Gumbel distribution (also known as the extreme value or the type I ## generalized extreme value distribution) with location parameter @var{mu} and ## scale parameter @var{beta}. The size of @var{x} is the common size of ## @var{p}, @var{mu} and @var{beta}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Default values are @var{mu} = 0 and @var{beta} = 1. ## ## When called with three output arguments, i.e. @qcode{[@var{x}, @var{xlo}, ## @var{xup}]}, @code{gumbelinv} computes the confidence bounds for @var{x} when ## the input parameters @var{mu} and @var{beta} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{xlo} and @var{xup} are arrays of the same size as ## @var{x} containing the lower and upper confidence bounds. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling maxima. For modeling minima, use the alternative ## extreme value iCDF, @code{evinv}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{gumbelcdf, gumbelpdf, gumbelrnd, gumbelfit, gumbellike, gumbelstat, ## evinv} ## @end deftypefn function [x, xlo, xup] = gumbelinv (p, mu, beta, pcov, alpha) ## Check for valid number of input arguments if (nargin < 1 || nargin > 5) error ("gumbelinv: invalid number of input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif if (nargin < 3) beta = 1; endif ## Check if PCOV is provided when confidence bounds are requested if (nargout > 2) if (nargin < 4) error ("gumbelinv: covariance matrix is required for confidence bounds."); endif ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("gumbelinv: invalid size of covariance matrix."); endif ## Check for valid alpha value if (nargin < 5) alpha = 0.05; elseif (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("gumbelinv: invalid value for alpha."); endif endif ## Check for common size of P, MU, and BETA if (! isscalar (p) || ! isscalar (mu) || ! isscalar (beta)) [err, p, mu, beta] = common_size (p, mu, beta); if (err > 0) error ("gumbelinv: P, MU, and BETA must be of common size or scalars."); endif endif ## Check for P, MU, and BETA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (beta)) error ("gumbelinv: P, MU, and BETA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (beta, "single")); is_class = "single"; else is_class = "double"; endif ## Compute inverse of type 1 extreme value cdf k = (eps <= p & p < 1); if (all (k(:))) q = -log (-log (p)); else q = zeros (size (p), is_class); q(k) = -log (-log (p(k))); ## Return -Inf for p = 0 and Inf for p = 1 q(p < eps) = -Inf; q(p == 1) = Inf; ## Return NaN for out of range values of P q(p < 0 | 1 < p | isnan (p)) = NaN; endif ## Return NaN for out of range values of BETA beta(beta <= 0) = NaN; x = (beta .* q) + mu; ## Compute confidence bounds if requested. if (nargout >= 2) xvar = pcov(1,1) + 2 * pcov(1,2) * q + pcov(2,2) * q .^ 2; if (any (xvar < 0)) || any(isnan(xvar)) error ("gumbelinv: bad covariance matrix."); endif z = -norminv (alpha / 2); halfwidth = z * sqrt (xvar); xlo = x - halfwidth; xup = x + halfwidth; endif endfunction %!demo %! ## Plot various iCDFs from the Gumbel distribution %! p = 0.001:0.001:0.999; %! x1 = gumbelinv (p, 0.5, 2); %! x2 = gumbelinv (p, 1.0, 2); %! x3 = gumbelinv (p, 1.5, 3); %! x4 = gumbelinv (p, 3.0, 4); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([-5, 20]) %! legend ({"μ = 0.5, β = 2", "μ = 1.0, β = 2", ... %! "μ = 1.5, β = 3", "μ = 3.0, β = 4"}, "location", "northwest") %! title ("Gumbel iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, x %! p = [0, 0.05, 0.5 0.95]; %! x = [-Inf, -1.0972, 0.3665, 2.9702]; %!assert (gumbelinv (p), x, 1e-4) %!assert (gumbelinv (p, zeros (1,4), ones (1,4)), x, 1e-4) %!assert (gumbelinv (p, 0, ones (1,4)), x, 1e-4) %!assert (gumbelinv (p, zeros (1,4), 1), x, 1e-4) %!assert (gumbelinv (p, [0, -Inf, NaN, Inf], 1), [-Inf, -Inf, NaN, Inf], 1e-4) %!assert (gumbelinv (p, 0, [Inf, NaN, -1, 0]), [-Inf, NaN, NaN, NaN], 1e-4) %!assert (gumbelinv ([p(1:2), NaN, p(4)], 0, 1), [x(1:2), NaN, x(4)], 1e-4) ## Test class of input preserved %!assert (gumbelinv ([p, NaN], 0, 1), [x, NaN], 1e-4) %!assert (gumbelinv (single ([p, NaN]), 0, 1), single ([x, NaN]), 1e-4) %!assert (gumbelinv ([p, NaN], single (0), 1), single ([x, NaN]), 1e-4) %!assert (gumbelinv ([p, NaN], 0, single (1)), single ([x, NaN]), 1e-4) ## Test whether gumbelcdf is successfully inverted %! p = [0.05, 0.5, 0.95]; %! x = gumbelinv(p); %!assert (gumbelcdf(x), p, 1e-4) ## Test input validation %!error gumbelinv () %!error gumbelinv (1,2,3,4,5,6) %!error ... %! gumbelinv (ones (3), ones (2), ones (2)) %!error ... %! [p, plo, pup] = gumbelinv (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = gumbelinv (1, 2, 3) %!error [p, plo, pup] = ... %! gumbelinv (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! gumbelinv (1, 2, 3, [1, 0; 0, 1], 1.22) %!error gumbelinv (i, 2, 2) %!error gumbelinv (2, i, 2) %!error gumbelinv (2, 2, i) %!error ... %! [p, plo, pup] = gumbelinv (1, 2, 3, [-1, 10; -Inf, -Inf], 0.04) statistics-release-1.7.3/inst/dist_fun/gumbelpdf.m000066400000000000000000000104161475240274700222630ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} gumbelpdf (@var{x}) ## @deftypefnx {statistics} {@var{y} =} gumbelpdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{y} =} gumbelpdf (@var{x}, @var{mu}, @var{beta}) ## ## Gumbel probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Gumbel distribution (also known as the extreme value or the type I ## generalized extreme value distribution) with location parameter @var{mu} and ## scale parameter @var{beta}. The size of @var{y} is the common size of ## @var{x}, @var{mu} and @var{beta}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Default values are @var{mu} = 0 and @var{beta} = 1. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling maxima. For modeling minima, use the alternative ## extreme value iCDF, @code{evpdf}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{gumbelcdf, gumbelinv, gumbelrnd, gumbelfit, gumbellike, gumbelstat, ## evpdf} ## @end deftypefn function y = gumbelpdf (x, mu, beta) ## Check for valid number of input arguments if (nargin < 1) error ("gumbelpdf: too few input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif if (nargin < 3) beta = 1; endif ## Check for common size of X, MU, and BETA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (beta)) [err, x, mu, beta] = common_size (x, mu, beta); if (err > 0) error ("gumbelpdf: X, MU, and BETA must be of common size or scalars."); endif endif ## Check for X, MU, and BETA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (beta)) error ("gumbelpdf: X, MU, and BETA must not be complex."); endif ## Return NaNs for out of range parameters beta(beta <= 0) = NaN; ## Compute pdf of type 1 extreme value distribution z = -(x - mu) ./ beta; y = exp (z - exp (z)) ./ beta; ## Force 0 for extreme right tail, instead of getting exp (Inf - Inf) = NaN y(z == Inf) = 0; endfunction %!demo %! ## Plot various PDFs from the Extreme value distribution %! x = -5:0.001:20; %! y1 = gumbelpdf (x, 0.5, 2); %! y2 = gumbelpdf (x, 1.0, 2); %! y3 = gumbelpdf (x, 1.5, 3); %! y4 = gumbelpdf (x, 3.0, 4); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! ylim ([0, 0.2]) %! legend ({"μ = 0.5, β = 2", "μ = 1.0, β = 2", ... %! "μ = 1.5, β = 3", "μ = 3.0, β = 4"}, "location", "northeast") %! title ("Extreme value PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y0, y1 %! x = [-5, 0, 1, 2, 3]; %! y0 = [0, 0.3679, 0.2547, 0.1182, 0.0474]; %! y1 = [0, 0.1794, 0.3679, 0.2547, 0.1182]; %!assert (gumbelpdf (x), y0, 1e-4) %!assert (gumbelpdf (x, zeros (1,5), ones (1,5)), y0, 1e-4) %!assert (gumbelpdf (x, ones (1,5), ones (1,5)), y1, 1e-4) ## Test input validation %!error gumbelpdf () %!error ... %! gumbelpdf (ones (3), ones (2), ones (2)) %!error gumbelpdf (i, 2, 2) %!error gumbelpdf (2, i, 2) %!error gumbelpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/gumbelrnd.m000066400000000000000000000155641475240274700223060ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} gumbelrnd (@var{mu}, @var{beta}) ## @deftypefnx {statistics} {@var{r} =} gumbelrnd (@var{mu}, @var{beta}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} gumbelrnd (@var{mu}, @var{beta}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} gumbelrnd (@var{mu}, @var{beta}, [@var{sz}]) ## ## Random arrays from the Gumbel distribution. ## ## @code{@var{r} = gumbelrnd (@var{mu}, @var{beta})} returns an array of random ## numbers chosen from the Gumbel distribution (also known as the extreme value ## or the type I generalized extreme value distribution) with location ## parameter @var{mu} and scale parameter @var{beta}. The size of @var{r} is ## the common size of @var{mu} and @var{beta}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{gumbelrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## The Gumbel distribution is used to model the distribution of the maximum (or ## the minimum) of a number of samples of various distributions. This version ## is suitable for modeling maxima. For modeling minima, use the alternative ## extreme value iCDF, @code{evinv}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{gumbelcdf, gumbelinv, gumbelpdf, gumbelfit, gumbellike, gumbelstat, ## evrnd} ## @end deftypefn function r = gumbelrnd (mu, beta, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("gumbelrnd: function called with too few input arguments."); endif ## Check for common size of MU and BETA if (! isscalar (mu) || ! isscalar (beta)) [retval, mu, beta] = common_size (mu, beta); if (retval > 0) error ("gumbelrnd: MU and BETA must be of common size or scalars."); endif endif ## Check for MU and BETA being reals if (iscomplex (mu) || iscomplex (beta)) error ("gumbelrnd: MU and BETA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["gumbelrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("gumbelrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("gumbelrnd: MU and BETA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (beta, "single")) cls = "single"; else cls = "double"; endif ## Return NaNs for out of range values of BETA beta(beta < 0) = NaN; ## Generate uniform random values, and apply the extreme value inverse CDF. r = -log (-log (rand (sz, cls))) .* beta + mu; endfunction ## Test output %!assert (size (gumbelrnd (1, 1)), [1 1]) %!assert (size (gumbelrnd (1, ones (2,1))), [2, 1]) %!assert (size (gumbelrnd (1, ones (2,2))), [2, 2]) %!assert (size (gumbelrnd (ones (2,1), 1)), [2, 1]) %!assert (size (gumbelrnd (ones (2,2), 1)), [2, 2]) %!assert (size (gumbelrnd (1, 1, 3)), [3, 3]) %!assert (size (gumbelrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (gumbelrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (gumbelrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (gumbelrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (gumbelrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (gumbelrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (gumbelrnd (1, 1)), "double") %!assert (class (gumbelrnd (1, single (1))), "single") %!assert (class (gumbelrnd (1, single ([1, 1]))), "single") %!assert (class (gumbelrnd (single (1), 1)), "single") %!assert (class (gumbelrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error gumbelrnd () %!error gumbelrnd (1) %!error ... %! gumbelrnd (ones (3), ones (2)) %!error ... %! gumbelrnd (ones (2), ones (3)) %!error gumbelrnd (i, 2, 3) %!error gumbelrnd (1, i, 3) %!error ... %! gumbelrnd (1, 2, -1) %!error ... %! gumbelrnd (1, 2, 1.2) %!error ... %! gumbelrnd (1, 2, ones (2)) %!error ... %! gumbelrnd (1, 2, [2 -1 2]) %!error ... %! gumbelrnd (1, 2, [2 0 2.5]) %!error ... %! gumbelrnd (1, 2, 2, -1, 5) %!error ... %! gumbelrnd (1, 2, 2, 1.5, 5) %!error ... %! gumbelrnd (2, ones (2), 3) %!error ... %! gumbelrnd (2, ones (2), [3, 2]) %!error ... %! gumbelrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/hncdf.m000066400000000000000000000137561475240274700214120ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} hncdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} hncdf (@var{x}, @var{mu}, @var{sigma}, @qcode{"upper"}) ## ## Half-normal cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the half-normal distribution with location parameter @var{mu} and ## scale parameter @var{sigma}. The size of @var{p} is the common size of ## @var{x}, @var{mu} and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## @code{[@dots{}] = hncdf (@var{x}, @var{mu}, @var{sigma}, "upper")} computes ## the upper tail probability of the half-normal distribution with parameters ## @var{mu} and @var{sigma}, at the values in @var{x}. ## ## The half-normal CDF is only defined for @qcode{@var{x} >= @var{mu}}. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{hninv, hnpdf, hnrnd, hnfit, hnlike, hnstat} ## @end deftypefn function p = hncdf (x, mu, sigma, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("hncdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("hncdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma)) [err, x, mu, sigma] = common_size (x, mu, sigma); if (err > 0) error ("hncdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("hncdf: X, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")) is_class = "single"; else is_class = "double"; endif ## Prepare output p = zeros (size (x), is_class); ## Return NaNs for out of range values of SIGMA parameter sigma(sigma <= 0) = NaN; ## Calculate (x-mu)/sigma => 0 and force zero below that z = (x - mu) ./ sigma; z(z < 0) = 0; if (uflag) p = erfc(z./sqrt(2)); else p = erf(z./sqrt(2)); endif endfunction %!demo %! ## Plot various CDFs from the half-normal distribution %! x = 0:0.001:10; %! p1 = hncdf (x, 0, 1); %! p2 = hncdf (x, 0, 2); %! p3 = hncdf (x, 0, 3); %! p4 = hncdf (x, 0, 5); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! xlim ([0, 10]) %! legend ({"μ = 0, σ = 1", "μ = 0, σ = 2", ... %! "μ = 0, σ = 3", "μ = 0, σ = 5"}, "location", "southeast") %! title ("Half-normal CDF") %! xlabel ("values in x") %! ylabel ("probability") %!demo %! ## Plot half-normal against normal cumulative distribution function %! x = -5:0.001:5; %! p1 = hncdf (x, 0, 1); %! p2 = normcdf (x); %! plot (x, p1, "-b", x, p2, "-g") %! grid on %! xlim ([-5, 5]) %! legend ({"half-normal with μ = 0, σ = 1", ... %! "standart normal (μ = 0, σ = 1)"}, "location", "southeast") %! title ("Half-normal against standard normal CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, p1, p1u, y2, y2u, y3, y3u %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! p1 = [0, 0, 0, 0.3829, 0.6827, 1]; %! p1u = [1, 1, 1, 0.6171, 0.3173, 0]; %!assert (hncdf (x, zeros (1,6), ones (1,6)), p1, 1e-4) %!assert (hncdf (x, 0, 1), p1, 1e-4) %!assert (hncdf (x, 0, ones (1,6)), p1, 1e-4) %!assert (hncdf (x, zeros (1,6), 1), p1, 1e-4) %!assert (hncdf (x, 0, [1, 1, 1, NaN, 1, 1]), [p1(1:3), NaN, p1(5:6)], 1e-4) %!assert (hncdf (x, [0, 0, 0, NaN, 0, 0], 1), [p1(1:3), NaN, p1(5:6)], 1e-4) %!assert (hncdf ([x(1:3), NaN, x(5:6)], 0, 1), [p1(1:3), NaN, p1(5:6)], 1e-4) %!assert (hncdf (x, zeros (1,6), ones (1,6), "upper"), p1u, 1e-4) %!assert (hncdf (x, 0, 1, "upper"), p1u, 1e-4) %!assert (hncdf (x, 0, ones (1,6), "upper"), p1u, 1e-4) %!assert (hncdf (x, zeros (1,6), 1, "upper"), p1u, 1e-4) ## Test class of input preserved %!assert (class (hncdf (single ([x, NaN]), 0, 1)), "single") %!assert (class (hncdf ([x, NaN], 0, single (1))), "single") %!assert (class (hncdf ([x, NaN], single (0), 1)), "single") ## Test input validation %!error hncdf () %!error hncdf (1) %!error hncdf (1, 2) %!error hncdf (1, 2, 3, "tail") %!error hncdf (1, 2, 3, 5) %!error ... %! hncdf (ones (3), ones (2), ones(2)) %!error ... %! hncdf (ones (2), ones (3), ones(2)) %!error ... %! hncdf (ones (2), ones (2), ones(3)) %!error hncdf (i, 2, 3) %!error hncdf (1, i, 3) %!error hncdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/hninv.m000066400000000000000000000102551475240274700214410ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} hninv (@var{p}, @var{mu}, @var{sigma}) ## ## Inverse of the half-normal cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the half-normal distribution with location parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{x} is the common size of @var{p}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{hncdf, hnpdf, hnrnd, hnfit, hnlike, hnstat} ## @end deftypefn function x = hninv (p, mu, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("hninv: function called with too few input arguments."); endif ## Check for common size of P, MU, and SIGMA if (! isscalar (p) || ! isscalar (mu) || ! isscalar(sigma)) [retval, p, mu, sigma] = common_size (p, mu, sigma); if (retval > 0) error ("hninv: P, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma)) error ("hninv: P, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single")); x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Return NaNs for out of range values of SIGMA parameter sigma(sigma <= 0) = NaN; ## Return NaNs for out of range P values p(p < 0 | 1 < p) = NaN; ## Calculate the quantile of half-normal distribution x = sqrt(2) * sigma .* erfinv (p) + mu; endfunction %!demo %! ## Plot various iCDFs from the half-normal distribution %! p = 0.001:0.001:0.999; %! x1 = hninv (p, 0, 1); %! x2 = hninv (p, 0, 2); %! x3 = hninv (p, 0, 3); %! x4 = hninv (p, 0, 5); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([0, 10]) %! legend ({"μ = 0, σ = 1", "μ = 0, σ = 2", ... %! "μ = 0, σ = 3", "μ = 0, σ = 5"}, "location", "northwest") %! title ("Half-normal iCDF") %! xlabel ("probability") %! ylabel ("x") ## Test output %!shared p, x %! p = [0, 0.3829, 0.6827, 1]; %! x = [0, 1/2, 1, Inf]; %!assert (hninv (p, 0, 1), x, 1e-4); %!assert (hninv (p, 5, 1), x + 5, 1e-4); %!assert (hninv (p, 0, ones (1,4)), x, 1e-4); %!assert (hninv (p, 0, [-1, 0, 1, 1]), [NaN, NaN, x(3:4)], 1e-4) ## Test class of input preserved %!assert (class (hninv (single ([p, NaN]), 0, 1)), "single") %!assert (class (hninv ([p, NaN], single (0), 1)), "single") %!assert (class (hninv ([p, NaN], 0, single (1))), "single") ## Test input validation %!error hninv (1) %!error hninv (1, 2) %!error ... %! hninv (1, ones (2), ones (3)) %!error ... %! hninv (ones (2), 1, ones (3)) %!error ... %! hninv (ones (2), ones (3), 1) %!error hninv (i, 2, 3) %!error hninv (1, i, 3) %!error hninv (1, 2, i) statistics-release-1.7.3/inst/dist_fun/hnpdf.m000066400000000000000000000113071475240274700214150ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} hnpdf (@var{x}, @var{mu}, @var{sigma}) ## ## Half-normal probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the half-normal distribution with location parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{y} is the common size of @var{x}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## The half-normal CDF is only defined for @qcode{@var{x} >= @var{mu}}. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{hncdf, hninv, hnrnd, hnfit, hnlike, hnstat} ## @end deftypefn function y = hnpdf (x, mu, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("hnpdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("hnpdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("hnpdf: X, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); y = NaN (size (x), "single"); else y = NaN (size (x)); endif ## Return NaNs for out of range values of SIGMA parameter sigma(sigma <= 0) = NaN; ## Compute half-normal PDF z = (x - mu) ./ sigma; y = sqrt (2 / pi) ./ sigma .* exp (-0.5 * z .^ 2); ## Force zero for unsupported X y(z < 0) = 0; endfunction %!demo %! ## Plot various PDFs from the half-normal distribution %! x = 0:0.001:10; %! y1 = hnpdf (x, 0, 1); %! y2 = hnpdf (x, 0, 2); %! y3 = hnpdf (x, 0, 3); %! y4 = hnpdf (x, 0, 5); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! xlim ([0, 10]) %! ylim ([0, 0.9]) %! legend ({"μ = 0, σ = 1", "μ = 0, σ = 2", ... %! "μ = 0, σ = 3", "μ = 0, σ = 5"}, "location", "northeast") %! title ("Half-normal PDF") %! xlabel ("values in x") %! ylabel ("density") %!demo %! ## Plot half-normal against normal probability density function %! x = -5:0.001:5; %! y1 = hnpdf (x, 0, 1); %! y2 = normpdf (x); %! plot (x, y1, "-b", x, y2, "-g") %! grid on %! xlim ([-5, 5]) %! ylim ([0, 0.9]) %! legend ({"half-normal with μ = 0, σ = 1", ... %! "standart normal (μ = 0, σ = 1)"}, "location", "northeast") %! title ("Half-normal against standard normal PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! y = [0, 0, 0.7979, 0.7041, 0.4839, 0]; %!assert (hnpdf ([x, NaN], 0, 1), [y, NaN], 1e-4) %!assert (hnpdf (x, 0, [-2, -1, 0, 1, 1, 1]), [nan(1,3), y([4:6])], 1e-4) ## Test class of input preserved %!assert (class (hncdf (single ([x, NaN]), 0, 1)), "single") %!assert (class (hncdf ([x, NaN], 0, single (1))), "single") %!assert (class (hncdf ([x, NaN], single (0), 1)), "single") ## Test input validation %!error hnpdf () %!error hnpdf (1) %!error hnpdf (1, 2) %!error ... %! hnpdf (1, ones (2), ones (3)) %!error ... %! hnpdf (ones (2), 1, ones (3)) %!error ... %! hnpdf (ones (2), ones (3), 1) %!error hnpdf (i, 2, 3) %!error hnpdf (1, i, 3) %!error hnpdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/hnrnd.m000066400000000000000000000146211475240274700214310ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} hnrnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} hnrnd (@var{mu}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} hnrnd (@var{mu}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} hnrnd (@var{mu}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the half-normal distribution. ## ## @code{@var{r} = hnrnd (@var{mu}, @var{sigma})} returns an array of random ## numbers chosen from the half-normal distribution with location parameter ## @var{mu} and scale parameter @var{sigma}. The size of @var{r} is the common ## size of @var{mu} and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{hnrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{hncdf, hninv, hnpdf, hnfit, hnlike, hnstat} ## @end deftypefn function r = hnrnd (mu, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("hnrnd: function called with too few input arguments."); endif ## Check for common size of MU, and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("hnrnd: MU and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (mu) || iscomplex (sigma)) error ("hnrnd: MU and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["hnrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("hnrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("hnrnd: MU and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from half-normal distribution r = abs (randn (sz, cls)) .* sigma + mu; ## Force output to NaN for invalid parameter SIGMA <= 0 k = (sigma <= 0); r(k) = NaN; endfunction ## Test output %!assert (size (hnrnd (1, 1, 1)), [1, 1]) %!assert (size (hnrnd (1, 1, 2)), [2, 2]) %!assert (size (hnrnd (1, 1, [2, 1])), [2, 1]) %!assert (size (hnrnd (1, zeros (2, 2))), [2, 2]) %!assert (size (hnrnd (1, ones (2, 1))), [2, 1]) %!assert (size (hnrnd (1, ones (2, 2))), [2, 2]) %!assert (size (hnrnd (ones (2, 1), 1)), [2, 1]) %!assert (size (hnrnd (ones (2, 2), 1)), [2, 2]) %!assert (size (hnrnd (1, 1, 3)), [3, 3]) %!assert (size (hnrnd (1, 1, [4 1])), [4, 1]) %!assert (size (hnrnd (1, 1, 4, 1)), [4, 1]) %!test %! r = hnrnd (1, [1, 0, -1]); %! assert (r([2:3]), [NaN, NaN]) ## Test class of input preserved %!assert (class (hnrnd (1, 0)), "double") %!assert (class (hnrnd (1, single (0))), "single") %!assert (class (hnrnd (1, single ([0 0]))), "single") %!assert (class (hnrnd (1, single (1))), "single") %!assert (class (hnrnd (1, single ([1 1]))), "single") %!assert (class (hnrnd (single (1), 1)), "single") %!assert (class (hnrnd (single ([1 1]), 1)), "single") ## Test input validation %!error hnrnd () %!error hnrnd (1) %!error ... %! hnrnd (ones (3), ones (2)) %!error ... %! hnrnd (ones (2), ones (3)) %!error hnrnd (i, 2, 3) %!error hnrnd (1, i, 3) %!error ... %! hnrnd (1, 2, -1) %!error ... %! hnrnd (1, 2, 1.2) %!error ... %! hnrnd (1, 2, ones (2)) %!error ... %! hnrnd (1, 2, [2 -1 2]) %!error ... %! hnrnd (1, 2, [2 0 2.5]) %!error ... %! hnrnd (1, 2, 2, -1, 5) %!error ... %! hnrnd (1, 2, 2, 1.5, 5) %!error ... %! hnrnd (2, ones (2), 3) %!error ... %! hnrnd (2, ones (2), [3, 2]) %!error ... %! hnrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/hygecdf.m000066400000000000000000000202361475240274700217300ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1997-2016 Kurt Hornik ## Copyright (C) 2022 Nicholas R. Jankowski ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} hygecdf (@var{x}, @var{m}, @var{k}, @var{n}) ## @deftypefnx {statistics} {@var{p} =} hygecdf (@var{x}, @var{m}, @var{k}, @var{n}, @qcode{"upper"}) ## ## Hypergeometric cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the hypergeometric distribution with parameters @var{m}, @var{k}, ## and @var{n}. The size of @var{p} is the common size of @var{x}, @var{m}, ## @var{k}, and @var{n}. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## This is the cumulative probability of obtaining not more than @var{x} marked ## items when randomly drawing a sample of size @var{n} without replacement from ## a population of total size @var{m} containing @var{k} marked items. The ## parameters @var{m}, @var{k}, and @var{n} must be positive integers with ## @var{k} and @var{n} not greater than @var{m}. ## ## @code{[@dots{}] = hygecdf (@var{x}, @var{m}, @var{k}, @var{n}, "upper")} ## computes the upper tail probability of the hypergeometric distribution with ## parameters @var{m}, @var{k}, and @var{n}, at the values in @var{x}. ## ## Further information about the hypergeometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Hypergeometric_distribution} ## ## @seealso{hygeinv, hygepdf, hygernd, hygestat} ## @end deftypefn function p = hygecdf (x, m, k, n, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("hygecdf: function called with too few input arguments."); endif ## Check for common size of X, T, k, and N if (! isscalar (x) || ! isscalar (m) || ! isscalar (k) || ! isscalar (n)) [retval, x, m, k, n] = common_size (x, m, k, n); if (retval > 0) error ("hygecdf: X, T, k, and N must be of common size or scalars."); endif endif ## Check for X, T, k, and N being reals if (iscomplex (x) || iscomplex (m) || iscomplex (k) || iscomplex (n)) error ("hygecdf: X, T, k, and N must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (m, "single") || isa (k, "single") || isa (n, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Check for "upper" flag if (nargin > 4 && strcmpi (uflag, "upper")) x = n - floor(x) - 1; k = m - k; elseif (nargin > 4 && ! strcmpi (uflag, "upper")) error ("hygecdf: invalid argument for upper tail."); endif ## Force 1 where required is_1 = (x >= n | x >= k); p(is_1) = 1; ## Force NaNs where required is_nan = (isnan (x) | isnan (m) | isnan (k) | isnan (n) | ... m < 0 | k < 0 | n < 0 | round (m) != m | round (k) != k | ... round (n) != n | n > m | k > m); p(is_nan) = NaN; ## Get values for which P = 0 is_0 = (m - k - n + x + 1 <= 0 | x < 0); ok = ! (is_1 | is_nan | is_0); ## Compute hypergeometric CDF if (any (ok(:))) ## For improved accuracy, compute the upper tail 1-p instead ## of the lower tail pfor x values that are larger than the mean lo = (x <= k .* n ./ m); ok_lo = ok & lo; if (any (ok_lo(:))) p(ok_lo) = localPDF (floor (x(ok_lo)), m(ok_lo), k(ok_lo), n(ok_lo)); endif ok_hi = ok & ! lo; if (any (ok_hi(:))) p(ok_hi) = 1 - localPDF (n(ok_hi) - floor (x(ok_hi)) - 1, ... m(ok_hi), m(ok_hi) - k(ok_hi), n(ok_hi)); endif endif endfunction function p = localPDF (x, m, k, n) HPDF = hygepdf (x, m, k, n); ## Compute hygecdf(x,m,k,n)/hygepdf(x,m,k,n) with a series ## whose terms can be computed recursively, backwards. xmax = max (x(:)); ybig = repmat((0:xmax)', 1, length (x)); xbig = repmat(x(:)', xmax + 1, 1); mbig = repmat(m(:)', xmax + 1, 1); kbig = repmat(k(:)', xmax + 1, 1); nbig = repmat(n(:)', xmax + 1, 1); terms = ((ybig+1) .* (mbig-kbig-nbig+ybig+1)) ./ ((nbig-ybig) .* (kbig-ybig)); terms(ybig >= xbig) = 1; terms = flip (cumprod (flip (terms))); terms(ybig > xbig) = 0; ratio = sum(terms,1); ratio = reshape(ratio,size(x)); p = ratio.*HPDF; ## Correct round-off errors p(p > 1) = 1; endfunction %!demo %! ## Plot various CDFs from the hypergeometric distribution %! x = 0:60; %! p1 = hygecdf (x, 500, 50, 100); %! p2 = hygecdf (x, 500, 60, 200); %! p3 = hygecdf (x, 500, 70, 300); %! plot (x, p1, "*b", x, p2, "*g", x, p3, "*r") %! grid on %! xlim ([0, 60]) %! legend ({"m = 500, k = 50, n = 100", "m = 500, k = 60, n = 200", ... %! "m = 500, k = 70, n = 300"}, "location", "southeast") %! title ("Hypergeometric CDF") %! xlabel ("values in x (number of successes)") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1 0 1 2 3]; %! y = [0 1/6 5/6 1 1]; %!assert (hygecdf (x, 4*ones (1,5), 2, 2), y, 5*eps) %!assert (hygecdf (x, 4, 2*ones (1,5), 2), y, 5*eps) %!assert (hygecdf (x, 4, 2, 2*ones (1,5)), y, 5*eps) %!assert (hygecdf (x, 4*[1 -1 NaN 1.1 1], 2, 2), [y(1) NaN NaN NaN y(5)], 5*eps) %!assert (hygecdf (x, 4*[1 -1 NaN 1.1 1], 2, 2, "upper"), ... %! [y(5) NaN NaN NaN y(1)], 5*eps) %!assert (hygecdf (x, 4, 2*[1 -1 NaN 1.1 1], 2), [y(1) NaN NaN NaN y(5)], 5*eps) %!assert (hygecdf (x, 4, 2*[1 -1 NaN 1.1 1], 2, "upper"), ... %! [y(5) NaN NaN NaN y(1)], 5*eps) %!assert (hygecdf (x, 4, 5, 2), [NaN NaN NaN NaN NaN]) %!assert (hygecdf (x, 4, 2, 2*[1 -1 NaN 1.1 1]), [y(1) NaN NaN NaN y(5)], 5*eps) %!assert (hygecdf (x, 4, 2, 2*[1 -1 NaN 1.1 1], "upper"), ... %! [y(5) NaN NaN NaN y(1)], 5*eps) %!assert (hygecdf (x, 4, 2, 5), [NaN NaN NaN NaN NaN]) %!assert (hygecdf ([x(1:2) NaN x(4:5)], 4, 2, 2), [y(1:2) NaN y(4:5)], 5*eps) %!test %! p = hygecdf (x, 10, [1 2 3 4 5], 2, "upper"); %! assert (p, [1, 34/90, 2/30, 0, 0], 10*eps); %!test %! p = hygecdf (2*x, 10, [1 2 3 4 5], 2, "upper"); %! assert (p, [1, 34/90, 0, 0, 0], 10*eps); ## Test class of input preserved %!assert (hygecdf ([x, NaN], 4, 2, 2), [y, NaN], 5*eps) %!assert (hygecdf (single ([x, NaN]), 4, 2, 2), single ([y, NaN]), ... %! eps ("single")) %!assert (hygecdf ([x, NaN], single (4), 2, 2), single ([y, NaN]), ... %! eps ("single")) %!assert (hygecdf ([x, NaN], 4, single (2), 2), single ([y, NaN]), ... %! eps ("single")) %!assert (hygecdf ([x, NaN], 4, 2, single (2)), single ([y, NaN]), ... %! eps ("single")) ## Test input validation %!error hygecdf () %!error hygecdf (1) %!error hygecdf (1,2) %!error hygecdf (1,2,3) %!error hygecdf (1,2,3,4,5) %!error hygecdf (1,2,3,4,"uper") %!error ... %! hygecdf (ones (2), ones (3), 1, 1) %!error ... %! hygecdf (1, ones (2), ones (3), 1) %!error ... %! hygecdf (1, 1, ones (2), ones (3)) %!error hygecdf (i, 2, 2, 2) %!error hygecdf (2, i, 2, 2) %!error hygecdf (2, 2, i, 2) %!error hygecdf (2, 2, 2, i) statistics-release-1.7.3/inst/dist_fun/hygeinv.m000066400000000000000000000153451475240274700217750ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1997-2016 Kurt Hornik ## Copyright (C) 2022 Nicholas R. Jankowski ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} hygeinv (@var{p}, @var{m}, @var{k}, @var{n}) ## ## Inverse of the hypergeometric cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the hypergeometric distribution with parameters @var{m}, @var{k}, and @var{n}. ## The size of @var{x} is the common size of @var{p}, @var{m}, @var{k}, and ## @var{n}. A scalar input functions as a constant matrix of the same size as ## the other inputs. ## ## This is the number of drawn marked items @var{x} given a probability @var{p}, ## when randomly drawing a sample of size @var{n} without replacement from a ## population of total size @var{m} containing @var{k} marked items. The ## parameters @var{m}, @var{k}, and @var{n} must be positive integers with ## @var{k} and @var{n} not greater than @var{m}. ## ## Further information about the hypergeometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Hypergeometric_distribution} ## ## @seealso{hygeinv, hygepdf, hygernd, hygestat} ## @end deftypefn function x = hygeinv (p, m, k, n) ## Check for valid number of input arguments if (nargin < 4) error ("hygeinv: function called with too few input arguments."); endif ## Check for common size of P, T, M, and N if (! isscalar (p) || ! isscalar (m) || ! isscalar (k) || ! isscalar (n)) [retval, p, m, k, n] = common_size (p, m, k, n); if (retval > 0) error ("hygeinv: P, T, M, and N must be of common size or scalars."); endif endif ## Check for P, T, M, and N being reals if (iscomplex (p) || iscomplex (m) || iscomplex (k) || iscomplex (n)) error ("hygeinv: P, T, M, and N must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (m, "single") || isa (k, "single") || isa (n, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ok = ((m >= 0) & (k >= 0) & (n > 0) & (k <= m) & (n <= m) & (m == fix (m)) & (k == fix (k)) & (n == fix (n))); if (isscalar (m)) if (ok) x = discrete_inv (p, 0 : n, hygepdf (0 : n, m, k, n)); x(p == 0) = 0; # Hack to return correct value for start of distribution endif else p_0 = (p == 0); x (ok & p_0) = 0; # set any p=0 to 0 if not already set to output NaN p_0 = (p == 1); x (ok & p_0) = n(ok & p_0); ok &= (p>0 & p<1); # remove 0's and p's outside (0,1), leave unfilled as NaN if (any (ok(:))) n = n(ok); v = 0 : max (n(:)); ## Manually perform discrete_inv to enable vectorizing with array input p_tmp = cumsum (hygepdf (v, m(ok), k(ok), n, "vectorexpand"), 2); sz_p = size (p_tmp); end_locs = sub2ind (sz_p, [1 : numel(n)]', n(:) + 1); ## Manual row-wise vectorization of lookup, which returns index of element ## less than or equal to test value, zero if test value less than lowest ## number, and max index if greater than highest number. operated on ## flipped p_tmp, adjusting for different vector lengths in array rows. p_tmp = (p_tmp ./ p_tmp(end_locs))(:, end:-1:1) - p(ok)(:); p_tmp(p_tmp>=0) = NaN; [p_match, p_match_idx] = max (p_tmp, [], 2); p_match_idx(isnan(p_match)) = v(end) + 2; x(ok) = v(v(end) - p_match_idx + 3); endif endif endfunction %!demo %! ## Plot various iCDFs from the hypergeometric distribution %! p = 0.001:0.001:0.999; %! x1 = hygeinv (p, 500, 50, 100); %! x2 = hygeinv (p, 500, 60, 200); %! x3 = hygeinv (p, 500, 70, 300); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r") %! grid on %! ylim ([0, 60]) %! legend ({"m = 500, k = 50, n = 100", "m = 500, k = 60, n = 200", ... %! "m = 500, k = 70, n = 300"}, "location", "northwest") %! title ("Hypergeometric iCDF") %! xlabel ("probability") %! ylabel ("values in p (number of successes)") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (hygeinv (p, 4*ones (1,5), 2*ones (1,5), 2*ones (1,5)), [NaN 0 1 2 NaN]) %!assert (hygeinv (p, 4*ones (1,5), 2, 2), [NaN 0 1 2 NaN]) %!assert (hygeinv (p, 4, 2*ones (1,5), 2), [NaN 0 1 2 NaN]) %!assert (hygeinv (p, 4, 2, 2*ones (1,5)), [NaN 0 1 2 NaN]) %!assert (hygeinv (p, 4*[1 -1 NaN 1.1 1], 2, 2), [NaN NaN NaN NaN NaN]) %!assert (hygeinv (p, 4, 2*[1 -1 NaN 1.1 1], 2), [NaN NaN NaN NaN NaN]) %!assert (hygeinv (p, 4, 5, 2), [NaN NaN NaN NaN NaN]) %!assert (hygeinv (p, 4, 2, 2*[1 -1 NaN 1.1 1]), [NaN NaN NaN NaN NaN]) %!assert (hygeinv (p, 4, 2, 5), [NaN NaN NaN NaN NaN]) %!assert (hygeinv ([p(1:2) NaN p(4:5)], 4, 2, 2), [NaN 0 NaN 2 NaN]) ## Test class of input preserved %!assert (hygeinv ([p, NaN], 4, 2, 2), [NaN 0 1 2 NaN NaN]) %!assert (hygeinv (single ([p, NaN]), 4, 2, 2), single ([NaN 0 1 2 NaN NaN])) %!assert (hygeinv ([p, NaN], single (4), 2, 2), single ([NaN 0 1 2 NaN NaN])) %!assert (hygeinv ([p, NaN], 4, single (2), 2), single ([NaN 0 1 2 NaN NaN])) %!assert (hygeinv ([p, NaN], 4, 2, single (2)), single ([NaN 0 1 2 NaN NaN])) ## Test input validation %!error hygeinv () %!error hygeinv (1) %!error hygeinv (1,2) %!error hygeinv (1,2,3) %!error ... %! hygeinv (ones (2), ones (3), 1, 1) %!error ... %! hygeinv (1, ones (2), ones (3), 1) %!error ... %! hygeinv (1, 1, ones (2), ones (3)) %!error hygeinv (i, 2, 2, 2) %!error hygeinv (2, i, 2, 2) %!error hygeinv (2, 2, i, 2) %!error hygeinv (2, 2, 2, i) statistics-release-1.7.3/inst/dist_fun/hygepdf.m000066400000000000000000000205561475240274700217520ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1996-2016 Kurt Hornik ## Copyright (C) 2022 Nicholas R. Jankowski ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} hygepdf (@var{x}, @var{m}, @var{k}, @var{n}) ## @deftypefnx {statistics} {@var{y} =} hygepdf (@dots{}, @qcode{"vectorexpand"}) ## ## Hypergeometric probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the hypergeometric distribution with parameters @var{m}, @var{k}, and ## @var{n}. The size of @var{y} is the common size of @var{x}, @var{m}, ## @var{k}, and @var{n}. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## This is the probability of obtaining @var{x} marked items when randomly ## drawing a sample of size @var{n} without replacement from a population of ## total size @var{m} containing @var{k} marked items. The parameters @var{m}, ## @var{k}, and @var{n} must be positive integers with @var{k} and @var{n} not ## greater than @var{m}. ## ## If the optional parameter @qcode{vectorexpand} is provided, @var{x} may be an ## array with size different from parameters @var{m}, @var{k}, and @var{n} ## (which must still be of a common size or scalar). Each element of @var{x} ## will be evaluated against each set of parameters @var{m}, @var{k}, and ## @var{n} in columnwise order. The output @var{y} will be an array of size ## @qcode{@var{r} x @var{s}}, where @qcode{@var{r} = numel (@var{m})}, and ## @qcode{@var{s} = numel (@var{x})}. ## ## Further information about the hypergeometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Hypergeometric_distribution} ## ## @seealso{hygecdf, hygeinv, hygernd, hygestat} ## @end deftypefn function y = hygepdf (x, m, k, n, vect_expand) ## Check for valid number of input arguments if (nargin < 4) error ("hygepdf: function called with too few input arguments."); endif ## Check for X, T, M, and N being reals if (iscomplex (x) || iscomplex (m) || iscomplex (k) || iscomplex (n)) error ("hygepdf: X, T, M, and N must not be complex."); endif ## Check for 5th argument or add default if (nargin < 5) vect_expand = []; endif if strcmpi (vect_expand, "vectorexpand") ## Expansion to improve vectorization of hyge calling functions. ## Project inputs over a 2D array with x(:) as a row vector and m,k,n as ## a column vector. each y(i,j) is hygepdf(x(j), m(i), k(i), n(i)) ## Following expansion, remainder of algorithm processes as normal. if (! isscalar (m) || ! isscalar (k) || ! isscalar (n)) [retval, m, k, n] = common_size (m, k, n); if (retval > 0) error ("hygepdf: T, M, and N must be of common size or scalars."); endif ## Ensure col vectors before expansion m = m(:); k = k(:); n = n(:); endif ## Expand x,m,k,n to arrays of size numel(m) x numel(x) sz = [numel(m), numel(x)]; x = x(:)'; # ensure row vector before expansion x = x(ones (sz(1), 1), :); m = m(:, ones (sz(2), 1)); k = k(:, ones (sz(2), 1)); n = n(:, ones (sz(2), 1)); else ## Check for common size of X, T, M, and N if (! isscalar (m) || ! isscalar (k) || ! isscalar (n)) [retval, x, m, k, n] = common_size (x, m, k, n); if (retval > 0) error ("hygepdf: X, T, M, and N must be of common size or scalars."); endif endif sz = size (x); endif ## Check for class type if (isa (x, "single") || isa (m, "single") || isa (k, "single") || isa (n, "single")) y = zeros (sz, "single"); else y = zeros (sz); endif ## Everything in nel gives NaN nel = (isnan (x) | (m < 0) | (k < 0) | (n <= 0) | (k > m) | (n > m) | (m != fix (m)) | (k != fix (k)) | (n != fix (n))); ## Everything in zel gives 0 unless in nel zel = ((x != fix (x)) | (x < 0) | (x > k) | (n < x) | (n-x > m-k)); y(nel) = NaN; ok = ! nel & ! zel; if (any (ok(:))) if (isscalar (m)) y(ok) = exp (gammaln (k+1) - gammaln (k-x(ok)+1) - gammaln (x(ok)+1) + ... gammaln (m-k+1) - gammaln (m-k-n+x(ok)+1) - ... gammaln (n-x(ok)+1) - gammaln (m+1) + gammaln (m-n+1) + ... gammaln (n+1)); else y(ok) = exp (gammaln (k(ok)+1) - gammaln (k(ok)-x(ok)+1) - ... gammaln (x(ok)+1) + gammaln (m(ok)-k(ok)+1) - ... gammaln (m(ok)-k(ok)-n(ok)+x(ok)+1) - ... gammaln (n(ok)-x(ok)+1) - gammaln (m(ok)+1) + ... gammaln (m(ok)-n(ok)+1) + gammaln (n(ok)+1)); endif endif endfunction %!demo %! ## Plot various PDFs from the hypergeometric distribution %! x = 0:60; %! y1 = hygepdf (x, 500, 50, 100); %! y2 = hygepdf (x, 500, 60, 200); %! y3 = hygepdf (x, 500, 70, 300); %! plot (x, y1, "*b", x, y2, "*g", x, y3, "*r") %! grid on %! xlim ([0, 60]) %! ylim ([0, 0.18]) %! legend ({"m = 500, k = 50, μ = 100", "m = 500, k = 60, μ = 200", ... %! "m = 500, k = 70, μ = 300"}, "location", "northeast") %! title ("Hypergeometric PDF") %! xlabel ("values in x (number of successes)") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 1 2 3]; %! y = [0 1/6 4/6 1/6 0]; %!assert (hygepdf (x, 4 * ones (1, 5), 2, 2), y, 3 * eps) %!assert (hygepdf (x, 4, 2 * ones (1, 5), 2), y, 3 * eps) %!assert (hygepdf (x, 4, 2, 2 * ones (1, 5)), y, 3 * eps) %!assert (hygepdf (x, 4 * [1, -1, NaN, 1.1, 1], 2, 2), [0, NaN, NaN, NaN, 0]) %!assert (hygepdf (x, 4, 2 * [1, -1, NaN, 1.1, 1], 2), [0, NaN, NaN, NaN, 0]) %!assert (hygepdf (x, 4, 5, 2), [NaN, NaN, NaN, NaN, NaN], 3 * eps) %!assert (hygepdf (x, 4, 2, 2 * [1, -1, NaN, 1.1, 1]), [0, NaN, NaN, NaN, 0]) %!assert (hygepdf (x, 4, 2, 5), [NaN, NaN, NaN, NaN, NaN], 3 * eps) %!assert (hygepdf ([x, NaN], 4, 2, 2), [y, NaN], 3 * eps) ## Test class of input preserved %!assert (hygepdf (single ([x, NaN]), 4, 2, 2), single ([y, NaN]), eps ("single")) %!assert (hygepdf ([x, NaN], single (4), 2, 2), single ([y, NaN]), eps ("single")) %!assert (hygepdf ([x, NaN], 4, single (2), 2), single ([y, NaN]), eps ("single")) %!assert (hygepdf ([x, NaN], 4, 2, single (2)), single ([y, NaN]), eps ("single")) ## Test vector expansion %!test %! z = zeros(3,5); %! z([4,5,6,8,9,12]) = [1, 0.5, 1/6, 0.5, 2/3, 1/6]; %! assert (hygepdf (x, 4, [0, 1, 2], 2, "vectorexpand"), z, 3 * eps); %! assert (hygepdf (x, 4, [0, 1, 2]', 2, "vectorexpand"), z, 3 * eps); %! assert (hygepdf (x', 4, [0, 1, 2], 2, "vectorexpand"), z, 3 * eps); %! assert (hygepdf (2, 4, [0 ,1, 2], 2, "vectorexpand"), z(:,4), 3 * eps); %! assert (hygepdf (x, 4, 1, 2, "vectorexpand"), z(2,:), 3 *eps); %! assert (hygepdf ([NaN, x], 4, [0 1 2]', 2, "vectorexpand"), [NaN(3, 1), z], 3 * eps); ## Test input validation %!error hygepdf () %!error hygepdf (1) %!error hygepdf (1,2) %!error hygepdf (1,2,3) %!error ... %! hygepdf (1, ones (3), ones (2), ones (2)) %!error ... %! hygepdf (1, ones (2), ones (3), ones (2)) %!error ... %! hygepdf (1, ones (2), ones (2), ones (3)) %!error hygepdf (i, 2, 2, 2) %!error hygepdf (2, i, 2, 2) %!error hygepdf (2, 2, i, 2) %!error hygepdf (2, 2, 2, i) statistics-release-1.7.3/inst/dist_fun/hygernd.m000066400000000000000000000177271475240274700217720ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1997-2016 Kurt Hornik ## Copyright (C) 2022 Nicholas R. Jankowski ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} hygernd (@var{m}, @var{k}, @var{n}) ## @deftypefnx {statistics} {@var{r} =} hygernd (@var{m}, @var{k}, @var{n}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} hygernd (@var{m}, @var{k}, @var{n}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} hygernd (@var{m}, @var{k}, @var{n}, [@var{sz}]) ## ## Random arrays from the hypergeometric distribution. ## ## @code{@var{r} = hygernd ((@var{m}, @var{k}, @var{n}} returns an array of ## random numbers chosen from the hypergeometric distribution with parameters ## @var{m}, @var{k}, and @var{n}. The size of @var{r} is the common size of ## @var{m}, @var{k}, and @var{n}. A scalar input functions as a constant matrix ## of the same size as the other inputs. ## ## The parameters @var{m}, @var{k}, and @var{n} must be positive integers ## with @var{k} and @var{n} not greater than @var{m}. ## ## When called with a single size argument, @code{hygernd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the hypergeometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Hypergeometric_distribution} ## ## @seealso{hygecdf, hygeinv, hygepdf, hygestat} ## @end deftypefn function r = hygernd (m, k, n, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("hygernd: function called with too few input arguments."); endif ## Check for common size of T, M, and N if (! isscalar (m) || ! isscalar (k) || ! isscalar (n)) [retval, m, k, n] = common_size (m, k, n); if (retval > 0) error ("hygernd: T, M, and N must be of common size or scalars."); endif endif ## Check for T, M, and N being reals if (iscomplex (m) || iscomplex (k) || iscomplex (n)) error ("hygernd: T, M, and N must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (m); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["hygernd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("hygernd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (m) && ! isequal (size (m), sz)) error ("hygernd: T, M, and N must be scalars or of size SZ."); endif ## Check for class type if (isa (m, "single") || isa (k, "single") || isa (n, "single")) cls = "single"; else cls = "double"; endif ok = ((m >= 0) & (k >= 0) & (n > 0) & (k <= m) & (n <= m) & (m == fix (m)) & (k == fix (k)) & (n == fix (n))); ## Generate random sample from the hypergeometric distribution if (isscalar (m)) if (ok) v = 0:n; p = hygepdf (v, m, k, n); r = v(lookup (cumsum (p(1:end-1)) / sum (p), rand (sz)) + 1); r = reshape (r, sz); if (strcmp (cls, "single")) r = single (r); endif else r = NaN (sz, cls); endif else r = NaN (sz, cls); n = n(ok); num_n = numel (n); v = 0 : max (n(:)); p = cumsum (hygepdf (v, m(ok), k(ok), n, "vectorexpand"), 2); ## Manual row-wise vectorization of lookup, which returns index of element ## less than or equal to test value, zero if test value is less than lowest ## number, and max index if greater than highest number. end_locs = sub2ind (size (p), [1 : num_n]', n(:) + 1); p = (p ./ p(end_locs)) - rand (num_n, 1); p(p>=0) = NaN; # NaN values ignored by max [p_match, p_match_idx] = max (p, [], 2); p_match_idx(isnan(p_match)) = 0; # rand < min(p) gives NaN, reset to 0 r(ok) = v(p_match_idx + 1); endif endfunction ## Test output %!assert (size (hygernd (4,2,2)), [1, 1]) %!assert (size (hygernd (4*ones (2,1), 2,2)), [2, 1]) %!assert (size (hygernd (4*ones (2,2), 2,2)), [2, 2]) %!assert (size (hygernd (4, 2*ones (2,1), 2)), [2, 1]) %!assert (size (hygernd (4, 2*ones (2,2), 2)), [2, 2]) %!assert (size (hygernd (4, 2, 2*ones (2,1))), [2, 1]) %!assert (size (hygernd (4, 2, 2*ones (2,2))), [2, 2]) %!assert (size (hygernd (4, 2, 2, 3)), [3, 3]) %!assert (size (hygernd (4, 2, 2, [4 1])), [4, 1]) %!assert (size (hygernd (4, 2, 2, 4, 1)), [4, 1]) ## Test class of input preserved %!assert (class (hygernd (4,2,2)), "double") %!assert (class (hygernd (single (4),2,2)), "single") %!assert (class (hygernd (single ([4 4]),2,2)), "single") %!assert (class (hygernd (4,single (2),2)), "single") %!assert (class (hygernd (4,single ([2 2]),2)), "single") %!assert (class (hygernd (4,2,single (2))), "single") %!assert (class (hygernd (4,2,single ([2 2]))), "single") ## Test input validation %!error hygernd () %!error hygernd (1) %!error hygernd (1, 2) %!error ... %! hygernd (ones (3), ones (2), ones (2)) %!error ... %! hygernd (ones (2), ones (3), ones (2)) %!error ... %! hygernd (ones (2), ones (2), ones (3)) %!error hygernd (i, 2, 3) %!error hygernd (1, i, 3) %!error hygernd (1, 2, i) %!error ... %! hygernd (1, 2, 3, -1) %!error ... %! hygernd (1, 2, 3, 1.2) %!error ... %! hygernd (1, 2, 3, ones (2)) %!error ... %! hygernd (1, 2, 3, [2 -1 2]) %!error ... %! hygernd (1, 2, 3, [2 0 2.5]) %!error ... %! hygernd (1, 2, 3, 2, -1, 5) %!error ... %! hygernd (1, 2, 3, 2, 1.5, 5) %!error ... %! hygernd (2, ones (2), 2, 3) %!error ... %! hygernd (2, ones (2), 2, [3, 2]) %!error ... %! hygernd (2, ones (2), 2, 3, 2) statistics-release-1.7.3/inst/dist_fun/invgcdf.m000066400000000000000000000143661475240274700217460ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} invgcdf (@var{x}, @var{mu}, @var{lambda}) ## @deftypefnx {statistics} {@var{p} =} invgcdf (@var{x}, @var{mu}, @var{lambda}, @qcode{"upper"}) ## ## Inverse Gaussian cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the inverse Gaussian distribution with scale parameter @var{mu} and ## shape parameter @var{lambda}. The size of @var{p} is the common size of ## @var{x}, @var{mu} and @var{lambda}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## @code{@var{p} = invgcdf (@var{x}, @var{mu}, @var{lambda}, "upper")} computes ## the upper tail probability of the inverse Gaussian distribution with ## parameters @var{mu} and @var{lambda}, at the values in @var{x}. ## ## The inverse Gaussian CDF is only defined for @qcode{@var{mu} > 0} and ## @qcode{@var{lambda} > 0}. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invginv, invgpdf, invgrnd, invgfit, invglike, invgstat} ## @end deftypefn function p = invgcdf (x, mu, lambda, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("invgcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("invgcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and LAMBDA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (lambda)) [err, x, mu, lambda] = common_size (x, mu, lambda); if (err > 0) error ("invgcdf: X, MU, and LAMBDA must be of common size or scalars."); endif endif ## Check for X, MU, and LAMBDA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (lambda)) error ("invgcdf: X, MU, and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (lambda, "single")) is_class = "single"; else is_class = "double"; endif ## Prepare output p = zeros (size (x), is_class); ## Return NaNs for out of range values of MU and LAMBDA parameters mu(mu <= 0) = NaN; lambda(lambda <= 0) = NaN; ## Check for valid support of X is_zero = (x <= 0); x(is_zero) = realmin; is_inf = (x == Inf); ## Calculate z1, z2 z1 = sqrt (lambda ./ x) .* (x ./ mu - 1); z2 = -sqrt (lambda ./ x) .* (x ./ mu + 1); ## Compute the CDF if the inverse Gaussian if (uflag) p = 0.5 .* erfc (z1 ./ sqrt (2)) - ... exp (2 .* lambda ./ mu) .* 0.5 .* erfc (-z2 ./ sqrt (2)); p(is_zero) = 1; p(is_inf) = 0; else p = 0.5 .* erfc (-z1 ./ sqrt (2)) + ... exp (2 .* lambda ./ mu) .* 0.5 .* erfc (-z2 ./ sqrt (2)); p(is_zero) = 0; p(is_inf) = 1; endif endfunction %!demo %! ## Plot various CDFs from the inverse Gaussian distribution %! x = 0:0.001:3; %! p1 = invgcdf (x, 1, 0.2); %! p2 = invgcdf (x, 1, 1); %! p3 = invgcdf (x, 1, 3); %! p4 = invgcdf (x, 3, 0.2); %! p5 = invgcdf (x, 3, 1); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-y") %! grid on %! xlim ([0, 3]) %! legend ({"μ = 1, σ = 0.2", "μ = 1, σ = 1", "μ = 1, σ = 3", ... %! "μ = 3, σ = 0.2", "μ = 3, σ = 1"}, "location", "southeast") %! title ("Inverse Gaussian CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, p1, p1u, y2, y2u, y3, y3u %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! p1 = [0, 0, 0, 0.3650, 0.6681, 1]; %! p1u = [1, 1, 1, 0.6350, 0.3319, 0]; %!assert (invgcdf (x, ones (1,6), ones (1,6)), p1, 1e-4) %!assert (invgcdf (x, 1, 1), p1, 1e-4) %!assert (invgcdf (x, 1, ones (1,6)), p1, 1e-4) %!assert (invgcdf (x, ones (1,6), 1), p1, 1e-4) %!assert (invgcdf (x, 1, [1, 1, 1, NaN, 1, 1]), [p1(1:3), NaN, p1(5:6)], 1e-4) %!assert (invgcdf (x, [1, 1, 1, NaN, 1, 1], 1), [p1(1:3), NaN, p1(5:6)], 1e-4) %!assert (invgcdf ([x(1:3), NaN, x(5:6)], 1, 1), [p1(1:3), NaN, p1(5:6)], 1e-4) %!assert (invgcdf (x, ones (1,6), ones (1,6), "upper"), p1u, 1e-4) %!assert (invgcdf (x, 1, 1, "upper"), p1u, 1e-4) %!assert (invgcdf (x, 1, ones (1,6), "upper"), p1u, 1e-4) %!assert (invgcdf (x, ones (1,6), 1, "upper"), p1u, 1e-4) ## Test class of input preserved %!assert (class (invgcdf (single ([x, NaN]), 1, 1)), "single") %!assert (class (invgcdf ([x, NaN], 1, single (1))), "single") %!assert (class (invgcdf ([x, NaN], single (1), 1)), "single") ## Test input validation %!error invgcdf () %!error invgcdf (1) %!error invgcdf (1, 2) %!error invgcdf (1, 2, 3, "tail") %!error invgcdf (1, 2, 3, 5) %!error ... %! invgcdf (ones (3), ones (2), ones(2)) %!error ... %! invgcdf (ones (2), ones (3), ones(2)) %!error ... %! invgcdf (ones (2), ones (2), ones(3)) %!error invgcdf (i, 2, 3) %!error invgcdf (1, i, 3) %!error invgcdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/invginv.m000066400000000000000000000150671475240274700220050ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} invginv (@var{p}, @var{mu}, @var{lambda}) ## ## Inverse of the inverse Gaussian cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the inverse Gaussian distribution with scale parameter @var{mu} and shape ## parameter @var{lambda}. The size of @var{x} is the common size of @var{p}, ## @var{mu}, and @var{lambda}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## The inverse Gaussian CDF is only defined for @qcode{@var{mu} > 0} and ## @qcode{@var{lambda} > 0}. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invgcdf, invgpdf, invgrnd, invgfit, invglike, invgstat} ## @end deftypefn function x = invginv (p, mu, lambda) ## Check for valid number of input arguments if (nargin < 3) error ("invginv: function called with too few input arguments."); endif ## Check for common size of P, MU, and LAMBDA if (! isscalar (p) || ! isscalar (mu) || ! isscalar(lambda)) [retval, p, mu, lambda] = common_size (p, mu, lambda); vec = true; if (retval > 0) error ("invginv: P, MU, and LAMBDA must be of common size or scalars."); endif else vec = false; endif ## Check for X, MU, and LAMBDA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (lambda)) error ("invginv: P, MU, and LAMBDA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (lambda, "single")); x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Return NaNs for out of range values of MU and LAMBDA parameters mu(mu <= 0) = NaN; lambda(lambda <= 0) = NaN; ## Find valid parameters and p-values (handle edges cases below) validmulambda = (mu > 0) & (lambda > 0) & (lambda < Inf); validp_values = (validmulambda & (p > 0) & (p < 1)); valid_all = all (validp_values(:)); valid_any = any (validp_values(:)); ## Handle edges cases here if (! valid_all) x(p == 0 & validmulambda) = 0; x(p == 1 & validmulambda) = Inf; ## Keep valid cases (if any left) if (valid_any) if (vec) p = p(validp_values); mu = mu(validp_values); lambda = lambda(validp_values); endif else return; endif endif ## Apply Newton's Method to find a root of p = invgcdf(x,mu,lambda) ## Choose a starting guess for x0. Use quantiles from a lognormal ## distribution with the same mean (==1) and variance (==lambda0) lambda0 = lambda ./ mu; lognorm = log (1 ./ lambda0 + 1); mulnorm = -0.5 .* lognorm; x0 = exp (mulnorm - sqrt (2 .* lognorm) .* erfcinv (2 * p)); ## Set maximum iterations and tolerance for Newton's Method mit = 500; tol = eps (class (x0)) .^ (3/4); ## Get quantiles F = invgcdf (x0, 1, lambda0); dF = F - p; for it = 1:mit ## Compute the Newton step f = invgpdf (x0, 1, lambda0); h = dF ./ f; x0_1 = max (x0/10, min (10 * x0, x0 - h)); ## Check if tolerance is reached complete = (abs (h) <= tol * x0); if (all (complete(:))) x0 = x0_1; break endif ## Check for increasing error unless tolerance is reached dFold = dF; for j = 1:25 F = invgcdf (x0_1, 1, lambda0); dF = F - p; worse = (abs (dF) > abs (dFold)) & ! complete; if (! any (worse(:))) break endif x0_1(worse) = (x0(worse) + x0_1(worse)) / 2; endfor ## Update for next step x0 = x0_1; endfor ## Issue a warning for exceeding iterations or not converging to tolerance notconv = (abs(dF./F) > tol.^(2/3)); if (it > mit || any (notconv(:))) warning (strcat (["invginv: Newton's Method did not converge"], ... [" or exceeded maximum iterations."])); endif ## Apply the scale factor if (valid_all) x = x0 .* mu; else x(validp_values) = x0 .* mu; endif endfunction %!demo %! ## Plot various iCDFs from the inverse Gaussian distribution %! p = 0.001:0.001:0.999; %! x1 = invginv (p, 1, 0.2); %! x2 = invginv (p, 1, 1); %! x3 = invginv (p, 1, 3); %! x4 = invginv (p, 3, 0.2); %! x5 = invginv (p, 3, 1); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-y") %! grid on %! ylim ([0, 3]) %! legend ({"μ = 1, σ = 0.2", "μ = 1, σ = 1", "μ = 1, σ = 3", ... %! "μ = 3, σ = 0.2", "μ = 3, σ = 1"}, "location", "northwest") %! title ("Inverse Gaussian iCDF") %! xlabel ("probability") %! ylabel ("x") ## Test output %!shared p, x %! p = [0, 0.3829, 0.6827, 1]; %! x = [0, 0.5207, 1.0376, Inf]; %!assert (invginv (p, 1, 1), x, 1e-4); %!assert (invginv (p, 1, ones (1,4)), x, 1e-4); %!assert (invginv (p, 1, [-1, 0, 1, 1]), [NaN, NaN, x(3:4)], 1e-4) %!assert (invginv (p, [-1, 0, 1, 1], 1), [NaN, NaN, x(3:4)], 1e-4) ## Test class of input preserved %!assert (class (invginv (single ([p, NaN]), 0, 1)), "single") %!assert (class (invginv ([p, NaN], single (0), 1)), "single") %!assert (class (invginv ([p, NaN], 0, single (1))), "single") ## Test input validation %!error invginv (1) %!error invginv (1, 2) %!error ... %! invginv (1, ones (2), ones (3)) %!error ... %! invginv (ones (2), 1, ones (3)) %!error ... %! invginv (ones (2), ones (3), 1) %!error invginv (i, 2, 3) %!error invginv (1, i, 3) %!error invginv (1, 2, i) statistics-release-1.7.3/inst/dist_fun/invgpdf.m000066400000000000000000000113221475240274700217500ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} invgpdf (@var{x}, @var{mu}, @var{lambda}) ## ## Inverse Gaussian probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the inverse Gaussian distribution with scale parameter @var{mu} and shape ## parameter @var{lambda}. The size of @var{y} is the common size of @var{x}, ## @var{mu}, and @var{lambda}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## The inverse Gaussian CDF is only defined for @qcode{@var{mu} > 0} and ## @qcode{@var{lambda} > 0}. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invgcdf, invginv, invgrnd, invgfit, invglike, invgstat} ## @end deftypefn function y = invgpdf (x, mu, lambda) ## Check for valid number of input arguments if (nargin < 3) error ("invgpdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and LAMBDA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(lambda)) [retval, x, mu, lambda] = common_size (x, mu, lambda); if (retval > 0) error ("invgpdf: X, MU, and LAMBDA must be of common size or scalars."); endif endif ## Check for X, MU, and LAMBDA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (lambda)) error ("invgpdf: X, MU, and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (lambda, "single")); is_class = "single"; else is_class = "double"; endif ## Return NaNs for out of range values of MU and LAMBDA parameters mu(mu <= 0) = NaN; lambda(lambda <= 0) = NaN; ## Check for valid support of X is_zero = (x <= 0); x(is_zero) = realmin; ## Compute inverse Gaussian PDF y = sqrt (lambda ./ (2 .* pi .* x .^ 3)) .* ... exp (-0.5 .* lambda .* (x ./ mu - 2 + mu ./ x) ./ mu); ## Force zero for unsupported X but valid parameters k0 = is_zero & mu > 0 & lambda > 0; y(k0) = 0; ## Cast to appropriate class y = cast (y, is_class); endfunction %!demo %! ## Plot various PDFs from the inverse Gaussian distribution %! x = 0:0.001:3; %! y1 = invgpdf (x, 1, 0.2); %! y2 = invgpdf (x, 1, 1); %! y3 = invgpdf (x, 1, 3); %! y4 = invgpdf (x, 3, 0.2); %! y5 = invgpdf (x, 3, 1); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-y") %! grid on %! xlim ([0, 3]) %! ylim ([0, 3]) %! legend ({"μ = 1, σ = 0.2", "μ = 1, σ = 1", "μ = 1, σ = 3", ... %! "μ = 3, σ = 0.2", "μ = 3, σ = 1"}, "location", "northeast") %! title ("Inverse Gaussian PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! y = [0, 0, 0, 0.8788, 0.3989, 0]; %!assert (invgpdf ([x, NaN], 1, 1), [y, NaN], 1e-4) %!assert (invgpdf (x, 1, [-2, -1, 0, 1, 1, 1]), [nan(1,3), y([4:6])], 1e-4) ## Test class of input preserved %!assert (class (hncdf (single ([x, NaN]), 1, 1)), "single") %!assert (class (hncdf ([x, NaN], 1, single (1))), "single") %!assert (class (hncdf ([x, NaN], single (1), 1)), "single") ## Test input validation %!error invgpdf () %!error invgpdf (1) %!error invgpdf (1, 2) %!error ... %! invgpdf (1, ones (2), ones (3)) %!error ... %! invgpdf (ones (2), 1, ones (3)) %!error ... %! invgpdf (ones (2), ones (3), 1) %!error invgpdf (i, 2, 3) %!error invgpdf (1, i, 3) %!error invgpdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/invgrnd.m000066400000000000000000000160701475240274700217670ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} invgrnd (@var{mu}, @var{lambda}) ## @deftypefnx {statistics} {@var{r} =} invgrnd (@var{mu}, @var{lambda}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} invgrnd (@var{mu}, @var{lambda}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} invgrnd (@var{mu}, @var{lambda}, [@var{sz}]) ## ## Random arrays from the inverse Gaussian distribution. ## ## @code{@var{r} = invgrnd (@var{mu}, @var{lambda})} returns an array of random ## numbers chosen from the inverse Gaussian distribution with location parameter ## @var{mu} and scale parameter @var{lambda}. The size of @var{r} is the common ## size of @var{mu} and @var{lambda}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{invgrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## The inverse Gaussian CDF is only defined for @qcode{@var{mu} > 0} and ## @qcode{@var{lambda} > 0}. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invgcdf, invginv, invgpdf, invgfit, invglike, invgstat} ## @end deftypefn function r = invgrnd (mu, lambda, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("invgrnd: function called with too few input arguments."); endif ## Check for common size of MU, and LAMBDA if (! isscalar (mu) || ! isscalar (lambda)) [retval, mu, lambda] = common_size (mu, lambda); vec = true; if (retval > 0) error ("invgrnd: MU and LAMBDA must be of common size or scalars."); endif else vec = false; endif ## Check for X, MU, and LAMBDA being reals if (iscomplex (mu) || iscomplex (lambda)) error ("invgrnd: MU and LAMBDA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["invgrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("invgrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("invgrnd: MU and LAMBDA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (lambda, "single")) cls = "single"; else cls = "double"; endif ## Expand parameters (if needed) if (! vec) mu = repmat (mu, sz); lambda = repmat (lambda, sz); endif ## Generate random sample from inverse Gaussian distribution v = randn (sz, cls); y = v .^ 2; r = mu + (mu .^ 2 .* y) ./ (2 .* lambda) - (mu ./ (2 .* lambda)) .* ... sqrt (4 * mu .* lambda .* y + mu .* mu .* y .* y); inver = (rand (sz) .* (mu + r) > mu); r(inver) = mu(inver) .^2 ./ r(inver); ## Force output to NaN for invalid parameters MU and LAMBDA k = (mu <= 0 | lambda <= 0); r(k) = NaN; endfunction ## Test results %!assert (size (invgrnd (1, 1, 1)), [1, 1]) %!assert (size (invgrnd (1, 1, 2)), [2, 2]) %!assert (size (invgrnd (1, 1, [2, 1])), [2, 1]) %!assert (size (invgrnd (1, zeros (2, 2))), [2, 2]) %!assert (size (invgrnd (1, ones (2, 1))), [2, 1]) %!assert (size (invgrnd (1, ones (2, 2))), [2, 2]) %!assert (size (invgrnd (ones (2, 1), 1)), [2, 1]) %!assert (size (invgrnd (ones (2, 2), 1)), [2, 2]) %!assert (size (invgrnd (1, 1, 3)), [3, 3]) %!assert (size (invgrnd (1, 1, [4 1])), [4, 1]) %!assert (size (invgrnd (1, 1, 4, 1)), [4, 1]) %!test %! r = invgrnd (1, [1, 0, -1]); %! assert (r([2:3]), [NaN, NaN]) ## Test class of input preserved %!assert (class (invgrnd (1, 0)), "double") %!assert (class (invgrnd (1, single (0))), "single") %!assert (class (invgrnd (1, single ([0 0]))), "single") %!assert (class (invgrnd (1, single (1))), "single") %!assert (class (invgrnd (1, single ([1 1]))), "single") %!assert (class (invgrnd (single (1), 1)), "single") %!assert (class (invgrnd (single ([1 1]), 1)), "single") ## Test input validation %!error invgrnd () %!error invgrnd (1) %!error ... %! invgrnd (ones (3), ones (2)) %!error ... %! invgrnd (ones (2), ones (3)) %!error invgrnd (i, 2, 3) %!error invgrnd (1, i, 3) %!error ... %! invgrnd (1, 2, -1) %!error ... %! invgrnd (1, 2, 1.2) %!error ... %! invgrnd (1, 2, ones (2)) %!error ... %! invgrnd (1, 2, [2 -1 2]) %!error ... %! invgrnd (1, 2, [2 0 2.5]) %!error ... %! invgrnd (1, 2, 2, -1, 5) %!error ... %! invgrnd (1, 2, 2, 1.5, 5) %!error ... %! invgrnd (2, ones (2), 3) %!error ... %! invgrnd (2, ones (2), [3, 2]) %!error ... %! invgrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/iwishpdf.m000066400000000000000000000053551475240274700221410ustar00rootroot00000000000000## Copyright (C) 2013 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} iwishpdf (@var{W}, @var{Tau}, @var{df}, @var{log_y}=false) ## ## Compute the probability density function of the inverse Wishart distribution. ## ## Inputs: A @var{p} x @var{p} matrix @var{W} where to find the PDF and the ## @var{p} x @var{p} positive definite scale matrix @var{Tau} and scalar degrees ## of freedom parameter @var{df} characterizing the inverse Wishart distribution. ## (For the density to be finite, need @var{df} > (@var{p} - 1).) ## If the flag @var{log_y} is set, return the log probability density -- this ## helps avoid underflow when the numerical value of the density is very small. ## ## Output: @var{y} is the probability density of Wishart(@var{Sigma}, @var{df}) ## at @var{W}. ## ## @seealso{iwishrnd, wishpdf, wishrnd} ## @end deftypefn function y = iwishpdf (W, Tau, df, log_y=false) if (nargin < 3) print_usage (); endif p = size (Tau, 1); if (df <= (p - 1)) error ("iwishpdf: DF too small, no finite densities exist."); endif ## Calculate the logarithm of G_d(df/2), the multivariate gamma function g = (p * (p - 1) / 4) * log (pi); for i = 1:p g = g + log (gamma ((df + (1 - i)) / 2)); endfor C = chol (W); ## Use formulas for determinant of positive definite matrix for better ## efficiency and numerical accuracy logdet_W = 2*sum(log(diag(C))); logdet_Tau = 2*sum(log(diag(chol(Tau)))); y = -(df * p) / 2 * log (2) + (df / 2) * logdet_Tau - g ... -((df + p + 1) / 2) * logdet_W - trace (Tau * chol2inv (C)) / 2; if (! log_y) y = exp (y); endif endfunction ## Test results cross-checked against diwish function in R MCMCpack library %!assert(iwishpdf(4, 3, 3.1), 0.04226595, 1E-7); %!assert(iwishpdf([2 -0.3;-0.3 4], [1 0.3;0.3 1], 4), 1.60166e-05, 1E-10); %!assert(iwishpdf([6 2 5; 2 10 -5; 5 -5 25], ... %! [9 5 5; 5 10 -8; 5 -8 22], 5.1), 4.946831e-12, 1E-17); ## Test input validation %!error iwishpdf () %!error iwishpdf (1, 2) %!error iwishpdf (1, 2, 0) statistics-release-1.7.3/inst/dist_fun/iwishrnd.m000066400000000000000000000056621475240274700221540ustar00rootroot00000000000000## Copyright (C) 2013 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{W}, @var{DI}] =} iwishrnd (@var{Tau}, @var{df}, @var{DI}, @var{n}=1) ## ## Return a random matrix sampled from the inverse Wishart distribution with ## given parameters. ## ## Inputs: the @math{p x p} positive definite matrix @var{Tau} and scalar ## degrees of freedom parameter @var{df} (and optionally the transposed Cholesky ## factor @var{DI} of @var{Sigma} = @code{inv(Tau)}). ## ## @var{df} can be non-integer as long as @math{@var{df} > d} ## ## Output: a random @math{p x p} matrix @var{W} from the inverse ## Wishart(@var{Tau}, @var{df}) distribution. (@code{inv(W)} is from the ## Wishart(@code{inv(Tau)}, @var{df}) distribution.) If @var{n} > 1, ## then @var{W} is @var{p} x @var{p} x @var{n} and holds @var{n} such random ## matrices. (Optionally, the transposed Cholesky factor @var{DI} of @var{Sigma} ## is also returned.) ## ## Averaged across many samples, the mean of @var{W} should approach ## @var{Tau} / (@var{df} - @var{p} - 1). ## ## @subheading References ## ## @enumerate ## @item ## Yu-Cheng Ku and Peter Bloomfield (2010), Generating Random Wishart Matrices ## with Fractional Degrees of Freedom in OX, ## http://www.gwu.edu/~forcpgm/YuChengKu-030510final-WishartYu-ChengKu.pdf ## @end enumerate ## ## @seealso{iwishpdf, wishpdf, wishrnd} ## @end deftypefn function [W, DI] = iwishrnd (Tau, df, DI, n = 1) if (nargin < 2) print_usage (); endif if (nargin < 3 || isempty (DI)) try D = chol (inv (Tau)); catch error (strcat (["iwishrnd: Cholesky decomposition failed;"], ... [" TAU probably not positive definite."])); end_try_catch DI = D'; else D = DI'; endif w = wishrnd ([], df, D, n); if (n > 1) p = size (D, 1); W = nan (p, p, n); endif for i = 1:n W(:, :, i) = inv (w(:, :, i)); endfor endfunction %!assert(size (iwishrnd (1,2,1)), [1, 1]); %!assert(size (iwishrnd ([],2,1)), [1, 1]); %!assert(size (iwishrnd ([3 1; 1 3], 2.00001, [], 1)), [2, 2]); %!assert(size (iwishrnd (eye(2), 2, [], 3)), [2, 2, 3]); %% Test input validation %!error iwishrnd () %!error iwishrnd (1) %!error iwishrnd ([-3 1; 1 3],1) %!error iwishrnd ([1; 1],1) statistics-release-1.7.3/inst/dist_fun/jsucdf.m000066400000000000000000000045211475240274700215740ustar00rootroot00000000000000## Copyright (C) 2006 Frederick (Rick) A Niles ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} jsucdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} jsucdf (@var{x}, @var{alpha1}) ## @deftypefnx {statistics} {@var{p} =} jsucdf (@var{x}, @var{alpha1}, @var{alpha2}) ## ## Johnson SU cumulative distribution function (CDF). ## ## For each element of @var{x}, return the cumulative distribution functions ## (CDF) at @var{x} of the Johnson SU distribution with shape parameters ## @var{alpha1} and @var{alpha2}. The size of @var{p} is the common size of the ## input arguments @var{x}, @var{alpha1}, and @var{alpha2}. A scalar input ## functions as a constant matrix of the same size as the other ## ## Default values are @var{alpha1} = 1, @var{alpha2} = 1. ## ## @seealso{jsupdf} ## @end deftypefn function p = jsucdf (x, alpha1, alpha2) if (nargin < 1 || nargin > 3) print_usage; endif if (nargin == 1) alpha1 = 1; alpha2 = 1; elseif (nargin == 2) alpha2 = 1; endif if (! isscalar (x) || ! isscalar (alpha1) || ! isscalar(alpha2)) [retval, x, alpha1, alpha2] = common_size (x, alpha1, alpha2); if (retval > 0) error (strcat (["jsucdf: X, ALPHA1, and ALPHA2 must be of common"], ... [" size or scalars."])); endif endif one = ones (size (x)); p = stdnormal_cdf (alpha1 .* one + alpha2 .* log (x + sqrt (x .* x + one))); endfunction %!error jsucdf () %!error jsucdf (1, 2, 3, 4) %!error ... %! jsucdf (1, ones (2), ones (3)) statistics-release-1.7.3/inst/dist_fun/jsupdf.m000066400000000000000000000045611475240274700216150ustar00rootroot00000000000000## Copyright (C) 2006 Frederick (Rick) A Niles ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} jsupdf (@var{x}) ## @deftypefnx {statistics} {@var{y} =} jsupdf (@var{x}, @var{alpha1}) ## @deftypefnx {statistics} {@var{y} =} jsupdf (@var{x}, @var{alpha1}, @var{alpha2}) ## ## Johnson SU probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## at @var{x} of the Johnson SU distribution with shape parameters @var{alpha1} ## and @var{alpha2}. The size of @var{p} is the common size of the input ## arguments @var{x}, @var{alpha1}, and @var{alpha2}. A scalar input functions ## as a constant matrix of the same size as the other ## ## Default values are @var{alpha1} = 1, @var{alpha2} = 1. ## ## @seealso{jsucdf} ## @end deftypefn function y = jsupdf (x, alpha1, alpha2) if (nargin < 1 || nargin > 3) print_usage; endif if (nargin == 1) alpha1 = 1; alpha2 = 1; elseif (nargin == 2) alpha2 = 1; endif if (! isscalar (x) || ! isscalar (alpha1) || ! isscalar(alpha2)) [retval, x, alpha1, alpha2] = common_size (x, alpha1, alpha2); if (retval > 0) error (strcat (["jsupdf: X, ALPHA1, and ALPHA2 must be of common"], ... [" size or scalars."])); endif endif one = ones (size (x)); sr = sqrt (x .* x + one); y = (alpha2 ./ sr) .* ... stdnormal_pdf (alpha1 .* one + alpha2 .* log (x + sr)); endfunction %!error jsupdf () %!error jsupdf (1, 2, 3, 4) %!error ... %! jsupdf (1, ones (2), ones (3)) statistics-release-1.7.3/inst/dist_fun/laplacecdf.m000066400000000000000000000131121475240274700223700ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} laplacecdf (@var{x}, @var{mu}, @var{beta}) ## @deftypefnx {statistics} {@var{p} =} laplacecdf (@var{x}, @var{mu}, @var{beta}, @qcode{"upper"}) ## ## Laplace cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Laplace distribution with location parameter @var{mu} and scale ## parameter (i.e. "diversity") @var{beta}. The size of @var{p} is the common ## size of @var{x}, @var{mu}, and @var{beta}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{beta} > 0}. ## For @qcode{@var{beta} <= 0}, @qcode{NaN} is returned. ## ## @code{@var{p} = laplacecdf (@var{x}, @var{mu}, @var{beta}, "upper")} computes ## the upper tail probability of the Laplace distribution with parameters ## @var{mu} and @var{beta}, at the values in @var{x}. ## ## Further information about the Laplace distribution can be found at ## @url{https://en.wikipedia.org/wiki/Laplace_distribution} ## ## @seealso{laplaceinv, laplacepdf, laplacernd} ## @end deftypefn function p = laplacecdf (x, mu, beta, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("laplacecdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("laplacecdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and BETA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(beta)) [retval, x, mu, beta] = common_size (x, mu, beta); if (retval > 0) error (strcat (["laplacecdf: X, MU, and BETA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, MU, and BETA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (beta)) error ("laplacecdf: X, MU, and BETA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (beta, "single")); p = NaN (size (x), "single"); else p = NaN (size (x)); endif ## Find normal and edge cases k1 = (x == -Inf) & (beta > 0); k2 = (x == Inf) & (beta > 0); k = ! k1 & ! k2 & (beta > 0); ## Compute Laplace CDF if (uflag) p(k1) = 1; p(k2) = 0; p(k) = (1 + sign (-x(k) + mu(k)) .* ... (1 - exp (- abs (-x(k) + mu(k)) ./ beta(k)))) ./ 2; else p(k1) = 0; p(k2) = 1; p(k) = (1 + sign (x(k) - mu(k)) .* ... (1 - exp (- abs (x(k) - mu(k)) ./ beta(k)))) ./ 2; endif endfunction %!demo %! ## Plot various CDFs from the Laplace distribution %! x = -10:0.01:10; %! p1 = laplacecdf (x, 0, 1); %! p2 = laplacecdf (x, 0, 2); %! p3 = laplacecdf (x, 0, 4); %! p4 = laplacecdf (x, -5, 4); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! xlim ([-10, 10]) %! legend ({"μ = 0, β = 1", "μ = 0, β = 2", ... %! "μ = 0, β = 4", "μ = -5, β = 4"}, "location", "southeast") %! title ("Laplace CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-Inf, -log(2), 0, log(2), Inf]; %! y = [0, 1/4, 1/2, 3/4, 1]; %!assert (laplacecdf ([x, NaN], 0, 1), [y, NaN]) %!assert (laplacecdf (x, 0, [-2, -1, 0, 1, 2]), [nan(1, 3), 0.75, 1]) ## Test class of input preserved %!assert (laplacecdf (single ([x, NaN]), 0, 1), single ([y, NaN]), eps ("single")) %!assert (laplacecdf ([x, NaN], single (0), 1), single ([y, NaN]), eps ("single")) %!assert (laplacecdf ([x, NaN], 0, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error laplacecdf () %!error laplacecdf (1) %!error ... %! laplacecdf (1, 2) %!error ... %! laplacecdf (1, 2, 3, 4, 5) %!error laplacecdf (1, 2, 3, "tail") %!error laplacecdf (1, 2, 3, 4) %!error ... %! laplacecdf (ones (3), ones (2), ones (2)) %!error ... %! laplacecdf (ones (2), ones (3), ones (2)) %!error ... %! laplacecdf (ones (2), ones (2), ones (3)) %!error laplacecdf (i, 2, 2) %!error laplacecdf (2, i, 2) %!error laplacecdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/laplaceinv.m000066400000000000000000000111451475240274700224340ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} laplaceinv (@var{p}, @var{mu}, @var{beta}) ## ## Inverse of the Laplace cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Laplace distribution with location parameter @var{mu} and scale parameter ## (i.e. "diversity") @var{beta}. The size of @var{x} is the common size of ## @var{p}, @var{mu}, and @var{beta}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{beta} > 0}. ## For @qcode{@var{beta} <= 0}, @qcode{NaN} is returned. ## ## Further information about the Laplace distribution can be found at ## @url{https://en.wikipedia.org/wiki/Laplace_distribution} ## ## @seealso{laplaceinv, laplacepdf, laplacernd} ## @end deftypefn function x = laplaceinv (p, mu, beta) ## Check for valid number of input arguments if (nargin < 3) error ("laplaceinv: function called with too few input arguments."); endif ## Check for common size of P, MU, and BETA if (! isscalar (p) || ! isscalar (mu) || ! isscalar(beta)) [retval, p, mu, beta] = common_size (p, mu, beta); if (retval > 0) error (strcat (["laplaceinv: P, MU, and BETA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, MU, and BETA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (beta)) error ("laplaceinv: P, MU, and BETA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (beta, "single")); x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Compute Laplace iCDF k = (p >= 0) & (p <= 1) & (beta > 0); x(k) = mu(k) + beta(k) .* ((p(k) < 1/2) .* log (2 .* p(k)) - ... (p(k) > 1/2) .* log (2 .* (1 - p(k)))); endfunction %!demo %! ## Plot various iCDFs from the Laplace distribution %! p = 0.001:0.001:0.999; %! x1 = cauchyinv (p, 0, 1); %! x2 = cauchyinv (p, 0, 2); %! x3 = cauchyinv (p, 0, 4); %! x4 = cauchyinv (p, -5, 4); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([-10, 10]) %! legend ({"μ = 0, β = 1", "μ = 0, β = 2", ... %! "μ = 0, β = 4", "μ = -5, β = 4"}, "location", "northwest") %! title ("Laplace iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, x %! p = [-1 0 0.5 1 2]; %! x = [NaN, -Inf, 0, Inf, NaN]; %!assert (laplaceinv (p, 0, 1), x) %!assert (laplaceinv (p, 0, [-2, -1, 0, 1, 2]), [nan(1, 3), Inf, NaN]) %!assert (laplaceinv ([p, NaN], 0, 1), [x, NaN]) ## Test class of input preserved %!assert (laplaceinv (single ([p, NaN]), 0, 1), single ([x, NaN])) %!assert (laplaceinv ([p, NaN], single (0), 1), single ([x, NaN])) %!assert (laplaceinv ([p, NaN], 0, single (1)), single ([x, NaN])) ## Test input validation %!error laplaceinv () %!error laplaceinv (1) %!error ... %! laplaceinv (1, 2) %!error laplaceinv (1, 2, 3, 4) %!error ... %! laplaceinv (1, ones (2), ones (3)) %!error ... %! laplaceinv (ones (2), 1, ones (3)) %!error ... %! laplaceinv (ones (2), ones (3), 1) %!error laplaceinv (i, 2, 3) %!error laplaceinv (1, i, 3) %!error laplaceinv (1, 2, i) statistics-release-1.7.3/inst/dist_fun/laplacepdf.m000066400000000000000000000110731475240274700224110ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} laplacepdf (@var{x}, @var{mu}, @var{beta}) ## ## Laplace probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Laplace distribution with location parameter @var{mu} and scale ## parameter (i.e. "diversity") @var{beta}. The size of @var{y} is the common ## size of @var{x}, @var{mu}, and @var{beta}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{beta} > 0}. ## For @qcode{@var{beta} <= 0}, @qcode{NaN} is returned. ## ## Further information about the Laplace distribution can be found at ## @url{https://en.wikipedia.org/wiki/Laplace_distribution} ## ## @seealso{laplacecdf, laplacepdf, laplacernd} ## @end deftypefn function y = laplacepdf (x, mu, beta) ## Check for valid number of input arguments if (nargin < 3) error ("laplacepdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and BETA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(beta)) [retval, x, mu, beta] = common_size (x, mu, beta); if (retval > 0) error (strcat (["laplacepdf: X, MU, and BETA must be of"], ... [" common size or scalars."])); endif endif ## Check for X, MU, and BETA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (beta)) error ("laplacepdf: X, MU, and BETA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (beta, "single")); y = NaN (size (x), "single"); else y = NaN (size (x)); endif ## Compute Laplace PDF k1 = ((x == -Inf) & (beta > 0)) | ((x == Inf) & (beta > 0)); y(k1) = 0; k = ! k1 & (beta > 0); y(k) = exp (- abs (x(k) - mu(k)) ./ beta(k)) ./ (2 .* beta(k)); endfunction %!demo %! ## Plot various PDFs from the Laplace distribution %! x = -10:0.01:10; %! y1 = laplacepdf (x, 0, 1); %! y2 = laplacepdf (x, 0, 2); %! y3 = laplacepdf (x, 0, 4); %! y4 = laplacepdf (x, -5, 4); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! xlim ([-10, 10]) %! ylim ([0, 0.6]) %! legend ({"μ = 0, β = 1", "μ = 0, β = 2", ... %! "μ = 0, β = 4", "μ = -5, β = 4"}, "location", "northeast") %! title ("Laplace PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test results %!shared x, y %! x = [-Inf -log(2) 0 log(2) Inf]; %! y = [0, 1/4, 1/2, 1/4, 0]; %!assert (laplacepdf ([x, NaN], 0, 1), [y, NaN]) %!assert (laplacepdf (x, 0, [-2, -1, 0, 1, 2]), [nan(1, 3), 0.25, 0]) ## Test class of input preserved %!assert (laplacepdf (single ([x, NaN]), 0, 1), single ([y, NaN])) %!assert (laplacepdf ([x, NaN], single (0), 1), single ([y, NaN])) %!assert (laplacepdf ([x, NaN], 0, single (1)), single ([y, NaN])) ## Test input validation %!error laplacepdf () %!error laplacepdf (1) %!error ... %! laplacepdf (1, 2) %!error laplacepdf (1, 2, 3, 4) %!error ... %! laplacepdf (1, ones (2), ones (3)) %!error ... %! laplacepdf (ones (2), 1, ones (3)) %!error ... %! laplacepdf (ones (2), ones (3), 1) %!error laplacepdf (i, 2, 3) %!error laplacepdf (1, i, 3) %!error laplacepdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/laplacernd.m000066400000000000000000000154641475240274700224330ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} laplacernd (@var{mu}, @var{beta}) ## @deftypefnx {statistics} {@var{r} =} laplacernd (@var{mu}, @var{beta}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} laplacernd (@var{mu}, @var{beta}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} laplacernd (@var{mu}, @var{beta}, [@var{sz}]) ## ## Random arrays from the Laplace distribution. ## ## @code{@var{r} = laplacernd (@var{mu}, @var{beta})} returns an array of ## random numbers chosen from the Laplace distribution with location parameter ## @var{mu} and scale parameter @var{beta}. The size of @var{r} is the common ## size of @var{mu} and @var{beta}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{beta} > 0}. ## For @qcode{@var{beta} <= 0}, @qcode{NaN} is returned. ## ## When called with a single size argument, @code{laplacernd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Laplace distribution can be found at ## @url{https://en.wikipedia.org/wiki/Laplace_distribution} ## ## @seealso{laplacecdf, laplaceinv, laplacernd} ## @end deftypefn function r = laplacernd (mu, beta, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("laplacernd: function called with too few input arguments."); endif ## Check for common size of MU, and BETA if (! isscalar (mu) || ! isscalar (beta)) [retval, mu, beta] = common_size (mu, beta); if (retval > 0) error ("laplacernd: MU and BETA must be of common size or scalars."); endif endif ## Check for X, MU, and BETA being reals if (iscomplex (mu) || iscomplex (beta)) error ("laplacernd: MU and BETA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["laplacernd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("laplacernd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("laplacernd: MU and BETA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (beta, "single")) is_type = "single"; else is_type = "double"; endif ## Generate random sample from Laplace distribution tmp = rand (sz, is_type); r = ((tmp < 1/2) .* log (2 * tmp) - ... (tmp > 1/2) .* log (2 * (1 - tmp))) .* beta + mu; ## Force output to NaN for invalid parameter BETA <= 0 k = (beta <= 0); r(k) = NaN; endfunction ## Test output %!assert (size (laplacernd (1, 1)), [1 1]) %!assert (size (laplacernd (1, ones (2,1))), [2, 1]) %!assert (size (laplacernd (1, ones (2,2))), [2, 2]) %!assert (size (laplacernd (ones (2,1), 1)), [2, 1]) %!assert (size (laplacernd (ones (2,2), 1)), [2, 2]) %!assert (size (laplacernd (1, 1, 3)), [3, 3]) %!assert (size (laplacernd (1, 1, [4, 1])), [4, 1]) %!assert (size (laplacernd (1, 1, 4, 1)), [4, 1]) %!assert (size (laplacernd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (laplacernd (1, 1, 0, 1)), [0, 1]) %!assert (size (laplacernd (1, 1, 1, 0)), [1, 0]) %!assert (size (laplacernd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (laplacernd (1, 1)), "double") %!assert (class (laplacernd (1, single (1))), "single") %!assert (class (laplacernd (1, single ([1, 1]))), "single") %!assert (class (laplacernd (single (1), 1)), "single") %!assert (class (laplacernd (single ([1, 1]), 1)), "single") ## Test input validation %!error laplacernd () %!error laplacernd (1) %!error ... %! laplacernd (ones (3), ones (2)) %!error ... %! laplacernd (ones (2), ones (3)) %!error laplacernd (i, 2, 3) %!error laplacernd (1, i, 3) %!error ... %! laplacernd (1, 2, -1) %!error ... %! laplacernd (1, 2, 1.2) %!error ... %! laplacernd (1, 2, ones (2)) %!error ... %! laplacernd (1, 2, [2 -1 2]) %!error ... %! laplacernd (1, 2, [2 0 2.5]) %!error ... %! laplacernd (1, 2, 2, -1, 5) %!error ... %! laplacernd (1, 2, 2, 1.5, 5) %!error ... %! laplacernd (2, ones (2), 3) %!error ... %! laplacernd (2, ones (2), [3, 2]) %!error ... %! laplacernd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/logicdf.m000066400000000000000000000124421475240274700217260ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} logicdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} logicdf (@var{x}, @var{mu}, @var{sigma}, @qcode{"upper"}) ## ## Logistic cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the logistic distribution with location parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{p} is the common size of @var{x}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{sigma} > 0}. ## For @qcode{@var{sigma} <= 0}, @qcode{NaN} is returned. ## ## @code{@var{p} = logicdf (@var{x}, @var{mu}, @var{sigma}, "upper")} computes ## the upper tail probability of the logistic distribution with parameters ## @var{mu} and @var{sigma}, at the values in @var{x}. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logiinv, logipdf, logirnd, logifit, logilike, logistat} ## @end deftypefn function p = logicdf (x, mu, sigma, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("logicdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("logicdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("logicdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("logicdf: X, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); p = NaN (size (x), "single"); else p = NaN (size (x)); endif ## Find normal and edge cases k1 = (x == -Inf) & (sigma > 0); k2 = (x == Inf) & (sigma > 0); k = ! k1 & ! k2 & (sigma > 0); ## Compute logistic CDF if (uflag) p(k1) = 1; p(k2) = 0; p(k) = 1 ./ (1 + exp ((x(k) - mu(k)) ./ sigma(k))); else p(k1) = 0; p(k2) = 1; p(k) = 1 ./ (1 + exp (- (x(k) - mu(k)) ./ sigma(k))); endif endfunction %!demo %! ## Plot various CDFs from the logistic distribution %! x = -5:0.01:20; %! p1 = logicdf (x, 5, 2); %! p2 = logicdf (x, 9, 3); %! p3 = logicdf (x, 9, 4); %! p4 = logicdf (x, 6, 2); %! p5 = logicdf (x, 2, 1); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-m") %! grid on %! legend ({"μ = 5, σ = 2", "μ = 9, σ = 3", "μ = 9, σ = 4", ... %! "μ = 6, σ = 2", "μ = 2, σ = 1"}, "location", "southeast") %! title ("Logistic CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-Inf -log(3) 0 log(3) Inf]; %! y = [0, 1/4, 1/2, 3/4, 1]; %!assert (logicdf ([x, NaN], 0, 1), [y, NaN], eps) %!assert (logicdf (x, 0, [-2, -1, 0, 1, 2]), [nan(1, 3), 0.75, 1]) ## Test class of input preserved %!assert (logicdf (single ([x, NaN]), 0, 1), single ([y, NaN]), eps ("single")) %!assert (logicdf ([x, NaN], single (0), 1), single ([y, NaN]), eps ("single")) %!assert (logicdf ([x, NaN], 0, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error logicdf () %!error logicdf (1) %!error ... %! logicdf (1, 2) %!error logicdf (1, 2, 3, "tail") %!error logicdf (1, 2, 3, 4) %!error ... %! logicdf (1, ones (2), ones (3)) %!error ... %! logicdf (ones (2), 1, ones (3)) %!error ... %! logicdf (ones (2), ones (3), 1) %!error logicdf (i, 2, 3) %!error logicdf (1, i, 3) %!error logicdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/logiinv.m000066400000000000000000000110641475240274700217650ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} logiinv (@var{p}, @var{mu}, @var{sigma}) ## ## Inverse of the logistic cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the logistic distribution with location parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{p} is the common size of @var{x}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{sigma} > 0}. ## For @qcode{@var{sigma} <= 0}, @qcode{NaN} is returned. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logicdf, logipdf, logirnd, logifit, logilike, logistat} ## @end deftypefn function x = logiinv (p, mu, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("logiinv: function called with too few input arguments."); endif ## Check for common size of P, MU, and SIGMA if (! isscalar (p) || ! isscalar (mu) || ! isscalar(sigma)) [retval, p, mu, sigma] = common_size (p, mu, sigma); if (retval > 0) error ("logiinv: P, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma)) error ("logiinv: P, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single")); x = NaN (size (p), "single"); else x = NaN (size (p)); endif k = (p == 0) & (sigma > 0); x(k) = -Inf; k = (p == 1) & (sigma > 0); x(k) = Inf; k = (p > 0) & (p < 1) & (sigma > 0); x(k) = mu(k) + sigma(k) .* log (p(k) ./ (1 - p(k))); endfunction %!demo %! ## Plot various iCDFs from the logistic distribution %! p = 0.001:0.001:0.999; %! x1 = logiinv (p, 5, 2); %! x2 = logiinv (p, 9, 3); %! x3 = logiinv (p, 9, 4); %! x4 = logiinv (p, 6, 2); %! x5 = logiinv (p, 2, 1); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-m") %! grid on %! legend ({"μ = 5, σ = 2", "μ = 9, σ = 3", "μ = 9, σ = 4", ... %! "μ = 6, σ = 2", "μ = 2, σ = 1"}, "location", "southeast") %! title ("Logistic iCDF") %! xlabel ("probability") %! ylabel ("x") ## Test output %!test %! p = [0.01:0.01:0.99]; %! assert (logiinv (p, 0, 1), log (p ./ (1-p)), 25*eps); %!shared p %! p = [-1 0 0.5 1 2]; %!assert (logiinv (p, 0, 1), [NaN -Inf 0 Inf NaN]) %!assert (logiinv (p, 0, [-1, 0, 1, 2, 3]), [NaN NaN 0 Inf NaN]) ## Test class of input preserved %!assert (logiinv ([p, NaN], 0, 1), [NaN -Inf 0 Inf NaN NaN]) %!assert (logiinv (single ([p, NaN]), 0, 1), single ([NaN -Inf 0 Inf NaN NaN])) %!assert (logiinv ([p, NaN], single (0), 1), single ([NaN -Inf 0 Inf NaN NaN])) %!assert (logiinv ([p, NaN], 0, single (1)), single ([NaN -Inf 0 Inf NaN NaN])) ## Test input validation %!error logiinv () %!error logiinv (1) %!error ... %! logiinv (1, 2) %!error ... %! logiinv (1, ones (2), ones (3)) %!error ... %! logiinv (ones (2), 1, ones (3)) %!error ... %! logiinv (ones (2), ones (3), 1) %!error logiinv (i, 2, 3) %!error logiinv (1, i, 3) %!error logiinv (1, 2, i) statistics-release-1.7.3/inst/dist_fun/logipdf.m000066400000000000000000000107321475240274700217430ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} logipdf (@var{x}, @var{mu}, @var{sigma}) ## ## Logistic probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the logistic distribution with location parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{p} is the common size of @var{x}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{sigma} > 0}. ## For @qcode{@var{sigma} <= 0}, @qcode{NaN} is returned. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logicdf, logiinv, logirnd, logifit, logilike, logistat} ## @end deftypefn function y = logipdf (x, mu, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("logipdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("logipdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("logipdf: X, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); y = NaN (size (x), "single"); else y = NaN (size (x)); endif ## Compute logistic PDF k1 = ((x == -Inf) & (sigma > 0)) | ((x == Inf) & (sigma > 0)); y(k1) = 0; k = ! k1 & (sigma > 0); y(k) = (1 ./ (4 .* sigma(k))) .* ... (sech ((x(k) - mu(k)) ./ (2 .* sigma(k))) .^ 2); endfunction %!demo %! ## Plot various PDFs from the logistic distribution %! x = -5:0.01:20; %! y1 = logipdf (x, 5, 2); %! y2 = logipdf (x, 9, 3); %! y3 = logipdf (x, 9, 4); %! y4 = logipdf (x, 6, 2); %! y5 = logipdf (x, 2, 1); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-m") %! grid on %! ylim ([0, 0.3]) %! legend ({"μ = 5, σ = 2", "μ = 9, σ = 3", "μ = 9, σ = 4", ... %! "μ = 6, σ = 2", "μ = 2, σ = 1"}, "location", "northeast") %! title ("Logistic PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-Inf -log(4) 0 log(4) Inf]; %! y = [0, 0.16, 1/4, 0.16, 0]; %!assert (logipdf ([x, NaN], 0, 1), [y, NaN], eps) %!assert (logipdf (x, 0, [-2, -1, 0, 1, 2]), [nan(1, 3), y([4:5])], eps) ## Test class of input preserved %!assert (logipdf (single ([x, NaN]), 0, 1), single ([y, NaN]), eps ("single")) %!assert (logipdf ([x, NaN], single (0), 1), single ([y, NaN]), eps ("single")) %!assert (logipdf ([x, NaN], 0, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error logipdf () %!error logipdf (1) %!error ... %! logipdf (1, 2) %!error ... %! logipdf (1, ones (2), ones (3)) %!error ... %! logipdf (ones (2), 1, ones (3)) %!error ... %! logipdf (ones (2), ones (3), 1) %!error logipdf (i, 2, 3) %!error logipdf (1, i, 3) %!error logipdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/logirnd.m000066400000000000000000000151511475240274700217550ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} logirnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} logirnd (@var{mu}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} logirnd (@var{mu}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} logirnd (@var{mu}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the logistic distribution. ## ## @code{@var{r} = logirnd (@var{mu}, @var{sigma})} returns an array of ## random numbers chosen from the logistic distribution with location parameter ## @var{mu} and scale parameter @var{sigma}. The size of @var{r} is the common size ## of @var{mu} and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be reals and @qcode{@var{sigma} > 0}. ## For @qcode{@var{sigma} <= 0}, @qcode{NaN} is returned. ## ## When called with a single size argument, @code{logirnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logcdf, logiinv, logipdf, logifit, logilike, logistat} ## @end deftypefn function r = logirnd (mu, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("logirnd: function called with too few input arguments."); endif ## Check for common size of MU, and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("logirnd: MU and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (mu) || iscomplex (sigma)) error ("logirnd: MU and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["logirnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("logirnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("logirnd: MU and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single")) is_type = "single"; else is_type = "double"; endif ## Generate random sample from logistic distribution r = - log (1 ./ rand (sz, is_type) - 1) .* sigma + mu; ## Force output to NaN for invalid parameter SIGMA <= 0 k = (sigma <= 0); r(k) = NaN; endfunction ## Test output %!assert (size (logirnd (1, 1)), [1 1]) %!assert (size (logirnd (1, ones (2,1))), [2, 1]) %!assert (size (logirnd (1, ones (2,2))), [2, 2]) %!assert (size (logirnd (ones (2,1), 1)), [2, 1]) %!assert (size (logirnd (ones (2,2), 1)), [2, 2]) %!assert (size (logirnd (1, 1, 3)), [3, 3]) %!assert (size (logirnd (1, 1, [4, 1])), [4, 1]) %!assert (size (logirnd (1, 1, 4, 1)), [4, 1]) %!assert (size (logirnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (logirnd (1, 1, 0, 1)), [0, 1]) %!assert (size (logirnd (1, 1, 1, 0)), [1, 0]) %!assert (size (logirnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (logirnd (1, 1)), "double") %!assert (class (logirnd (1, single (1))), "single") %!assert (class (logirnd (1, single ([1, 1]))), "single") %!assert (class (logirnd (single (1), 1)), "single") %!assert (class (logirnd (single ([1, 1]), 1)), "single") ## Test input validation %!error logirnd () %!error logirnd (1) %!error ... %! logirnd (ones (3), ones (2)) %!error ... %! logirnd (ones (2), ones (3)) %!error logirnd (i, 2, 3) %!error logirnd (1, i, 3) %!error ... %! logirnd (1, 2, -1) %!error ... %! logirnd (1, 2, 1.2) %!error ... %! logirnd (1, 2, ones (2)) %!error ... %! logirnd (1, 2, [2 -1 2]) %!error ... %! logirnd (1, 2, [2 0 2.5]) %!error ... %! logirnd (1, 2, 2, -1, 5) %!error ... %! logirnd (1, 2, 2, 1.5, 5) %!error ... %! logirnd (2, ones (2), 3) %!error ... %! logirnd (2, ones (2), [3, 2]) %!error ... %! logirnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/loglcdf.m000066400000000000000000000145051475240274700217330ustar00rootroot00000000000000## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} loglcdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} loglcdf (@var{x}, @var{mu}, @var{sigma}, @qcode{"upper"}) ## ## Loglogistic cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the loglogistic distribution with mean parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{p} is the common size of @var{x}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters, @var{mu} and @var{sigma}, must be positive reals and @var{x} ## is supported in the range @math{[0,inf)}, otherwise @qcode{NaN} is returned. ## ## @code{@var{p} = loglcdf (@var{x}, @var{mu}, @var{sigma}, "upper")} computes ## the upper tail probability of the log-logistic distribution with parameters ## @var{mu} and @var{sigma}, at the values in @var{x}. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{loglinv, loglpdf, loglrnd, loglfit, logllike, loglstat} ## @end deftypefn function p = loglcdf (x, mu, sigma, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("loglcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("loglcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("loglcdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("loglcdf: X, MU, and SIGMA must not be complex."); endif ## Check for invalid points mu(mu < 0) = NaN; sigma(sigma <= 0) = NaN; x(x <= 0) = realmin; ## Compute log-logistic CDF z = (log (x) - mu) ./ sigma; if (uflag) p = 1 ./ (1 + exp (z)); else p = 1 ./ (1 + exp (-z)); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); p = cast (p, "single"); endif endfunction %!demo %! ## Plot various CDFs from the log-logistic distribution %! x = 0:0.001:2; %! p1 = loglcdf (x, log (1), 1/0.5); %! p2 = loglcdf (x, log (1), 1); %! p3 = loglcdf (x, log (1), 1/2); %! p4 = loglcdf (x, log (1), 1/4); %! p5 = loglcdf (x, log (1), 1/8); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c", x, p5, "-m") %! legend ({"σ = 2 (β = 0.5)", "σ = 1 (β = 1)", "σ = 0.5 (β = 2)", ... %! "σ = 0.25 (β = 4)", "σ = 0.125 (β = 8)"}, "location", "northwest") %! grid on %! title ("Log-logistic CDF") %! xlabel ("values in x") %! ylabel ("probability") %! text (0.05, 0.64, "μ = 0 (α = 1), values of σ (β) as shown in legend") ## Test output %!shared out1, out2 %! out1 = [0, 0.5, 0.66666667, 0.75, 0.8, 0.83333333]; %! out2 = [0, 0.4174, 0.4745, 0.5082, 0.5321, 0.5506]; %!assert (loglcdf ([0:5], 0, 1), out1, 1e-8) %!assert (loglcdf ([0:5], 0, 1, "upper"), 1 - out1, 1e-8) %!assert (loglcdf ([0:5], 0, 1), out1, 1e-8) %!assert (loglcdf ([0:5], 0, 1, "upper"), 1 - out1, 1e-8) %!assert (loglcdf ([0:5], 1, 3), out2, 1e-4) %!assert (loglcdf ([0:5], 1, 3, "upper"), 1 - out2, 1e-4) ## Test class of input preserved %!assert (class (loglcdf (single (1), 2, 3)), "single") %!assert (class (loglcdf (1, single (2), 3)), "single") %!assert (class (loglcdf (1, 2, single (3))), "single") ## Test input validation %!error loglcdf (1) %!error loglcdf (1, 2) %!error ... %! loglcdf (1, 2, 3, 4) %!error ... %! loglcdf (1, 2, 3, "uper") %!error ... %! loglcdf (1, ones (2), ones (3)) %!error ... %! loglcdf (1, ones (2), ones (3), "upper") %!error ... %! loglcdf (ones (2), 1, ones (3)) %!error ... %! loglcdf (ones (2), 1, ones (3), "upper") %!error ... %! loglcdf (ones (2), ones (3), 1) %!error ... %! loglcdf (ones (2), ones (3), 1, "upper") %!error loglcdf (i, 2, 3) %!error loglcdf (i, 2, 3, "upper") %!error loglcdf (1, i, 3) %!error loglcdf (1, i, 3, "upper") %!error loglcdf (1, 2, i) %!error loglcdf (1, 2, i, "upper") statistics-release-1.7.3/inst/dist_fun/loglinv.m000066400000000000000000000116331475240274700217720ustar00rootroot00000000000000## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} loglinv (@var{p}, @var{mu}, @var{sigma}) ## ## Inverse of the log-logistic cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the log-logistic distribution with mean parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{x} is the common size of @var{p}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters, @var{mu} and @var{sigma}, must be positive reals and @var{p} ## is supported in the range @math{[0,1]}, otherwise @qcode{NaN} is returned. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{loglcdf, loglpdf, loglrnd, loglfit, logllike, loglstat} ## @end deftypefn function x = loglinv (p, mu, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("loglinv: function called with too few input arguments."); endif ## Check for common size of P, MU, and SIGMA if (! isscalar (p) || ! isscalar (mu) || ! isscalar(sigma)) [retval, p, mu, sigma] = common_size (p, mu, sigma); if (retval > 0) error ("loglinv: P, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma)) error ("loglinv: P, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single")); x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Check for valid points k = (p >= 0) & (p <= 1) & (mu >= 0) & (sigma > 0); ## Compute the log-logistic iCDF x(k) = exp (logit (p(k)) .* sigma(k) + mu(k)); endfunction %!demo %! ## Plot various iCDFs from the log-logistic distribution %! p = 0.001:0.001:0.999; %! x1 = loglinv (p, log (1), 1/0.5); %! x2 = loglinv (p, log (1), 1); %! x3 = loglinv (p, log (1), 1/2); %! x4 = loglinv (p, log (1), 1/4); %! x5 = loglinv (p, log (1), 1/8); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c", p, x5, "-m") %! ylim ([0, 20]) %! grid on %! legend ({"σ = 2 (β = 0.5)", "σ = 1 (β = 1)", "σ = 0.5 (β = 2)", ... %! "σ = 0.25 (β = 4)", "σ = 0.125 (β = 8)"}, "location", "northwest") %! title ("Log-logistic iCDF") %! xlabel ("probability") %! ylabel ("x") %! text (0.03, 12.5, "μ = 0 (α = 1), values of σ (β) as shown in legend") ## Test output %!shared p, out1, out2 %! p = [-1, 0, 0.2, 0.5, 0.8, 0.95, 1, 2]; %! out1 = [NaN, 0, 0.25, 1, 4, 19, Inf, NaN]; %! out2 = [NaN, 0, 0.0424732, 2.718282, 173.970037, 18644.695061, Inf, NaN]; %!assert (loglinv (p, 0, 1), out1, 1e-8) %!assert (loglinv (p, 0, 1), out1, 1e-8) %!assert (loglinv (p, 1, 3), out2, 1e-6) ## Test class of input preserved %!assert (class (loglinv (single (1), 2, 3)), "single") %!assert (class (loglinv (1, single (2), 3)), "single") %!assert (class (loglinv (1, 2, single (3))), "single") ## Test input validation %!error loglinv (1) %!error loglinv (1, 2) %!error ... %! loglinv (1, ones (2), ones (3)) %!error ... %! loglinv (ones (2), 1, ones (3)) %!error ... %! loglinv (ones (2), ones (3), 1) %!error loglinv (i, 2, 3) %!error loglinv (1, i, 3) %!error loglinv (1, 2, i) statistics-release-1.7.3/inst/dist_fun/loglpdf.m000066400000000000000000000117101475240274700217430ustar00rootroot00000000000000## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} loglpdf (@var{x}, @var{mu}, @var{sigma}) ## ## Loglogistic probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the loglogistic distribution with mean parameter @var{mu} and scale ## parameter @var{sigma}. The size of @var{y} is the common size of @var{x}, ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters, @var{mu} and @var{sigma}, must be positive reals, otherwise ## @qcode{NaN} is returned. @var{x} is supported in the range @math{[0,Inf)}, ## otherwise @qcode{0} is returned. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{loglcdf, loglinv, loglrnd, loglfit, logllike, loglstat} ## @end deftypefn function y = loglpdf (x, mu, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("loglpdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar(sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("loglpdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("loglpdf: X, MU, and SIGMA must not be complex."); endif ## Check for invalid points mu(mu < 0) = NaN; sigma(sigma <= 0) = NaN; ## Compute log-logistic PDF a = exp (mu); b = 1./ sigma; y = ((b ./ a) .* (x ./ a) .^ (b - 1)) ./ ((1 + (x ./ a) .^ b) .^ 2); y(x <= 0) = 0; ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); y = cast (y, "single"); endif endfunction %!demo %! ## Plot various PDFs from the log-logistic distribution %! x = 0.001:0.001:2; %! y1 = loglpdf (x, log (1), 1/0.5); %! y2 = loglpdf (x, log (1), 1); %! y3 = loglpdf (x, log (1), 1/2); %! y4 = loglpdf (x, log (1), 1/4); %! y5 = loglpdf (x, log (1), 1/8); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c", x, y5, "-m") %! grid on %! ylim ([0,3]) %! legend ({"σ = 2 (β = 0.5)", "σ = 1 (β = 1)", "σ = 0.5 (β = 2)", ... %! "σ = 0.25 (β = 4)", "σ = 0.125 (β = 8)"}, "location", "northeast") %! title ("Log-logistic PDF") %! xlabel ("values in x") %! ylabel ("density") %! text (0.1, 2.8, "μ = 0 (α = 1), values of σ (β) as shown in legend") ## Test output %!shared out1, out2 %! out1 = [0, 0, 1, 0.2500, 0.1111, 0.0625, 0.0400, 0.0278, 0]; %! out2 = [0, 0, 0.0811, 0.0416, 0.0278, 0.0207, 0.0165, 0]; %!assert (loglpdf ([-1,0,realmin,1:5,Inf], 0, 1), out1, 1e-4) %!assert (loglpdf ([-1,0,realmin,1:5,Inf], 0, 1), out1, 1e-4) %!assert (loglpdf ([-1:5,Inf], 1, 3), out2, 1e-4) ## Test class of input preserved %!assert (class (loglpdf (single (1), 2, 3)), "single") %!assert (class (loglpdf (1, single (2), 3)), "single") %!assert (class (loglpdf (1, 2, single (3))), "single") ## Test input validation %!error loglpdf (1) %!error loglpdf (1, 2) %!error ... %! loglpdf (1, ones (2), ones (3)) %!error ... %! loglpdf (ones (2), 1, ones (3)) %!error ... %! loglpdf (ones (2), ones (3), 1) %!error loglpdf (i, 2, 3) %!error loglpdf (1, i, 3) %!error loglpdf (1, 2, i) statistics-release-1.7.3/inst/dist_fun/loglrnd.m000066400000000000000000000156501475240274700217640ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} loglrnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} loglrnd (@var{mu}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} loglrnd (@var{mu}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} loglrnd (@var{mu}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the loglogistic distribution. ## ## @code{@var{r} = loglrnd (@var{mu}, @var{sigma})} returns an array of random ## numbers chosen from the loglogistic distribution with mean parameter @var{mu} ## and scale parameter @var{sigma}. The size of @var{r} is the common size of ## @var{mu} and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be positive reals, otherwise @qcode{NaN} is returned. ## ## When called with mu single size argument, @code{loglrnd} returns mu square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with mu row vector of dimensions, @var{sz}. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{loglcdf, loglinv, loglpdf, loglfit, logllike, loglstat} ## @end deftypefn function r = loglrnd (mu, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("loglrnd: function called with too few input arguments."); endif ## Check for common size of MU, and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("loglrnd: MU and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (mu) || iscomplex (sigma)) error ("loglrnd: MU and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["loglrnd: SZ must be mu scalar or mu row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("loglrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("loglrnd: MU and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single")) is_type = "single"; else is_type = "double"; endif ## Generate random sample from log-logistic distribution u = rand (sz, is_type); r = exp (mu) .* (u ./ (1 - u)) .^ (sigma); ## Force output to NaN for invalid parameters MU and SIGMA k = (mu < 0 | sigma <= 0); r(k) = NaN; endfunction ## Test output %!assert (size (loglrnd (1, 1)), [1 1]) %!assert (size (loglrnd (1, ones (2,1))), [2, 1]) %!assert (size (loglrnd (1, ones (2,2))), [2, 2]) %!assert (size (loglrnd (ones (2,1), 1)), [2, 1]) %!assert (size (loglrnd (ones (2,2), 1)), [2, 2]) %!assert (size (loglrnd (1, 1, 3)), [3, 3]) %!assert (size (loglrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (loglrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (loglrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (loglrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (loglrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (loglrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (loglrnd (1, 1)), "double") %!assert (class (loglrnd (1, single (1))), "single") %!assert (class (loglrnd (1, single ([1, 1]))), "single") %!assert (class (loglrnd (single (1), 1)), "single") %!assert (class (loglrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error loglrnd () %!error loglrnd (1) %!error ... %! loglrnd (ones (3), ones (2)) %!error ... %! loglrnd (ones (2), ones (3)) %!error loglrnd (i, 2, 3) %!error loglrnd (1, i, 3) %!error ... %! loglrnd (1, 2, -1) %!error ... %! loglrnd (1, 2, 1.2) %!error ... %! loglrnd (1, 2, ones (2)) %!error ... %! loglrnd (1, 2, [2 -1 2]) %!error ... %! loglrnd (1, 2, [2 0 2.5]) %!error ... %! loglrnd (1, 2, 2, -1, 5) %!error ... %! loglrnd (1, 2, 2, 1.5, 5) %!error ... %! loglrnd (2, ones (2), 3) %!error ... %! loglrnd (2, ones (2), [3, 2]) %!error ... %! loglrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/logncdf.m000066400000000000000000000213501475240274700217310ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} logncdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} logncdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} logncdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} logncdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} logncdf (@var{x}, @var{mu}, @var{sigma}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} logncdf (@var{x}, @var{mu}, @var{sigma}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} logncdf (@dots{}, @qcode{"upper"}) ## ## Lognormal cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the lognormal distribution with mean parameter @var{mu} and ## standard deviation parameter @var{sigma}, each corresponding to the ## associated normal distribution. The size of @var{p} is the common size of ## @var{x}, @var{mu}, and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## If a random variable follows this distribution, its logarithm is normally ## distributed with mean @var{mu} and standard deviation @var{sigma}. ## ## Default parameter values are @qcode{@var{mu} = 0} and ## @qcode{@var{sigma} = 1}. Both parameters must be reals and ## @qcode{@var{sigma} > 0}. For @qcode{@var{sigma} <= 0}, @qcode{NaN} is ## returned. ## ## When called with three output arguments, i.e. @qcode{[@var{p}, @var{plo}, ## @var{pup}]}, @code{logncdf} computes the confidence bounds for @var{p} when ## the input parameters @var{mu} and @var{sigma} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{plo} and @var{pup} are arrays of the same size as ## @var{p} containing the lower and upper confidence bounds. ## ## @code{[@dots{}] = logncdf (@dots{}, "upper")} computes the upper tail ## probability of the log-normal distribution with parameters @var{mu} and ## @var{sigma}, at the values in @var{x}. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logninv, lognpdf, lognrnd, lognfit, lognlike, lognstat} ## @end deftypefn function [varargout] = logncdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 6) error ("logncdf: invalid number of input arguments."); endif ## Check for "upper" flag if (nargin > 1 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 1 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("logncdf: invalid argument for upper tail."); elseif (nargin > 2 && isempty (varargin{end})) uflag = false; varargin(end) = []; else uflag = false; endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) mu = varargin{1}; else mu = 0; endif if (numel (varargin) > 1) sigma = varargin{2}; else sigma = 1; endif if (numel (varargin) > 2) pcov = varargin{3}; ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("logncdf: invalid size of covariance matrix."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("logncdf: covariance matrix is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 3) alpha = varargin{4}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("logncdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma)) [err, x, mu, sigma] = common_size (x, mu, sigma); if (err > 0) error ("logncdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("logncdf: X, MU, and SIGMA must not be complex."); endif ## Return NaN for out of range parameters. sigma(sigma <= 0) = NaN; ## Negative data would create complex values, which erfc cannot handle. x(x < 0) = 0; ## Compute lognormal cdf z = (log (x) - mu) ./ sigma; if (uflag) z = -z; endif p = 0.5 * erfc (-z ./ sqrt(2)); ## Compute confidence bounds (if requested) if (nargout >= 2) zvar = (pcov(1,1) + 2 * pcov(1,2) * z + pcov(2,2) * z .^ 2) ./ (sigma .^ 2); if (any (zvar(:) < 0)) error ("logncdf: bad covariance matrix."); end normz = -norminv (alpha / 2); halfwidth = normz * sqrt (zvar); zlo = z - halfwidth; zup = z + halfwidth; plo = 0.5 * erfc (-zlo ./ sqrt (2)); pup = 0.5 * erfc (-zup ./ sqrt (2)); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output varargout{1} = cast (p, is_class); if (nargout > 1) varargout{2} = cast (plo, is_class); varargout{3} = cast (pup, is_class); endif endfunction %!demo %! ## Plot various CDFs from the log-normal distribution %! x = 0:0.01:3; %! p1 = logncdf (x, 0, 1); %! p2 = logncdf (x, 0, 0.5); %! p3 = logncdf (x, 0, 0.25); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r") %! grid on %! legend ({"μ = 0, σ = 1", "μ = 0, σ = 0.5", "μ = 0, σ = 0.25"}, ... %! "location", "southeast") %! title ("Log-normal CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1, 0, 1, e, Inf]; %! y = [0, 0, 0.5, 1/2+1/2*erf(1/2), 1]; %!assert (logncdf (x, zeros (1,5), sqrt(2)*ones (1,5)), y, eps) %!assert (logncdf (x, zeros (1,5), sqrt(2)*ones (1,5), []), y, eps) %!assert (logncdf (x, 0, sqrt(2)*ones (1,5)), y, eps) %!assert (logncdf (x, zeros (1,5), sqrt(2)), y, eps) %!assert (logncdf (x, [0 1 NaN 0 1], sqrt(2)), [0 0 NaN y(4:5)], eps) %!assert (logncdf (x, 0, sqrt(2)*[0 NaN Inf 1 1]), [NaN NaN y(3:5)], eps) %!assert (logncdf ([x(1:3) NaN x(5)], 0, sqrt(2)), [y(1:3) NaN y(5)], eps) ## Test class of input preserved %!assert (logncdf ([x, NaN], 0, sqrt(2)), [y, NaN], eps) %!assert (logncdf (single ([x, NaN]), 0, sqrt(2)), single ([y, NaN]), eps ("single")) %!assert (logncdf ([x, NaN], single (0), sqrt(2)), single ([y, NaN]), eps ("single")) %!assert (logncdf ([x, NaN], 0, single (sqrt(2))), single ([y, NaN]), eps ("single")) ## Test input validation %!error logncdf () %!error logncdf (1,2,3,4,5,6,7) %!error logncdf (1, 2, 3, 4, "uper") %!error ... %! logncdf (ones (3), ones (2), ones (2)) %!error logncdf (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = logncdf (1, 2, 3) %!error [p, plo, pup] = ... %! logncdf (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! logncdf (1, 2, 3, [1, 0; 0, 1], 1.22) %!error [p, plo, pup] = ... %! logncdf (1, 2, 3, [1, 0; 0, 1], "alpha", "upper") %!error logncdf (i, 2, 2) %!error logncdf (2, i, 2) %!error logncdf (2, 2, i) %!error ... %! [p, plo, pup] =logncdf (1, 2, 3, [1, 0; 0, -inf], 0.04) statistics-release-1.7.3/inst/dist_fun/logninv.m000066400000000000000000000114111475240274700217660ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} logninv (@var{p}) ## @deftypefnx {statistics} {@var{x} =} logninv (@var{p}, @var{mu}) ## @deftypefnx {statistics} {@var{x} =} logninv (@var{p}, @var{mu}, @var{sigma}) ## ## Inverse of the lognormal cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the lognormal distribution with mean parameter @var{mu} and standard ## deviation parameter @var{sigma}, each corresponding to the associated normal ## distribution. The size of @var{x} is the common size of @var{p}, @var{mu}, ## and @var{sigma}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## If a random variable follows this distribution, its logarithm is normally ## distributed with mean @var{mu} and standard deviation @var{sigma}. ## ## Default parameter values are @qcode{@var{mu} = 0} and ## @qcode{@var{sigma} = 1}. Both parameters must be reals and ## @qcode{@var{sigma} > 0}. For @qcode{@var{sigma} <= 0}, @qcode{NaN} is ## returned. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logncdf, lognpdf, lognrnd, lognfit, lognlike, lognstat} ## @end deftypefn function x = logninv (p, mu = 0, sigma = 1) ## Check for valid number of input arguments if (nargin < 1 || nargin > 3) print_usage (); endif ## Check for common size of P, MU, and SIGMA if (! isscalar (p) || ! isscalar (mu) || ! isscalar (sigma)) [retval, p, mu, sigma] = common_size (p, mu, sigma); if (retval > 0) error ("logninv: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma)) error ("logninv: X, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Compute lognormal iCDF k = !(p >= 0) | !(p <= 1) | !(sigma > 0) | !(sigma < Inf); x(k) = NaN; k = (p == 1) & (sigma > 0) & (sigma < Inf); x(k) = Inf; k = (p >= 0) & (p < 1) & (sigma > 0) & (sigma < Inf); if (isscalar (mu) && isscalar (sigma)) x(k) = exp (mu) .* exp (sigma .* (-sqrt (2) * erfcinv (2 * p(k)))); else x(k) = exp (mu(k)) .* exp (sigma(k) .* (-sqrt (2) * erfcinv (2 * p(k)))); endif endfunction %!demo %! ## Plot various iCDFs from the log-normal distribution %! p = 0.001:0.001:0.999; %! x1 = logninv (p, 0, 1); %! x2 = logninv (p, 0, 0.5); %! x3 = logninv (p, 0, 0.25); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r") %! grid on %! ylim ([0, 3]) %! legend ({"μ = 0, σ = 1", "μ = 0, σ = 0.5", "μ = 0, σ = 0.25"}, ... %! "location", "northwest") %! title ("Log-normal iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (logninv (p, ones (1,5), ones (1,5)), [NaN 0 e Inf NaN]) %!assert (logninv (p, 1, ones (1,5)), [NaN 0 e Inf NaN]) %!assert (logninv (p, ones (1,5), 1), [NaN 0 e Inf NaN]) %!assert (logninv (p, [1 1 NaN 0 1], 1), [NaN 0 NaN Inf NaN]) %!assert (logninv (p, 1, [1 0 NaN Inf 1]), [NaN NaN NaN NaN NaN]) %!assert (logninv ([p(1:2) NaN p(4:5)], 1, 2), [NaN 0 NaN Inf NaN]) ## Test class of input preserved %!assert (logninv ([p, NaN], 1, 1), [NaN 0 e Inf NaN NaN]) %!assert (logninv (single ([p, NaN]), 1, 1), single ([NaN 0 e Inf NaN NaN])) %!assert (logninv ([p, NaN], single (1), 1), single ([NaN 0 e Inf NaN NaN])) %!assert (logninv ([p, NaN], 1, single (1)), single ([NaN 0 e Inf NaN NaN])) ## Test input validation %!error logninv () %!error logninv (1,2,3,4) %!error logninv (ones (3), ones (2), ones (2)) %!error logninv (ones (2), ones (3), ones (2)) %!error logninv (ones (2), ones (2), ones (3)) %!error logninv (i, 2, 2) %!error logninv (2, i, 2) %!error logninv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/lognpdf.m000066400000000000000000000111061475240274700217440ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} lognpdf (@var{x}) ## @deftypefnx {statistics} {@var{y} =} lognpdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{y} =} lognpdf (@var{x}, @var{mu}, @var{sigma}) ## ## Lognormal probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the lognormal distribution with mean parameter @var{mu} and standard ## deviation parameter @var{sigma}, each corresponding to the associated normal ## distribution. The size of @var{y} is the common size of @var{p}, @var{mu}, ## and @var{sigma}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## If a random variable follows this distribution, its logarithm is normally ## distributed with mean @var{mu} and standard deviation @var{sigma}. ## ## Default parameter values are @qcode{@var{mu} = 0} and ## @qcode{@var{sigma} = 1}. Both parameters must be reals and ## @qcode{@var{sigma} > 0}. For @qcode{@var{sigma} <= 0}, @qcode{NaN} is ## returned. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logncdf, logninv, lognrnd, lognfit, lognlike, lognstat} ## @end deftypefn function y = lognpdf (x, mu = 0, sigma = 1) ## Check for valid number of input arguments if (nargin < 1 || nargin > 3) print_usage (); endif ## Check for common size of P, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("lognpdf: X, MU, and SIGMA must be of common size or scalars"); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("lognpdf: X, MU, and SIGMA must not be complex"); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Compute lognormal PDF k = isnan (x) | !(sigma > 0) | !(sigma < Inf); y(k) = NaN; k = (x > 0) & (x < Inf) & (sigma > 0) & (sigma < Inf); if (isscalar (mu) && isscalar (sigma)) y(k) = normpdf (log (x(k)), mu, sigma) ./ x(k); else y(k) = normpdf (log (x(k)), mu(k), sigma(k)) ./ x(k); endif endfunction %!demo %! ## Plot various PDFs from the log-normal distribution %! x = 0:0.01:5; %! y1 = lognpdf (x, 0, 1); %! y2 = lognpdf (x, 0, 0.5); %! y3 = lognpdf (x, 0, 0.25); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r") %! grid on %! ylim ([0, 2]) %! legend ({"μ = 0, σ = 1", "μ = 0, σ = 0.5", "μ = 0, σ = 0.25"}, ... %! "location", "northeast") %! title ("Log-normal PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 e Inf]; %! y = [0, 0, 1/(e*sqrt(2*pi)) * exp(-1/2), 0]; %!assert (lognpdf (x, zeros (1,4), ones (1,4)), y, eps) %!assert (lognpdf (x, 0, ones (1,4)), y, eps) %!assert (lognpdf (x, zeros (1,4), 1), y, eps) %!assert (lognpdf (x, [0 1 NaN 0], 1), [0 0 NaN y(4)], eps) %!assert (lognpdf (x, 0, [0 NaN Inf 1]), [NaN NaN NaN y(4)], eps) %!assert (lognpdf ([x, NaN], 0, 1), [y, NaN], eps) ## Test class of input preserved %!assert (lognpdf (single ([x, NaN]), 0, 1), single ([y, NaN]), eps ("single")) %!assert (lognpdf ([x, NaN], single (0), 1), single ([y, NaN]), eps ("single")) %!assert (lognpdf ([x, NaN], 0, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error lognpdf () %!error lognpdf (1,2,3,4) %!error lognpdf (ones (3), ones (2), ones (2)) %!error lognpdf (ones (2), ones (3), ones (2)) %!error lognpdf (ones (2), ones (2), ones (3)) %!error lognpdf (i, 2, 2) %!error lognpdf (2, i, 2) %!error lognpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/lognrnd.m000066400000000000000000000156621475240274700217710ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} lognrnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} lognrnd (@var{mu}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} lognrnd (@var{mu}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} lognrnd (@var{mu}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the lognormal distribution. ## ## @code{@var{r} = lognrnd (@var{mu}, @var{sigma})} returns an array of random ## numbers chosen from the lognormal distribution with mean parameter @var{mu} ## and standard deviation parameter @var{sigma}, each corresponding to the ## associated normal distribution. The size of @var{r} is the common size of ## @var{mu}, and @var{sigma}. A scalar input functions as a constant matrix of ## the same size as the other inputs. Both parameters must be reals and ## @qcode{@var{sigma} > 0}. For @qcode{@var{sigma} <= 0}, @qcode{NaN} is ## returned. ## ## Both parameters must be reals and @qcode{@var{sigma} > 0}. ## For @qcode{@var{sigma} <= 0}, @qcode{NaN} is returned. ## ## When called with a single size argument, @code{lognrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logncdf, logninv, lognpdf, lognfit, lognlike, lognstat} ## @end deftypefn function r = lognrnd (mu, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("lognrnd: function called with too few input arguments."); endif ## Check for common size of P, MU, and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("lognrnd: MU and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (mu) || iscomplex (sigma)) error ("lognrnd: MU and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["lognrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("lognrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("lognrnd: MU and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from lognormal distribution if (isscalar (mu) && isscalar (sigma)) if ((sigma > 0) && (sigma < Inf)) r = exp (mu + sigma * randn (sz, cls)); else r = NaN (sz, cls); endif else r = exp (mu + sigma .* randn (sz, cls)); k = (sigma < 0) | (sigma == Inf); r(k) = NaN; endif endfunction ## Test output %!assert (size (lognrnd (1, 1)), [1 1]) %!assert (size (lognrnd (1, ones (2,1))), [2, 1]) %!assert (size (lognrnd (1, ones (2,2))), [2, 2]) %!assert (size (lognrnd (ones (2,1), 1)), [2, 1]) %!assert (size (lognrnd (ones (2,2), 1)), [2, 2]) %!assert (size (lognrnd (1, 1, 3)), [3, 3]) %!assert (size (lognrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (lognrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (lognrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (lognrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (lognrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (lognrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (lognrnd (1, 1)), "double") %!assert (class (lognrnd (1, single (1))), "single") %!assert (class (lognrnd (1, single ([1, 1]))), "single") %!assert (class (lognrnd (single (1), 1)), "single") %!assert (class (lognrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error lognrnd () %!error lognrnd (1) %!error ... %! lognrnd (ones (3), ones (2)) %!error ... %! lognrnd (ones (2), ones (3)) %!error lognrnd (i, 2, 3) %!error lognrnd (1, i, 3) %!error ... %! lognrnd (1, 2, -1) %!error ... %! lognrnd (1, 2, 1.2) %!error ... %! lognrnd (1, 2, ones (2)) %!error ... %! lognrnd (1, 2, [2 -1 2]) %!error ... %! lognrnd (1, 2, [2 0 2.5]) %!error ... %! lognrnd (1, 2, 2, -1, 5) %!error ... %! lognrnd (1, 2, 2, 1.5, 5) %!error ... %! lognrnd (2, ones (2), 3) %!error ... %! lognrnd (2, ones (2), [3, 2]) %!error ... %! lognrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/mnpdf.m000066400000000000000000000077661475240274700214400ustar00rootroot00000000000000## Copyright (C) 2012 Arno Onken ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} mnpdf (@var{x}, @var{pk}) ## ## Multinomial probability density function (PDF). ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{x} is vector with a single sample of a multinomial distribution with ## parameter @var{pk} or a matrix of random samples from multinomial ## distributions. In the latter case, each row of @var{x} is a sample from a ## multinomial distribution with the corresponding row of @var{pk} being its ## parameter. ## ## @item ## @var{pk} is a vector with the probabilities of the categories or a matrix ## with each row containing the probabilities of a multinomial sample. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{y} is a vector of probabilites of the random samples @var{x} from the ## multinomial distribution with corresponding parameter @var{pk}. The parameter ## @var{n} of the multinomial distribution is the sum of the elements of each ## row of @var{x}. The length of @var{y} is the number of columns of @var{x}. ## If a row of @var{pk} does not sum to @code{1}, then the corresponding element ## of @var{y} will be @code{NaN}. ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## x = [1, 4, 2]; ## pk = [0.2, 0.5, 0.3]; ## y = mnpdf (x, pk); ## @end group ## ## @group ## x = [1, 4, 2; 1, 0, 9]; ## pk = [0.2, 0.5, 0.3; 0.1, 0.1, 0.8]; ## y = mnpdf (x, pk); ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Wendy L. Martinez and Angel R. Martinez. @cite{Computational Statistics ## Handbook with MATLAB}. Appendix E, pages 547-557, Chapman & Hall/CRC, 2001. ## ## @item ## Merran Evans, Nicholas Hastings and Brian Peacock. @cite{Statistical ## Distributions}. pages 134-136, Wiley, New York, third edition, 2000. ## @end enumerate ## ## @seealso{mnrnd} ## @end deftypefn function y = mnpdf (x, pk) # Check arguments if (nargin != 2) print_usage (); endif if (! ismatrix (x) || any (x(:) < 0 | round (x(:) != x(:)))) error ("mnpdf: X must be a matrix of non-negative integer values."); endif if (! ismatrix (pk) || any (pk(:) < 0)) error ("mnpdf: PK must be a non-empty matrix with rows of probabilities."); endif # Adjust input sizes if (! isvector (x) || ! isvector (pk)) if (isvector (x)) x = x(:)'; endif if (isvector (pk)) pk = pk(:)'; endif if (size (x, 1) == 1 && size (pk, 1) > 1) x = repmat (x, size (pk, 1), 1); elseif (size (x, 1) > 1 && size (pk, 1) == 1) pk = repmat (pk, size (x, 1), 1); endif endif # Continue argument check if (any (size (x) != size (pk))) error ("mnpdf: X and PK must have compatible sizes."); endif # Count total number of elements of each multinomial sample n = sum (x, 2); # Compute probability density function of the multinomial distribution t = x .* log (pk); t(x == 0) = 0; y = exp (gammaln (n+1) - sum (gammaln (x+1), 2) + sum (t, 2)); # Set invalid rows to NaN k = (abs (sum (pk, 2) - 1) > 1e-6); y(k) = NaN; endfunction %!test %! x = [1, 4, 2]; %! pk = [0.2, 0.5, 0.3]; %! y = mnpdf (x, pk); %! assert (y, 0.11812, 0.001); %!test %! x = [1, 4, 2; 1, 0, 9]; %! pk = [0.2, 0.5, 0.3; 0.1, 0.1, 0.8]; %! y = mnpdf (x, pk); %! assert (y, [0.11812; 0.13422], 0.001); statistics-release-1.7.3/inst/dist_fun/mnrnd.m000066400000000000000000000135251475240274700214400ustar00rootroot00000000000000## Copyright (C) 2012 Arno Onken ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} mnrnd (@var{n}, @var{pk}) ## @deftypefnx {statistics} {@var{r} =} mnrnd (@var{n}, @var{pk}, @var{s}) ## ## Random arrays from the multinomial distribution. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{n} is the first parameter of the multinomial distribution. @var{n} can ## be scalar or a vector containing the number of trials of each multinomial ## sample. The elements of @var{n} must be non-negative integers. ## ## @item ## @var{pk} is the second parameter of the multinomial distribution. @var{pk} ## can be a vector with the probabilities of the categories or a matrix with ## each row containing the probabilities of a multinomial sample. If @var{pk} ## has more than one row and @var{n} is non-scalar, then the number of rows of ## @var{pk} must match the number of elements of @var{n}. ## ## @item ## @var{s} is the number of multinomial samples to be generated. @var{s} must ## be a non-negative integer. If @var{s} is specified, then @var{n} must be ## scalar and @var{pk} must be a vector. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{r} is a matrix of random samples from the multinomial distribution with ## corresponding parameters @var{n} and @var{pk}. Each row corresponds to one ## multinomial sample. The number of columns, therefore, corresponds to the ## number of columns of @var{pk}. If @var{s} is not specified, then the number ## of rows of @var{r} is the maximum of the number of elements of @var{n} and ## the number of rows of @var{pk}. If a row of @var{pk} does not sum to ## @code{1}, then the corresponding row of @var{r} will contain only @code{NaN} ## values. ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## n = 10; ## pk = [0.2, 0.5, 0.3]; ## r = mnrnd (n, pk); ## @end group ## ## @group ## n = 10 * ones (3, 1); ## pk = [0.2, 0.5, 0.3]; ## r = mnrnd (n, pk); ## @end group ## ## @group ## n = (1:2)'; ## pk = [0.2, 0.5, 0.3; 0.1, 0.1, 0.8]; ## r = mnrnd (n, pk); ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Wendy L. Martinez and Angel R. Martinez. @cite{Computational Statistics ## Handbook with MATLAB}. Appendix E, pages 547-557, Chapman & Hall/CRC, 2001. ## ## @item ## Merran Evans, Nicholas Hastings and Brian Peacock. @cite{Statistical ## Distributions}. pages 134-136, Wiley, New York, third edition, 2000. ## @end enumerate ## ## @seealso{mnpdf} ## @end deftypefn function r = mnrnd (n, pk, s) # Check arguments if (nargin == 3) if (! isscalar (n) || n < 0 || round (n) != n) error ("mnrnd: N must be a non-negative integer."); endif if (! isvector (pk) || any (pk < 0 | pk > 1)) error ("mnrnd: PK must be a vector of probabilities."); endif if (! isscalar (s) || s < 0 || round (s) != s) error ("mnrnd: S must be a non-negative integer."); endif elseif (nargin == 2) if (isvector (pk) && size (pk, 1) > 1) pk = pk'; endif if (! isvector (n) || any (n < 0 | round (n) != n) || size (n, 2) > 1) error ("mnrnd: N must be a non-negative integer column vector."); endif if (! ismatrix (pk) || isempty (pk) || any (pk < 0 | pk > 1)) error (strcat (["mnrnd: PK must be a non-empty matrix with"], ... [" rows of probabilities."])); endif if (! isscalar (n) && size (pk, 1) > 1 && length (n) != size (pk, 1)) error ("mnrnd: the length of N must match the number of rows of PK."); endif else print_usage (); endif # Adjust input sizes if (nargin == 3) n = n * ones (s, 1); pk = repmat (pk(:)', s, 1); elseif (nargin == 2) if (isscalar (n) && size (pk, 1) > 1) n = n * ones (size (pk, 1), 1); elseif (size (pk, 1) == 1) pk = repmat (pk, length (n), 1); endif endif sz = size (pk); # Upper bounds of categories ub = cumsum (pk, 2); # Make sure that the greatest upper bound is 1 gub = ub(:, end); ub(:, end) = 1; # Lower bounds of categories lb = [zeros(sz(1), 1) ub(:, 1:(end-1))]; # Draw multinomial samples r = zeros (sz); for i = 1:sz(1) # Draw uniform random numbers r_tmp = repmat (rand (n(i), 1), 1, sz(2)); # Compare the random numbers of r_tmp to the cumulated probabilities of pk # and count the number of samples for each category r(i, :) = sum (r_tmp <= repmat (ub(i, :), n(i), 1) & ... r_tmp > repmat (lb(i, :), n(i), 1), 1); endfor # Set invalid rows to NaN k = (abs (gub - 1) > 1e-6); r(k, :) = NaN; endfunction %!test %! n = 10; %! pk = [0.2, 0.5, 0.3]; %! r = mnrnd (n, pk); %! assert (size (r), size (pk)); %! assert (all (r >= 0)); %! assert (all (round (r) == r)); %! assert (sum (r) == n); %!test %! n = 10 * ones (3, 1); %! pk = [0.2, 0.5, 0.3]; %! r = mnrnd (n, pk); %! assert (size (r), [length(n), length(pk)]); %! assert (all (r >= 0)); %! assert (all (round (r) == r)); %! assert (all (sum (r, 2) == n)); %!test %! n = (1:2)'; %! pk = [0.2, 0.5, 0.3; 0.1, 0.1, 0.8]; %! r = mnrnd (n, pk); %! assert (size (r), size (pk)); %! assert (all (r >= 0)); %! assert (all (round (r) == r)); %! assert (all (sum (r, 2) == n)); statistics-release-1.7.3/inst/dist_fun/mvncdf.m000066400000000000000000000403751475240274700216020ustar00rootroot00000000000000## Copyright (C) 2008 Arno Onken ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} mvncdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} mvncdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} mvncdf (@var{x_lo}, @var{x_up}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} mvncdf (@dots{}, @var{options}) ## @deftypefnx {statistics} {[@var{p}, @var{err}] =} mvncdf (@dots{}) ## ## Multivariate normal cumulative distribution function (CDF). ## ## @code{@var{p} = mvncdf (@var{x})} returns the cumulative probability of the ## multivariate normal distribution evaluated at each row of @var{x} with zero ## mean and an identity covariance matrix. The rows of matrix @var{x} ## correspond to observations and its columns to variables. The return argument ## @var{p} is a column vector with the same number of rows as in @var{x}. ## ## @code{@var{p} = mvncdf (@var{x}, @var{mu}, @var{sigma})} returns cumulative ## probability of the multivariate normal distribution evaluated at each row of ## @var{x} with mean @var{mu} and a covariance matrix @var{sigma}. @var{mu} can ## be either a scalar (the same of every variable) or a row vector with the same ## number of elements as the number of variables in @var{x}. @var{sigma} ## covariance matrix may be specified a row vector if it only contains variances ## along its diagonal and zero covariances of the diagonal. In such a case, the ## diagonal vector @var{sigma} must have the same number of elements as the ## number of variables (columns) in @var{x}. If you only want to specify sigma, ## you can pass an empty matrix for @var{mu}. ## ## The multivariate normal cumulative probability at @var{x} is defined as the ## probability that a random vector @math{V}, distributed as multivariate ## normal, will fall within the semi-infinite rectangle with upper limits ## defined by @var{x}. ## @itemize ## @item @math{Pr@{V(1)<=X(1), V(2)<=X(2), ... V(D)<=X(D)@}}. ## @end itemize ## ## @code{@var{p} = mvncdf (@var{x_lo}, @var{x_hi}, @var{mu}, @var{sigma})} ## returns the multivariate normal cumulative probability evaluated over the ## rectangle (hyper-rectangle for multivariate data in @var{x}) with lower and ## upper limits defined by @var{x_lo} and @var{x_hi}, respectively. ## ## @code{[@var{p}, @var{err}] = mvncdf (@dots{})} also returns an error estimate ## @var{err} in @var{p}. ## ## @code{@var{p} = mvncdf (@dots{}, @var{options})} specifies the structure, ## which controls specific parameters for the numerical integration used to ## compute @var{p}. The required fieds are: ## ## @multitable @columnfractions 0.2 0.05 0.75 ## @item @qcode{"TolFun"} @tab @tab Maximum absolute error tolerance. Default ## is 1e-8 for D < 4, or 1e-4 for D >= 4. Note that for bivariate normal cdf, ## the Octave implementation has a presicion of more than 1e-10. ## ## @item @qcode{"MaxFunEvals"} @tab @tab Maximum number of integrand ## evaluations. Default is 1e7 for D > 4. ## ## @item @qcode{"Display"} @tab @tab Display options. Choices are @qcode{"off"} ## (default), @qcode{"iter"}, which shows the probability and estimated error at ## each repetition, and @qcode{"final"}, which shows the final probability and ## related error after the integrand has converged successfully. ## @end multitable ## ## @seealso{bvncdf, mvnpdf, mvnrnd} ## @end deftypefn function [p, err] = mvncdf (varargin) ## Check for valid number on input and output arguments narginchk (1,5); ## Check for 'options' structure and parse parameters or add defaults if (isstruct (varargin{end})) if (isfield (varargin{end}, "TolFun")) TolFun = varargin{end}.TolFun; else error ("mvncdf: options structure missing 'TolFun' field."); endif if (isempty (TolFun) && size (varargin{1}, 2) < 4) TolFun = 1e-8; elseif (isempty (TolFun) && size (varargin{1}, 2) < 26) TolFun = 1e-4; endif if (isfield (varargin{end}, "MaxFunEvals")) MaxFunEvals = varargin{end}.MaxFunEvals; else error ("mvncdf: options structure missing 'MaxFunEvals' field."); endif if (isempty (MaxFunEvals)) MaxFunEvals = 1e7; endif if (isfield (varargin{end}, "Display")) Display = varargin{end}.Display; else error ("mvncdf: options structure missing 'Display' field."); endif DispOptions = {"off", "final", "iter"}; if (sum (any (strcmpi (Display, DispOptions))) == 0) error ("mvncdf: 'Display' field in 'options' has invalid value."); endif rem_nargin = nargin - 1; else if (size (varargin{1}, 2) < 4) TolFun = 1e-8; elseif (size (varargin{1}, 2) < 26) TolFun = 1e-4; endif MaxFunEvals = 1e7; Display = "off"; rem_nargin = nargin; endif ## Check for X of X_lo and X_up if (rem_nargin < 4) # MVNCDF(X_UP,MU,SIGMA) x_up_Only = true; x_up = varargin{1}; ## Check for x being a matrix if (! ismatrix (x_up)) error ("mvncdf: X must be a matrix."); endif ## Create x_lo according to data type of x_lo x_lo = - Inf (size (x_up)); if isa (x_up, "single") x_lo = single (x_lo); endif ## Check for mu and sigma arguments if (rem_nargin > 1) mu = varargin{2}; else mu = []; endif if (rem_nargin > 2) sigma = varargin{3}; else sigma = []; endif else # MVNCDF(X_LO,X_UP,MU,SIGMA) x_up_Only = false; x_lo = varargin{1}; x_up = varargin{2}; mu = varargin{3}; sigma = varargin{4}; ## Check for x_lo and x_up being matrices of the same size ## and that they define increasing limits if (! ismatrix (x_lo) || ! ismatrix (x_up)) error ("mvncdf: X_LO and X_UP must be matrices."); endif if (size (x_lo) != size (x_up)) error ("mvncdf: X_LO and X_UP must have the same size."); endif if (any (any (x_lo > x_up))) error ("mvncdf: X_LO and X_UP must define increasing limits."); endif endif ## Check if data is single or double class is_type = "double"; if (isa (x_lo, "single")) is_type = "single"; endif ## Get size of data [n_x, d_x] = size (x_lo); ## Center data according to mu if (isempty (mu)) # already centered XLo0 = x_lo; XUp0 = x_up; elseif (isscalar (mu)) # mu is a scalar XLo0 = x_lo - mu; XUp0 = x_up - mu; elseif (isvector (mu)) # mu is a vector ## Get size of mu vector [n_mu, d_mu] = size (mu); if (d_mu != d_x) error ("mvncdf: wrong size of MU vector."); endif if (n_mu == 1 || n_mu == n_x) XLo0 = x_lo - mu; XUp0 = x_up - mu; else error ("mvncdf: wrong size of MU vector."); endif else error ("mvncdf: MU must be either empty, a scalar, or a vector."); endif ## Check how sigma was parsed if (isempty (sigma)) # already standardized ## If x_lo and x_up are column vectors, transpose them to row vectors if (d_x == 1) XLo0 = XLo0'; XUp0 = XUp0'; [n_x, d_x] = size (XUp0); endif sigmaIsDiag = true; sigma = ones (1, d_x); else ## Check if sigma parsed as diagonal vector if (size (sigma, 1) == 1 && size (sigma, 2) > 1) sigmaIsDiag = true; else sigmaIsDiag = false; endif ## If x_lo and x_up are column vectors, transpose them to row vectors if (d_x == 1) if (isequal (size (sigma), [1, n_x])) XLo0 = XLo0'; XUp0 = XUp0'; [n_x, d_x] = size (XUp0); elseif (! isscalar (mu)) error ("mvncdf: MU must be a scalar if SIGMA is a vector."); endif endif ## Check for sigma being a valid covariance matrix if (! sigmaIsDiag && (size (sigma, 1) != size (sigma, 2))) error ("mvncdf: covariance matrix SIGMA is not square."); elseif (! sigmaIsDiag && (! all (size (sigma) == [d_x, d_x]))) error (strcat (["mvncdf: covariance matrix SIGMA does"], ... [" not match dimensions in data."])); else ## If sigma is a covariance matrix check that it is positive semi-definite if (! sigmaIsDiag) [~, err] = chol (sigma); if (err != 0) error (strcat (["mvncdf: covariance matrix SIGMA must be"], ... [" positive semi-definite."])); endif else if (any (sigma) <= 0) error ("mvncdf: invalid SIGMA diagonal vector."); endif endif endif endif ## Standardize sigma and x data if (sigmaIsDiag) XLo0 = XLo0 ./ sqrt (sigma); XUp0 = XUp0 ./ sqrt (sigma); else s = sqrt (diag (sigma))'; XLo0 = XLo0 ./ s; XUp0 = XUp0 ./ s; Rho = sigma ./ (s * s'); endif ## Compute the cdf from standardized values. if (d_x == 1) p = normcdf (XUp0, 0, 1) - normcdf (XLo0, 0, 1); if (nargout > 1) err = NaN (size (p), is_type); endif elseif (sigmaIsDiag) p = prod (normcdf (XUp0, 0, 1) - normcdf (XLo0, 0, 1), 2); if (nargout > 1) err = NaN (size (p), is_type); endif elseif (d_x < 4) if (x_up_Only) # upper limit only if (d_x == 2) p = bvncdf (x_up, mu, sigma); else p = tvncdf (XUp0, Rho([2 3 6]), TolFun); endif else # lower and upper limits present ## Compute the probability over the rectangle as sums and differences ## of integrals over semi-infinite half-rectangles. For degenerate ## rectangles, force an exact zero by making each piece exactly zero. equalLimits = (XUp0 == XLo0); XUp0(equalLimits) = -Inf; XLo0(equalLimits) = -Inf; ## For bvncdf x_up(equalLimits) = -Inf; x_lo(equalLimits) = -Inf; p = zeros(n_x, 1, is_type); for i = 0:d_x k = nchoosek (1:d_x, i); for j = 1:size (k, 1) X = XUp0; X(:,k(j,:)) = XLo0(:,k(j,:)); if d_x == 2 x = x_up; x(:,k(j,:)) = x_lo(:,k(j,:)); p = p + (-1) ^ i * bvncdf (x, mu, sigma); else p = p + (-1) ^ i * tvncdf (X, Rho([2 3 6]), TolFun / 8); endif endfor endfor endif if (nargout > 1) err = repmat (cast (TolFun, is_type), size (p)); endif elseif (d_x < 26) p = zeros (n_x, 1, is_type); err = zeros (n_x, 1, is_type); for i = 1:n_x [p(i), err(i)] = mvtcdfqmc (XLo0(i,:), XUp0(i,:), Rho, Inf, ... TolFun, MaxFunEvals, Display); endfor else error ("mvncdf: too many dimensions in data (limit = 25 columns)."); endif ## Bound p in range [0, 1] p(p < 0) = 0; p(p > 1) = 1; endfunction ## function for computing a trivariate normal cdf function p = tvncdf (x, rho, tol) ## Get size of data n = size(x,1); ## Check if data is single or double class is_type = "double"; if (isa (x, "single") || isa (rho, "single")) is_type = "single"; endif ## Find a permutation that makes rho_32 == max(rho) [dum,imax] = max(abs(rho)); %#ok if imax == 1 % swap 1 and 3 rho_21 = rho(3); rho_31 = rho(2); rho_32 = rho(1); x = x(:,[3 2 1]); elseif imax == 2 % swap 1 and 2 rho_21 = rho(1); rho_31 = rho(3); rho_32 = rho(2); x = x(:,[2 1 3]); else % imax == 3 rho_21 = rho(1); rho_31 = rho(2); rho_32 = rho(3); end phi = 0.5 * erfc (- x(:,1) / sqrt (2)); p1 = phi .* bvncdf (x(:,2:3), [], rho_32); if abs(rho_21) > 0 loLimit = 0; hiLimit = asin(rho_21); rho_j1 = rho_21; rho_k1 = rho_31; p2 = zeros (size (p1), is_type); for i = 1:n b1 = x(i,1); bj = x(i,2); bk = x(i,3); if isfinite(b1) && isfinite(bj) && ~isnan(bk) p2(i) = quadgk(@tvnIntegrand,loLimit,hiLimit,'AbsTol',tol/3,'RelTol',0); endif endfor else p2 = zeros (size (p1), is_type); endif if abs(rho_31) > 0 loLimit = 0; hiLimit = asin(rho_31); rho_j1 = rho_31; rho_k1 = rho_21; p3 = zeros (size (p1), is_type); for i = 1:n b1 = x(i,1); bj = x(i,3); bk = x(i,2); if isfinite(b1) && isfinite(bj) && ~isnan(bk) p3(i) = quadgk(@tvnIntegrand,loLimit,hiLimit,'AbsTol',tol/3,'RelTol',0); endif endfor else p3 = zeros (size (p1), is_type); endif p = cast (p1 + (p2 + p3) ./ (2 .* pi), is_type); function integrand = tvnIntegrand(theta) # Integrand is exp( -(b1.^2 + bj.^2 - 2*b1*bj*sin(theta))/(2*cos(theta).^2)) sintheta = sin (theta); cossqtheta = cos (theta) .^ 2; expon = ((b1 * sintheta - bj) .^ 2 ./ cossqtheta + b1 .^ 2) / 2; sinphi = sintheta .* rho_k1 ./ rho_j1; numeru = bk .* cossqtheta - b1 .* (sinphi - rho_32 .* sintheta) ... - bj .* (rho_32 - sintheta .* sinphi); denomu = sqrt (cossqtheta .* (cossqtheta - sinphi .* sinphi ... - rho_32 .* (rho_32 - 2 .* sintheta .* sinphi))); phi = 0.5 * erfc (- (numeru ./ denomu) / sqrt (2)); integrand = exp (- expon) .* phi; endfunction endfunction %!demo %! mu = [1, -1]; %! Sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1, X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! X = [X1(:), X2(:)]; %! p = mvncdf (X, mu, Sigma); %! Z = reshape (p, 25, 25); %! surf (X1, X2, Z); %! title ("Bivariate Normal Distribution"); %! ylabel "X1" %! xlabel "X2" %!demo %! mu = [0, 0]; %! Sigma = [0.25, 0.3; 0.3, 1]; %! p = mvncdf ([0 0], [1 1], mu, Sigma); %! x1 = -3:.2:3; %! x2 = -3:.2:3; %! [X1, X2] = meshgrid (x1, x2); %! X = [X1(:), X2(:)]; %! p = mvnpdf (X, mu, Sigma); %! p = reshape (p, length (x2), length (x1)); %! contour (x1, x2, p, [0.0001, 0.001, 0.01, 0.05, 0.15, 0.25, 0.35]); %! xlabel ("x"); %! ylabel ("p"); %! title ("Probability over Rectangular Region"); %! line ([0, 0, 1, 1, 0], [1, 0, 0, 1, 1], "Linestyle", "--", "Color", "k"); %!test %! fD = (-2:2)'; %! X = repmat (fD, 1, 4); %! p = mvncdf (X); %! assert (p, [0; 0.0006; 0.0625; 0.5011; 0.9121], ones (5, 1) * 1e-4); %!test %! mu = [1, -1]; %! Sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1,X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! X = [X1(:), X2(:)]; %! p = mvncdf (X, mu, Sigma); %! p_out = [0.00011878988774500, 0.00034404112322371, ... %! 0.00087682502191813, 0.00195221905058185, ... %! 0.00378235566873474, 0.00638175749734415, ... %! 0.00943764224329656, 0.01239164888125426, ... %! 0.01472750274376648, 0.01623228313374828]'; %! assert (p([1:10]), p_out, 1e-16); %!test %! mu = [1, -1]; %! Sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1,X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! X = [X1(:), X2(:)]; %! p = mvncdf (X, mu, Sigma); %! p_out = [0.8180695783608276, 0.8854485749482751, ... %! 0.9308108777385832, 0.9579855743025508, ... %! 0.9722897881414742, 0.9788150170059926, ... %! 0.9813597788804785, 0.9821977956568989, ... %! 0.9824283794464095, 0.9824809345614861]'; %! assert (p([616:625]), p_out, 3e-16); %!test %! mu = [0, 0]; %! Sigma = [0.25, 0.3; 0.3, 1]; %! [p, err] = mvncdf ([0, 0], [1, 1], mu, Sigma); %! assert (p, 0.2097424404755626, 1e-16); %! assert (err, 1e-08); %!test %! x = [1 2]; %! mu = [0.5 1.5]; %! sigma = [1.0, 0.5; 0.5, 1.0]; %! p = mvncdf (x, mu, sigma); %! assert (p, 0.546244443857090, 1e-15); %!test %! x = [1 2]; %! mu = [0.5 1.5]; %! sigma = [1.0, 0.5; 0.5, 1.0]; %! a = [-inf 0]; %! p = mvncdf (a, x, mu, sigma); %! assert (p, 0.482672935215631, 1e-15); %!error p = mvncdf (randn (25,26), [], eye (26)); %!error p = mvncdf (randn (25,8), [], eye (9)); %!error p = mvncdf (randn (25,4), randn (25,5), [], eye (4)); %!error p = mvncdf (randn (25,4), randn (25,4), [2, 3; 2, 3], eye (4)); %!error p = mvncdf (randn (25,4), randn (25,4), ones (1, 5), eye (4)); %!error p = mvncdf ([-inf, 0], [1, 2], [0.5, 1.5], [1.0, 0.5; 0.5, 1.0], option) statistics-release-1.7.3/inst/dist_fun/mvnpdf.m000066400000000000000000000177321475240274700216200ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} mvnpdf (@var{x}, @var{mu}, @var{sigma}) ## ## Multivariate normal probability density function (PDF). ## ## @code{@var{y} = mvnpdf (@var{x})} returns the probability density of the ## multivariate normal distribution with zero mean and identity covariance ## matrix, evaluated at each row of @var{x}. Rows of the N-by-D matrix @var{x} ## correspond to observations orpoints, and columns correspond to variables or ## coordinates. @var{y} is an N-by-1 vector. ## ## @code{@var{y} = mvnpdf (@var{x}, @var{mu})} returns the density of the ## multivariate normal distribution with mean MU and identity covariance matrix, ## evaluated at each row of @var{x}. @var{mu} is a 1-by-D vector, or an N-by-D ## matrix, in which case the density is evaluated for each row of @var{x} with ## the corresponding row of @var{mu}. @var{mu} can also be a scalar value, ## which MVNPDF replicates to match the size of @var{x}. ## ## @code{@var{y} = mvnpdf (@var{x}, @var{mu}, @var{sigma})} returns the density ## of the multivariate normal distribution with mean @var{mu} and covariance ## @var{sigma}, evaluated at each row of @var{x}. @var{sigma} is a D-by-D ## matrix, or an D-by-D-by-N array, in which case the density is evaluated for ## each row of @var{x} with the corresponding page of @var{sigma}, i.e., ## @code{mvnpdf} computes @var{y(i)} using @var{x(i,:)} and @var{sigma(:,:,i)}. ## If the covariance matrix is diagonal, containing variances along the diagonal ## and zero covariances off the diagonal, @var{sigma} may also be specified as a ## 1-by-D matrix or a 1-by-D-by-N array, containing just the diagonal. Pass in ## the empty matrix for @var{mu} to use its default value when you want to only ## specify @var{sigma}. ## ## If @var{x} is a 1-by-D vector, @code{mvnpdf} replicates it to match the ## leading dimension of @var{mu} or the trailing dimension of @var{sigma}. ## ## @seealso{mvncdf, mvnrnd} ## @end deftypefn function y = mvnpdf (x, mu, sigma) ## Check for valid number of input arguments if (nargin < 1) error ("mvnpdf: too few input arguments."); endif if (nargin < 3) sigma = []; endif ## Check for valid size of data [row, col] = size (x); if (col < 1) error ("mvnpdf: too few dimensions in X."); elseif (ndims (x) != 2) error ("mvnpdf: wrong dimensions in X."); endif ## Check for second input argument or assume zero mean if (nargin < 2 || isempty (mu)) xc = x; # already centered elseif (numel (mu) == 1) xc = x - mu; # mu is a scalar elseif (ndims (mu) == 2) [rm, cm] = size (mu); # mu is a vector if (cm != col) error ("mvnpdf: columns in X and MU mismatch."); elseif (rm == row) xc = x - mu; elseif (rm == 1 || row == 1) xc = bsxfun (@minus, x, mu); else error ("mvnpdf: rows in X and MU mismatch."); endif else error ("mvnpdf: wrong size of MU."); endif [row, col] = size (xc); ## Check for third input argument or assume identity covariance if (nargin < 2 || isempty (sigma)) ## already standardized if (col == 1 && row > 1) xRinv = xc'; # make row vector col == row; else xRinv = xc; col == row; endif lnSDS = 0; elseif (ndims (sigma) == 2) ## Single covariance matrix [rs, cs] = size (sigma); if (rs == 1 && cs > 1) rs = cs; # sigma passed as a diagonal is_diag = true; else is_diag = false; endif if (col == 1 && row > 1 && rs == row) xc = xc'; # make row vector col = row; endif ## Check sigma for correct size if (rs != cs) error ("mvnpdf: bad covariance matrix."); elseif (rs != col) error ("mvnpdf: covariance matrix mismatch."); else if (is_diag) ## Check sigma for invalid values if (any (sigma <= 0)) error ("mvnpdf: sigma diagonal contains negative or zero values."); endif R = sqrt (sigma); xRinv = bsxfun (@rdivide, xc, R); lnSDS = sum (log (R)); else ## Check for valid covariance matrix [R, err] = cholcov (sigma, 0); if (err != 0) error ("mvnpdf: invalid covariance matrix."); endif xRinv = xc / R; lnSDS = sum (log (diag (R))); endif endif elseif (ndims (sigma) == 3) ## Multiple covariance matrices sd = size (sigma); if (sd(1) == 1 && sd(2) > 1) sd(1) = sd(2); # sigma passed as a diagonal sigma = reshape (sigma, sd(2), sd(3))'; is_diag = true; else is_diag = false; endif if (col == 1 && row > 1 && sd(1) == row) xc = xc'; # make row vector [row, col] = size (xc); endif ## If X and MU are row vectors, match them with covariance if (row == 1) row = sd(3); xc = repmat (xc, row, 1); endif ## Check sigma for correct size if (sd(1) != sd(2)) error ("mvnpdf: bad multiple covariance matrix."); elseif (sd(1) != col || sd(2) != col) error ("mvnpdf: multiple covariance matrix mismatch."); elseif (sd(3) != row) error ("mvnpdf: multiple covariance pages mismatch."); else if (is_diag) ## Check sigma for invalid values if (any (any (sigma <= 0))) error ("mvnpdf: sigma diagonals contain negative or zero values."); endif R = sqrt (sigma); xRinv = xc ./ R; lnSDS = sum (log (R), 2); else ## Create arrays according to class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")) xRinv = zeros (row, col," single"); lnSDS = zeros (row, 1, "single"); else xRinv = zeros (row, col); lnSDS = zeros (row, 1); endif for i = 1:row ## Check for valid covariance matrices [R, err] = cholcov (sigma (:,:,i), 0); if (err != 0) error ("mvnpdf:invalid multiple covariance matrix."); endif xRinv(i,:) = xc(i,:) / R; lnSDS(i) = sum(log(diag(R))); endfor endif endif else error ("mvnpdf: wrong dimensions in covariance matrix."); endif ## Compute the PDF y = exp (-0.5 * sum (xRinv .^ 2, 2) - lnSDS - col * log (2 * pi) / 2); endfunction %!demo %! mu = [1, -1]; %! sigma = [0.9, 0.4; 0.4, 0.3]; %! [X1, X2] = meshgrid (linspace (-1, 3, 25)', linspace (-3, 1, 25)'); %! x = [X1(:), X2(:)]; %! p = mvnpdf (x, mu, sigma); %! surf (X1, X2, reshape (p, 25, 25)); ## Input validation tests %!error y = mvnpdf (); %!error y = mvnpdf ([]); %!error y = mvnpdf (ones (3,3,3)); %!error ... %! y = mvnpdf (ones (10, 2), [4, 2, 3]); %!error ... %! y = mvnpdf (ones (10, 2), [4, 2; 3, 2]); %!error ... %! y = mvnpdf (ones (10, 2), ones (3, 3, 3)); ## Output validation tests %!shared x, mu, sigma %! x = [1, 2, 5, 4, 6]; %! mu = [2, 0, -1, 1, 4]; %! sigma = [2, 2, 2, 2, 2]; %!assert (mvnpdf (x), 1.579343404440977e-20, 1e-30); %!assert (mvnpdf (x, mu), 1.899325144348102e-14, 1e-25); %!assert (mvnpdf (x, mu, sigma), 2.449062307156273e-09, 1e-20); statistics-release-1.7.3/inst/dist_fun/mvnrnd.m000066400000000000000000000162511475240274700216250ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} mvnrnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} mvnrnd (@var{mu}, @var{sigma}, @var{n}) ## @deftypefnx {statistics} {@var{r} =} mvnrnd (@var{mu}, @var{sigma}, @var{n}, @var{T}) ## @deftypefnx {statistics} {[@var{r}, @var{T}] =} mvnrnd (@dots{}) ## ## Random vectors from the multivariate normal distribution. ## ## @code{@var{r} = mvnrnd (@var{mu}, @var{sigma})} returns an N-by-D matrix ## @var{r} of random vectors chosen from the multivariate normal distribution ## with mean vector @var{mu} and covariance matrix @var{sigma}. @var{mu} is an ## N-by-D matrix, and @code{mvnrnd} generates each N of @var{r} using the ## corresponding N of @var{mu}. @var{sigma} is a D-by-D symmetric positive ## semi-definite matrix, or a D-by-D-by-N array. If @var{sigma} is an array, ## @code{mvnrnd} generates each N of @var{r} using the corresponding page of ## @var{sigma}, i.e., @code{mvnrnd} computes @var{r(i,:)} using @var{mu(i,:)} ## and @var{sigma(:,:,i)}. If the covariance matrix is diagonal, containing ## variances along the diagonal and zero covariances off the diagonal, ## @var{sigma} may also be specified as a 1-by-D matrix or a 1-by-D-by-N array, ## containing just the diagonal. If @var{mu} is a 1-by-D vector, @code{mvnrnd} ## replicates it to match the trailing dimension of SIGMA. ## ## @code{@var{r} = mvnrnd (@var{mu}, @var{sigma}, @var{n})} returns a N-by-D ## matrix R of random vectors chosen from the multivariate normal distribution ## with 1-by-D mean vector @var{mu}, and D-by-D covariance matrix @var{sigma}. ## ## @code{@var{r} = mvnrnd (@var{mu}, @var{sigma}, @var{n}, @var{T})} supplies ## the Cholesky factor @var{T} of @var{sigma}, so that @var{sigma(:,:,J)} == ## @var{T(:,:,J)}'*@var{T(:,:,J)} if @var{sigma} is a 3D array or @var{sigma} == ## @var{T}'*@var{T} if @var{sigma} is a matrix. No error checking is done on ## @var{T}. ## ## @code{[@var{r}, @var{T}] = mvnrnd (@dots{})} returns the Cholesky factor ## @var{T}, so it can be re-used to make later calls more efficient, although ## there are greater efficiency gains when SIGMA can be specified as a diagonal ## instead. ## ## @seealso{mvncdf, mvnpdf} ## @end deftypefn function [r, T] = mvnrnd (mu, sigma, N, T) ## Check input arguments if (nargin < 2 || isempty (mu) || isempty (sigma)) error ("mvnrnd: too few input arguments."); elseif (ndims (mu) > 2) error ("mvnrnd: wrong size of MU."); elseif (ndims (sigma) > 3) error ("mvnrnd: wrong size of SIGMA."); endif ## Get data type if (isa (mu, "single") || isa (sigma, "single")) is_class = "single"; else is_class = "double"; endif ## Check whether sigma is passed as a diagonal or a matrix sd = size (sigma); if (sd(1) == 1 && sd(2) > 1) sd(1) = sd(2); is_diag = true; else is_diag = false; endif ## Get size of mean vector [rm, cm] = size (mu); ## Make sure MU is a row vector if (cm == 1 && rm == sd(1)) mu = mu'; [rm, cm] = size (mu); endif ## Check for valid N input argument if (nargin < 3 || isempty (N)) N_empty = true; else N_empty = false; ## If MU is a row vector, rep it out to match N if (rm == 1) rm = N; mu = repmat (mu, rm, 1); elseif (rm != N) error ("mvnrnd: size mismatch of N and MU."); endif endif ## For single covariance matrix if (ndims (sigma) == 2) ## Check sigma for correct size if (sd(1) != sd(2)) error ("mvnpdf: bad covariance matrix."); elseif (! sd(1) == cm) error ("mvnpdf: covariance matrix mismatch."); endif ## Check for Cholesky factor T if (nargin > 3) r = randn (rm, size (T, 1), is_class) * T + mu; elseif (is_diag) ## Check sigma for invalid values if (any (sigma <= 0)) error ("mvnpdf: SIGMA diagonal contains negative or zero values."); endif t = sqrt (sigma); if (nargout > 1) T = diag (t); endif r = bsxfun (@times, randn (rm, cm, is_class), t) + mu; else ## Compute a Cholesky factorization [T, err] = cholcov (sigma); if (err != 0) error ("mvnrnd: covariance matrix is not positive definite."); endif r = randn (rm, size (T, 1), is_class) * T + mu; endif endif ## For multiple covariance matrices if (ndims (sigma) == 3) ## If MU is a row vector, rep it out to match sigma if (rm == 1 && N_empty) rm = sd(3); mu = repmat (mu, rm, 1); endif ## Check sigma for correct size if (sd(1) != sd(2)) error ("mvnpdf: bad multiple covariance matrix."); elseif (sd(1) != cm) error ("mvnpdf: multiple covariance matrix mismatch."); elseif (sd(3) != rm) error ("mvnpdf: multiple covariance pages mismatch."); endif ## Check for Cholesky factor T if (nargin < 4) # T not present if (nargout > 1) T = zeros (sd, is_class); endif if (is_diag) sigma = reshape(sigma,sd(2),sd(3))'; ## Check sigma for invalid values if (any (sigma(:) <= 0)) error ("mvnpdf: SIGMA diagonals contain negative or zero values."); endif R = sqrt(sigma); r = bsxfun (@times, randn (rm, cm, is_class), R) + mu; if (nargout > 1) for i = 1:rm T(:,:,i) = diag (R(i,:)); endfor endif else r = zeros (rm, cm, is_class); for i = 1:rm [R, err] = cholcov (sigma(:,:,i)); if (err != 0) error (strcat (["mvnrnd: multiple covariance matrix"], ... [" is not positive definite."])); endif Rrows = size (R,1); r(i,:) = randn (1, Rrows, is_class) * R + mu(i,:); if (nargout > 1) T(1:Rrows,:,i) = R; endif endfor endif else # T present r = zeros (rm, cm, is_class); for i = 1:rm r(i,:) = randn (1, cm, is_class) * T(:,:,i) + mu(i,:); endfor endif endif endfunction ## Test input validation %!error mvnrnd () %!error mvnrnd ([2, 3, 4]) %!error mvnrnd (ones (2, 2, 2), ones (1, 2, 3, 4)) %!error mvnrnd (ones (1, 3), ones (1, 2, 3, 4)) ## Output validation tests %!assert (size (mvnrnd ([2, 3, 4], [2, 2, 2])), [1, 3]) %!assert (size (mvnrnd ([2, 3, 4], [2, 2, 2], 10)), [10, 3]) statistics-release-1.7.3/inst/dist_fun/mvtcdf.m000066400000000000000000000344111475240274700216020ustar00rootroot00000000000000## Copyright (C) 2008 Arno Onken ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} mvtcdf (@var{x}, @var{rho}, @var{df}) ## @deftypefnx {statistics} {@var{p} =} mvncdf (@var{x_lo}, @var{x_up}, @var{rho}, @var{df}) ## @deftypefnx {statistics} {@var{p} =} mvncdf (@dots{}, @var{options}) ## @deftypefnx {statistics} {[@var{p}, @var{err}] =} mvncdf (@dots{}) ## ## Multivariate Student's t cumulative distribution function (CDF). ## ## @code{@var{p} = mvtcdf (@var{x}, @var{rho}, @var{df})} returns the cumulative ## probability of the multivariate student's t distribution with correlation ## parameters @var{rho} and degrees of freedom @var{df}, evaluated at each row ## of @var{x}. The rows of the @math{NxD} matrix @var{x} correspond to sample ## observations and its columns correspond to variables or coordinates. The ## return argument @var{p} is a column vector with the same number of rows as in ## @var{x}. ## ## @var{rho} is a symmetric, positive definite, @math{DxD} correlation matrix. ## @var{dF} is a scalar or a vector with @math{N} elements. ## ## Note: @code{mvtcdf} computes the CDF for the standard multivariate Student's ## t distribution, centered at the origin, with no scale parameters. If ## @var{rho} is a covariance matrix, i.e. @code{diag(@var{rho})} is not all ## ones, @code{mvtcdf} rescales @var{rho} to transform it to a correlation ## matrix. @code{mvtcdf} does not rescale @var{x}, though. ## ## The multivariate Student's t cumulative probability at @var{x} is defined as ## the probability that a random vector T, distributed as multivariate normal, ## will fall within the semi-infinite rectangle with upper limits defined by ## @var{x}. ## @itemize ## @item @math{Pr@{T(1)<=X(1), T(2)<=X(2), ... T(D)<=X(D)@}}. ## @end itemize ## ## @code{@var{p} = mvtcdf (@var{x_lo}, @var{x_hi}, @var{rho}, @var{df})} returns ## the multivariate Student's t cumulative probability evaluated over the ## rectangle (hyper-rectangle for multivariate data in @var{x}) with lower and ## upper limits defined by @var{x_lo} and @var{x_hi}, respectively. ## ## @code{[@var{p}, @var{err}] = mvtcdf (@dots{})} also returns an error estimate ## @var{err} in @var{p}. ## ## @code{@var{p} = mvtcdf (@dots{}, @var{options})} specifies the structure, ## which controls specific parameters for the numerical integration used to ## compute @var{p}. The required fieds are: ## ## @multitable @columnfractions 0.2 0.05 0.75 ## @item @qcode{"TolFun"} @tab @tab Maximum absolute error tolerance. Default ## is 1e-8 for D < 4, or 1e-4 for D >= 4. ## ## @item @qcode{"MaxFunEvals"} @tab @tab Maximum number of integrand evaluations ## when @math{D >= 4}. Default is 1e7. Ignored when @math{D < 4}. ## ## @item @qcode{"Display"} @tab @tab Display options. Choices are @qcode{"off"} ## (default), @qcode{"iter"}, which shows the probability and estimated error at ## each repetition, and @qcode{"final"}, which shows the final probability and ## related error after the integrand has converged successfully. Ignored when ## @math{D < 4}. ## @end multitable ## ## @seealso{bvtcdf, mvtpdf, mvtrnd, mvtcdfqmc} ## @end deftypefn function [p, err] = mvtcdf (varargin) ## Check for valid number on input and output arguments narginchk (3,5); ## Check for 'options' structure and parse parameters or add defaults if (isstruct (varargin{end})) if (isfield (varargin{end}, "TolFun")) TolFun = varargin{end}.TolFun; else error ("mvtcdf: options structure missing 'TolFun' field."); endif if (isempty (TolFun) && size (varargin{1}, 2) < 4) TolFun = 1e-8; elseif (isempty (TolFun) && size (varargin{1}, 2) < 26) TolFun = 1e-4; endif if (isfield (varargin{end}, "MaxFunEvals")) MaxFunEvals = varargin{end}.MaxFunEvals; else error ("mvtcdf: options structure missing 'MaxFunEvals' field."); endif if (isempty (MaxFunEvals)) MaxFunEvals = 1e7; endif if (isfield (varargin{end}, "Display")) Display = varargin{end}.Display; else error ("mvtcdf: options structure missing 'Display' field."); endif DispOptions = {"off", "final", "iter"}; if (sum (any (strcmpi (Display, DispOptions))) == 0) error ("mvtcdf: 'Display' field in 'options' has invalid value."); endif rem_nargin = nargin - 1; else if (size (varargin{1}, 2) < 4) TolFun = 1e-8; elseif (size (varargin{1}, 2) < 26) TolFun = 1e-4; endif MaxFunEvals = 1e7; Display = "off"; rem_nargin = nargin; endif ## Check for X of X_lo and X_up if (rem_nargin < 4) # MVTCDF(X_UP,SIGMA,DF) x_up_Only = true; x_up = varargin{1}; ## Check for x being a matrix if (! ismatrix (x_up)) error ("mvtcdf: X must be a matrix."); endif ## Create x_lo according to data type of x_lo x_lo = - Inf (size (x_up)); if isa (x_up, "single") x_lo = single (x_lo); endif ## Get SIGMA and DF arguments rho = varargin{2}; df = varargin{3}; else # MVNCDF(X_LO,X_UP,SIGMA,DF) x_up_Only = false; x_lo = varargin{1}; x_up = varargin{2}; rho = varargin{3}; df = varargin{4}; ## Check for x_lo and x_up being matrices of the same size ## and that they define increasing limits if (! ismatrix (x_lo) || ! ismatrix (x_up)) error ("mvtcdf: X_LO and X_UP must be matrices."); endif if (any (size (x_lo) != size (x_up))) error ("mvtcdf: X_LO and X_UP must be of the same size."); endif if (any (any (x_lo > x_up))) error ("mvtcdf: X_LO and X_UP must define increasing limits."); endif endif ## Check if data is single or double class is_type = "double"; if (isa (x_up, "single") || isa (x_lo, "single") || ... isa (rho, "single") || isa (df, "single")) is_type = "single"; endif ## Get size of data [n_x, d_x] = size (x_lo); if (d_x < 1) error ("mvtcdf: too few dimensions in data."); endif ## Force univariate column vector into a row vector if ((d_x == 1) && (size (rho, 1) == n_x)) x_lo = x_lo'; x_up = x_up'; [n_x, d_x] = size (x_up); endif ## Check rho sz = size(rho); if (sz(1) != sz(2)) error ("mvtcdf: correlation matrix RHO is not square."); elseif (! isequal (sz, [d_x, d_x])) error (strcat (["mvtcdf: correlation matrix RHO does not"], ... [" match dimensions in data."])); endif ## Standardize rho to correlation if necessary (not the data) s = sqrt (diag (rho)); if (any (s != 1)) rho = rho ./ (s * s'); endif ## Continue checking rho for being a valid correlation matrix [~, err] = cholcov (rho, 0); if (err != 0) error (strcat (["mvtcdf: correlation matrix RHO must be"], ... [" positive semi-definite."])); endif ## Check df if (! isscalar (df) && ! (isvector (df) && length (df) == n_x)) error (strcat (["mvtcdf: DF must be a scalar or a vector with"], ... [" the same samples as in data."])); endif if (any (df <= 0) || ! isreal (df)) error ("mvtcdf: DF must contain only positive real numbers."); endif ## Compute the cdf if (d_x == 1) p = tcdf (x_up, df) - tcdf (x_lo, df); if (nargout > 1) err = NaN (size (p), is_type); endif elseif (d_x < 4) if (x_up_Only) # upper limit only if (d_x == 2) p = bvtcdf (x_up, rho(2), df, TolFun); else p = tvtcdf (x_up, rho([2 3 6]), df, TolFun); endif else # lower and upper limits present ## Compute the probability over the rectangle as sums and differences ## of integrals over semi-infinite half-rectangles. For degenerate ## rectangles, force an exact zero by making each piece exactly zero. equalLimits = (x_lo == x_up); x_lo(equalLimits) = -Inf; x_up(equalLimits) = -Inf; p = zeros (n_x, 1, is_type); for i = 0:d_x k = nchoosek (1:d_x, i); for j = 1:size (k, 1) X = x_up; X(:,k(j,:)) = x_lo(:,k(j,:)); if d_x == 2 p = p + (-1)^i * bvtcdf (X, rho(2), df, TolFun/4); else p = p + (-1)^i * tvtcdf (X, rho([2 3 6]), df, TolFun/8); endif endfor endfor endif if (nargout > 1) err = repmat (cast (TolFun, is_type), size (p)); endif elseif (d_x < 26) p = zeros (n_x, 1, is_type); err = zeros (n_x, 1, is_type); if (isscalar (df)) df = repmat (df, n_x, 1); endif for i = 1:n_x [p(i), err(i)] = mvtcdfqmc (x_lo(i,:), x_up(i,:), rho, df(i), ... TolFun, MaxFunEvals, Display); endfor else error ("mvncdf: too many dimensions in data (limit = 25 columns)."); endif ## Bound p in range [0, 1] p(p < 0) = 0; p(p > 1) = 1; endfunction ## CDF for the trivariate Student's T function p = tvtcdf (x, rho, df, TolFun) n_x = size (x, 1); if (isscalar (df)) df = repmat (df, n_x, 1); endif ## Find a permutation that makes rho_23 == max(rho) [~,imax] = max (abs (rho)); if (imax == 1) # swap 1 and 3 rho_12 = rho(3); rho_13 = rho(2); rho_23 = rho(1); x = x(:,[3 2 1]); elseif (imax == 2) # swap 1 and 2 rho_12 = rho(1); rho_13 = rho(3); rho_23 = rho(2); x = x(:,[2 1 3]); else # x already in correct order rho_12 = rho(1); rho_13 = rho(2); rho_23 = rho(3); endif if (rho_23 >= 0) p1 = bvtcdf ([x(:,1) min(x(:,2:3), [], 2)], 0, df, TolFun / 4); p1(any (isnan (x), 2)) = NaN; else p1 = bvtcdf (x(:,1:2), 0, df, TolFun /4) - ... bvtcdf ([x(:,1) -x(:,3)], 0, df, TolFun / 4); p1(p1 < 0) = 0; endif if (abs (rho_23) < 1) lo = asin (rho_23); hi = (sign (rho_23) + (rho_23 == 0)) .* pi ./ 2; p2 = zeros (size (p1), class (p1)); for i = 1:n_x x1 = x(i,1); x2 = x(i,2); x3 = x(i,3); if (isfinite (x2) && isfinite (x3) && ~! isnan (x1)) v = df(i); p2(i) = quadgk (@tvtIntegr1, lo, hi, "AbsTol", TolFun / 4, "RelTol", 0); endif endfor else p2 = zeros (class (p1)); endif if (abs (rho_12) > 0) lo = 0; hi = asin (rho_12); rj = rho_12; rk = rho_13; p3 = zeros (size (p1), class (p1)); for i = 1:n_x x1 = x(i,1); xj = x(i,2); xk = x(i,3); if (isfinite (x1) && isfinite (xj) && ! isnan (xk)) v = df(i); p3(i) = quadgk (@tvtIntegr2, lo, hi, "AbsTol", TolFun / 4, "RelTol", 0); endif endfor else p3 = zeros (class (p1)); endif if (abs (rho_13) > 0) lo = 0; hi = asin (rho_13); rj = rho_13; rk = rho_12; p4 = zeros (size (p1), class (p1)); for i = 1:n_x x1 = x(i,1); xj = x(i,3); xk = x(i,2); if (isfinite (x1) && isfinite (xj) && ! isnan (xk)) v = df(i); p4(i) = quadgk (@tvtIntegr2, lo, hi, "AbsTol", TolFun / 4, "RelTol", 0); endif endfor else p4 = zeros (class (p1)); endif if (isa (x, "single") || isa (rho, "single") || isa (df, "single")) p = cast (p1 + (-p2 + p3 + p4) ./ (2 .* pi), "single"); else p = cast (p1 + (-p2 + p3 + p4) ./ (2 .* pi), "double"); endif ## Functions to compute the integrands function integrand = tvtIntegr1 (theta) st = sin(theta); c2t = cos(theta) .^ 2; w = sqrt (1 ./ (1 + ((x2 * st - x3) .^ 2 ./ c2t + x2 .^ 2) / v)); integrand = w .^ v .* TCDF (x1 .* w, v); endfunction function integrand = tvtIntegr2 (theta) st = sin (theta); c2t = cos (theta) .^ 2; w = sqrt (1 ./ (1 + ((x1 *st - xj) .^ 2 ./ c2t + x1 .^ 2) / v)); integrand = w .^ v .* TCDF (uk (st, c2t) .* w, v); endfunction function uk = uk (st, c2t) sinphi = st .* rk ./ rj; numeru = xk .* c2t - x1 .* (sinphi - rho_23 .* st) ... - xj .* (rho_23 - st .* sinphi); denomu = sqrt (c2t .* (c2t - sinphi .* sinphi ... - rho_23 .* (rho_23 - 2 .* st .* sinphi))); uk = numeru ./ denomu; endfunction endfunction ## CDF for Student's T function p = TCDF (x, df) p = betainc(df ./ (df + x .^ 2), df / 2, 0.5) / 2; reflect = (x > 0); p(reflect) = 1 - p(reflect); endfunction %!demo %! ## Compute the cdf of a multivariate Student's t distribution with %! ## correlation parameters rho = [1, 0.4; 0.4, 1] and 2 degrees of freedom. %! %! rho = [1, 0.4; 0.4, 1]; %! df = 2; %! [X1, X2] = meshgrid (linspace (-2, 2, 25)', linspace (-2, 2, 25)'); %! X = [X1(:), X2(:)]; %! p = mvtcdf (X, rho, df); %! surf (X1, X2, reshape (p, 25, 25)); %! title ("Bivariate Student's t cummulative distribution function"); ## Test output against MATLAB R2018 %!test %! x = [1, 2]; %! rho = [1, 0.5; 0.5, 1]; %! df = 4; %! a = [-1, 0]; %! assert (mvtcdf(a, x, rho, df), 0.294196905339283, 1e-14); %!test %! x = [1, 2;2, 4;1, 5]; %! rho = [1, 0.5; 0.5, 1]; %! df = 4; %! p =[0.790285178602166; 0.938703291727784; 0.81222737321336]; %! assert (mvtcdf(x, rho, df), p, 1e-14); %!test %! x = [1, 2, 2, 4, 1, 5]; %! rho = eye (6); %! rho(rho == 0) = 0.5; %! df = 4; %! assert (mvtcdf(x, rho, df), 0.6874, 1e-4); %!error mvtcdf (1) %!error mvtcdf (1, 2) %!error ... %! mvtcdf (1, [2, 3; 3, 2], 1) %!error ... %! mvtcdf ([2, 3, 4], ones (2), 1) %!error ... %! mvtcdf ([1, 2, 3], [2, 3], ones (2), 1) %!error ... %! mvtcdf ([2, 3], ones (2), [1, 2, 3]) %!error ... %! mvtcdf ([2, 3], [1, 0.5; 0.5, 1], [1, 2, 3]) statistics-release-1.7.3/inst/dist_fun/mvtcdfqmc.m000066400000000000000000000211251475240274700223010ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} mvtcdfqmc (@var{A}, @var{B}, @var{Rho}, @var{df}) ## @deftypefnx {statistics} {@var{p} =} mvtcdfqmc (@dots{}, @var{TolFun}) ## @deftypefnx {statistics} {@var{p} =} mvtcdfqmc (@dots{}, @var{TolFun}, @var{MaxFunEvals}) ## @deftypefnx {statistics} {@var{p} =} mvtcdfqmc (@dots{}, @var{TolFun}, @var{MaxFunEvals}, @var{Display}) ## @deftypefnx {statistics} {[@var{p}, @var{err}] =} mvtcdfqmc (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{err}, @var{FunEvals}] =} mvtcdfqmc (@dots{}) ## ## Quasi-Monte-Carlo computation of the multivariate Student's T CDF. ## ## The QMC multivariate Student's t distribution is evaluated between the lower ## limit @var{A} and upper limit @var{B} of the hyper-rectangle with a ## correlation matrix @var{Rho} and degrees of freedom @var{df}. ## ## @multitable @columnfractions 0.2 0.8 ## @item "TolFun" @tab --- Maximum absolute error tolerance. Default is 1e-4. ## @item "MaxFunEvals" @tab --- Maximum number of integrand evaluations. ## Default is 1e7 for D > 4. ## @item "Display" @tab --- Display options. Choices are "off" (default), ## "iter", which shows the probability and estimated error at each repetition, ## and "final", which shows the final probability and related error after the ## integrand has converged successfully. ## @end multitable ## ## @code{[@var{p}, @var{err}, @var{FunEvals}] = mvtcdfqmc (@dots{})} returns the ## estimated probability, @var{p}, an estimate of the error, @var{err}, and the ## number of iterations until a successful convergence is met, unless the value ## in @var{MaxFunEvals} was reached. ## ## @seealso{mvtcdf, mvtpdf, mvtrnd} ## @end deftypefn function [p, err, FunEvals] = mvtcdfqmc (A, B, Rho, df, varargin) ## Check for input arguments narginchk (4,7); ## Add defaults TolFun = 1e-4; MaxFunEvals = 1e7; Display = "off"; ## Parse optional arguments (TolFun, MaxFunEvals, Display) if (nargin > 4) TolFun = varargin{1}; if (! isscalar (TolFun) || ! isreal (TolFun)) error ("mvtcdfqmc: TolFun must be a scalar."); endif endif if (nargin > 5) MaxFunEvals = varargin{2}; if (! isscalar (MaxFunEvals) || ! isreal (MaxFunEvals)) error ("mvtcdfqmc: MaxFunEvals must be a scalar."); endif MaxFunEvals = floor (MaxFunEvals); endif if (nargin > 6) Display = varargin{3}; DispOptions = {"off", "final", "iter"}; if (sum (any (strcmpi (Display, DispOptions))) == 0) error ("mvncdf: invalid value for 'Display' argument."); endif endif ## Check if input is single or double class is_type = "double"; if (isa (A, "single") || isa (B, "single") || isa (Rho, "single")) is_type = "single"; endif ## Check for appropriate lower upper limits and NaN values in data if (! all (A < B)) if (any (A > B)) error ("mvtcdfqmc: inconsistent lower upper limits."); elseif (any (isnan (A) | isnan (B))) warning ("mvtcdfqmc: NaNs in data."); p = NaN (is_type); err = NaN (is_type); else warning ("mvtcdfqmc: zero distance between lower upper limits."); p = zeros (is_type); err = zeros (is_type); endif FunEvals = 0; return; endif ## Ignore dimensions with infinite limits InfLim_idx = (A == -Inf) & (B == Inf); if (any (InfLim_idx)) if (all (InfLim_idx)) warning ("mvtcdfqmc: infinite distance between lower upper limits."); p = 1; err = 0; FunEvals = 0; return endif A(InfLim_idx) = []; B(InfLim_idx) = []; Rho(:,InfLim_idx) = []; Rho(InfLim_idx,:) = []; endif ## Get size of covariance matrix m = size (Rho, 1); ## Sort the order of integration according to increasing length of interval [~, ord] = sort (B - A); A = A(ord); B = B(ord); Rho = Rho(ord, ord); ## Check for highly correlated covariance matrix if any(any(abs(tril(Rho,-1)) > .999)) warning("mvtcdfqmc: highly correlated covariance matrix Rho."); endif ## Scale the integration limits and the Cholesky factor of Rho C = chol(Rho); c = diag(C); A = A(:) ./ c; B = B(:) ./ c; C = C ./ repmat(c',m,1); ## Set repetitions fof Monte Carlo MCreps = 25; MCdims = m - isinf(df); ## Set initial output p = zeros (is_type); sigsq = Inf (is_type); FunEvals = 0; err = NaN; ## Initialize vector P = [31, 47, 73, 113, 173, 263, 397, 593, 907, 1361, 2053, 3079, 4621, ... 6947, 10427, 15641, 23473, 35221, 52837, 79259, 118891, 178349, ... 267523, 401287, 601942, 902933, 1354471, 2031713]; for i = 5:length (P); if ((FunEvals + 2*MCreps*P(i)) > MaxFunEvals) break; endif ## Compute the Niederreiter point set generator NRgen = 2 .^ ((1:MCdims) / (MCdims + 1)); ## Compute randomized quasi-Monte Carlo estimate with P points [THat,sigsqTHat] = estimate_mvtqmc (MCreps, P(i), NRgen, C, df, ... A, B, is_type); FunEvals = FunEvals + 2 * MCreps *P (i); ## Recursively update the estimate and the error estimate p = p + (THat - p) ./ (1 + sigsqTHat ./ sigsq); sigsq = sigsqTHat ./ (1 + sigsqTHat ./ sigsq); ## Compute a conservative estimate of error err = 3.5 * sqrt (sigsq); ## Display output for every iteration if (strcmpi (Display, "iter")) printf ("mvtcdfqmc: Probability estimate: %0.4f ",p); printf ("Error estimate: %0.4e Iterations: %d\n", err, FunEvals); endif if (err < TolFun) if (strcmpi (Display, "final")) printf ("mvtcdfqmc: Successfully converged!\n"); printf ("Final probability estimate: %0.4f ",p); printf ("Final error estimate: %0.4e Iterations: %d\n", err, FunEvals); endif return endif endfor warning ("mvtcdfqmc: Error tolerance did NOT converge!"); printf ("Error tolerance: %0.4f Total Iterations: %d\n", TolFun, MaxFunEvals); endfunction ## Randomized Quasi-Monte-Carlo estimate of the integral function [THat, sigsqTHat] = estimate_mvtqmc (MCreps, P, NRgen, C, df, A, ... B, is_type) qq = (1:P)' * NRgen; THat = zeros (MCreps,1,is_type); for rep = 1:MCreps ## Generate A new random lattice of P points. For MVT, this is in the ## m-dimensional unit hypercube, for MVN, in the (m-1)-dimensional unit ## hypercube. w = abs (2 * mod (qq + repmat (rand (size (NRgen), is_type), P, 1), 1) - 1); ## Compute the mean of the integrand over all P of the points, and all P ## of the antithetic points. THat(rep) = (F_qrsvn (A, B, C, df, w) + F_qrsvn (A, B, C, df, 1 - w)) ./ 2; endfor ## Return the MC mean and se^2 sigsqTHat = var(THat) ./ MCreps; THat = mean(THat); endfunction ## Integrand for computation of MVT probabilities function TBar = F_qrsvn (A, B, C, df, w) N = size (w, 1); # number of quasirandom points m = length(A); # number of dimensions if isinf (df) rho = 1; else rho = chi_inv (w(:,m), df) ./ sqrt (df); end rA = norm_cdf (rho .* A(1)); # A is already scaled by diag(C) rB = norm_cdf (rho .* B(1)) - rA; # B is already scaled by diag(C) T = rB; Y = zeros (N, m, "like", T); for i = 2:m z = min (max (rA + rB .* w(:,i-1), eps / 2), 1 - eps / 2); Y(:,i-1) = norm_inv (z); Ysum = Y * C(:,i); rA = norm_cdf (rho .* A(i) - Ysum); # A is already scaled by diag(C) rB = norm_cdf (rho .* B(i) - Ysum) - rA; # B is already scaled by diag(C) T = T .* rB; end TBar = sum (T, 1) ./ length (T); endfunction ## Normal cumulative distribution function function a = norm_cdf (b) a = 0.5 * erfc (- b ./ sqrt (2)); endfunction ## Inverse of normal cumulative distribution function function a = norm_inv (b) a = - sqrt (2) .* erfcinv (2 * b); endfunction ## Inverse of chi cumulative distribution function function a = chi_inv (b,df) a = sqrt (gammaincinv (b, df ./ 2) .* 2); endfunction %!error mvtcdfqmc (1, 2, 3); %!error mvtcdfqmc (1, 2, 3, 4, 5, 6, 7, 8); statistics-release-1.7.3/inst/dist_fun/mvtpdf.m000066400000000000000000000104601475240274700216150ustar00rootroot00000000000000## Copyright (C) 2015 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} mvtpdf (@var{x}, @var{rho}, @var{df}) ## ## Multivariate Student's t probability density function (PDF). ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{x} are the points at which to find the probability, where each row ## corresponds to an observation. (@math{NxD} matrix) ## ## @item ## @var{rho} is the correlation matrix. (@math{DxD} symmetric positive ## definite matrix) ## ## @item ## @var{df} is the degrees of freedom. (scalar or vector of length @math{N}) ## ## @end itemize ## ## The distribution is assumed to be centered (zero mean). ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{y} is the probability density for each row of @var{x}. ## (@math{Nx1} vector) ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## x = [1 2]; ## rho = [1.0 0.5; 0.5 1.0]; ## df = 4; ## y = mvtpdf (x, rho, df) ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Michael Roth, On the Multivariate t Distribution, Technical report from ## Automatic Control at Linkoepings universitet, ## @url{http://users.isy.liu.se/en/rt/roth/student.pdf} ## @end enumerate ## ## @seealso{mvtcdf, mvtcdfqmc, mvtrnd} ## @end deftypefn function y = mvtpdf (x, rho, df) if (nargin != 3) print_usage (); endif # Dimensions d = size (rho, 1); n = size (x, 1); # Check parameters if (size (x, 2) != d) error ("mvtpdf: x must have the same number of columns as rho."); endif if (! isscalar (df) && (! isvector (df) || numel (df) != n)) error (strcat (["mvtpdf: DF must be a scalar or a vector with the"], ... [" same number of rows as X."])); endif if (d < 1 || size (rho, 2) != d || ! issymmetric (rho)) error ("mvtpdf: SIGMA must be nonempty and symmetric."); endif try U = chol (rho); catch error ("mvtpdf: rho must be positive definite"); end_try_catch df = df(:); sqrt_det_sigma = prod(diag(U)); #square root of determinant of rho ## Scale factor for PDF c = (gamma((df+d)/2) ./ gamma(df/2)) ./ (sqrt_det_sigma * (df*pi).^(d/2)); #note: sumsq(U' \ x') is equivalent to the quadratic form x*inv(rho)*x' y = c ./ ((1 + sumsq(U' \ x') ./ df') .^ ((df' + d)/2))'; endfunction %!demo %! ## Compute the pdf of a multivariate t distribution with correlation %! ## parameters rho = [1 .4; .4 1] and 2 degrees of freedom. %! %! rho = [1, 0.4; 0.4, 1]; %! df = 2; %! [X1, X2] = meshgrid (linspace (-2, 2, 25)', linspace (-2, 2, 25)'); %! X = [X1(:), X2(:)]; %! y = mvtpdf (X, rho, df); %! surf (X1, X2, reshape (y, 25, 25)); %! title ("Bivariate Student's t probability density function"); ## Test results verified with R mvtnorm package dmvt function ## dmvt(x = c(0,0), rho = diag(2), log = FALSE) %!assert (mvtpdf ([0 0], eye(2), 1), 0.1591549, 1E-7) ## dmvt(x = c(1,0), rho = matrix(c(1, 0.5, 0.5, 1), nrow=2, ncol=2), df = 2, log = FALSE) %!assert (mvtpdf ([1 0], [1 0.5; 0.5 1], 2), 0.06615947, 1E-7) ## dmvt(x = c(1,0.4,0), rho = matrix(c(1, 0.5, 0.3, 0.5, 1, 0.6, 0.3, 0.6, ... ## 1), nrow=3, ncol=3), df = 5, log = FALSE); dmvt(x = c(1.2,0.5,0.5), ... ## rho = matrix(c(1, 0.5, 0.3, 0.5, 1, 0.6, 0.3, 0.6, 1), nrow=3, ncol=3), ... ## df = 6, log = FALSE); dmvt(x = c(1.4,0.6,1), rho = matrix(c(1, 0.5, 0.3,... ## 0.5, 1, 0.6, 0.3, 0.6, 1), nrow=3, ncol=3), df = 7, log = FALSE) %!assert (mvtpdf ([1 0.4 0; 1.2 0.5 0.5; 1.4 0.6 1], ... %! [1 0.5 0.3; 0.5 1 0.6; 0.3 0.6 1], [5 6 7]), ... %! [0.04713313 0.03722421 0.02069011]', 1E-7) statistics-release-1.7.3/inst/dist_fun/mvtrnd.m000066400000000000000000000104331475240274700216270ustar00rootroot00000000000000## Copyright (C) 2012 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} mvtrnd (@var{rho}, @var{df}) ## @deftypefnx {statistics} {@var{r} =} mvtrnd (@var{rho}, @var{df}, @var{n}) ## ## Random vectors from the multivariate Student's t distribution. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{rho} is the matrix of correlation coefficients. If there are any ## non-unit diagonal elements then @var{rho} will be normalized, so that the ## resulting covariance of the obtained samples @var{r} follows: ## @code{cov (r) = df/(df-2) * rho ./ (sqrt (diag (rho) * diag (rho)))}. ## In order to obtain samples distributed according to a standard multivariate ## student's t-distribution, @var{rho} must be equal to the identity matrix. To ## generate multivariate student's t-distribution samples @var{r} with arbitrary ## covariance matrix @var{rho}, the following scaling might be used: ## @code{r = mvtrnd (rho, df, n) * diag (sqrt (diag (rho)))}. ## ## @item ## @var{df} is the degrees of freedom for the multivariate t-distribution. ## @var{df} must be a vector with the same number of elements as samples to be ## generated or be scalar. ## ## @item ## @var{n} is the number of rows of the matrix to be generated. @var{n} must be ## a non-negative integer and corresponds to the number of samples to be ## generated. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{r} is a matrix of random samples from the multivariate t-distribution ## with @var{n} row samples. ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## rho = [1, 0.5; 0.5, 1]; ## df = 3; ## n = 10; ## r = mvtrnd (rho, df, n); ## @end group ## ## @group ## rho = [1, 0.5; 0.5, 1]; ## df = [2; 3]; ## n = 2; ## r = mvtrnd (rho, df, 2); ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Wendy L. Martinez and Angel R. Martinez. @cite{Computational Statistics ## Handbook with MATLAB}. Appendix E, pages 547-557, Chapman & Hall/CRC, 2001. ## ## @item ## Samuel Kotz and Saralees Nadarajah. @cite{Multivariate t Distributions and ## Their Applications}. Cambridge University Press, Cambridge, 2004. ## @end enumerate ## ## @seealso{mvtcdf, mvtcdfqmc, mvtpdf} ## @end deftypefn function r = mvtrnd (rho, df, n) # Check arguments if (nargin < 2) print_usage (); endif [jnk, p] = cholcov (rho); # This is a more robust check for positive definite if (! ismatrix (rho) || any (any (rho != rho')) || (p != 0)) error ("mvtrnd: SIGMA must be a positive definite matrix."); endif if (!isvector (df) || any (df <= 0)) error ("mvtrnd: DF must be a positive scalar or vector."); endif df = df(:); if (nargin > 2) if (! isscalar (n) || n < 0 | round (n) != n) error ("mvtrnd: N must be a non-negative integer.") endif if (isscalar (df)) df = df * ones (n, 1); else if (length (df) != n) error ("mvtrnd: N must match the length of DF.") endif endif else n = length (df); endif # Normalize rho if (any (diag (rho) != 1)) rho = rho ./ sqrt (diag (rho) * diag (rho)'); endif # Dimension d = size (rho, 1); # Draw samples y = mvnrnd (zeros (1, d), rho, n); u = repmat (chi2rnd (df), 1, d); r = y .* sqrt (repmat (df, 1, d) ./ u); endfunction %!test %! rho = [1, 0.5; 0.5, 1]; %! df = 3; %! n = 10; %! r = mvtrnd (rho, df, n); %! assert (size (r), [10, 2]); %!test %! rho = [1, 0.5; 0.5, 1]; %! df = [2; 3]; %! n = 2; %! r = mvtrnd (rho, df, 2); %! assert (size (r), [2, 2]); statistics-release-1.7.3/inst/dist_fun/nakacdf.m000066400000000000000000000137251475240274700217130ustar00rootroot00000000000000## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} nakacdf (@var{x}, @var{mu}, @var{omega}) ## @deftypefnx {statistics} {@var{p} =} nakacdf (@var{x}, @var{mu}, @var{omega}, @qcode{"upper"}) ## ## Nakagami cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Nakagami distribution with shape parameter @var{mu} and spread ## parameter @var{omega}. The size of @var{p} is the common size of @var{x}, ## @var{mu}, and @var{omega}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be positive reals and @qcode{@var{mu} >= 0.5}. For ## @qcode{@var{mu} < 0.5} or @qcode{@var{omega} <= 0}, @qcode{NaN} is returned. ## ## @code{@var{p} = nakacdf (@var{x}, @var{mu}, @var{omega}, "upper")} computes ## the upper tail probability of the Nakagami distribution with parameters ## @var{mu} and @var{beta}, at the values in @var{x}. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{nakainv, nakapdf, nakarnd, nakafit, nakalike, nakastat} ## @end deftypefn function p = nakacdf (x, mu, omega, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("nakacdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("nakacdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and OMEGA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (omega)) [retval, x, mu, omega] = common_size (x, mu, omega); if (retval > 0) error ("nakacdf: X, MU, and OMEGA must be of common size or scalars."); endif endif ## Check for X, MU, and OMEGA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (omega)) error ("nakacdf: X, MU, and OMEGA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (omega, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force invalid parameters and missing data to NaN k1 = isnan (x) | ! (mu >= 0.5) | ! (omega > 0); p(k1) = NaN; ## Find normal and edge cases k2 = (x == Inf) & (mu >= 0.5) & (mu < Inf) & (omega > 0) & (omega < Inf); k = (x > 0) & (x < Inf) & (mu >= 0.5) & (mu < Inf) ... & (omega > 0) & (omega < Inf); ## Compute Nakagami CDF if (uflag) p(k2) = 0; left = mu .* ones (size (x)); right = (mu ./ omega) .* x .^ 2; p(k) = gammainc (right(k), left(k), "upper"); else p(k2) = 1; left = mu .* ones (size (x)); right = (mu ./ omega) .* x .^ 2; p(k) = gammainc (right(k), left(k)); endif endfunction %!demo %! ## Plot various CDFs from the Nakagami distribution %! x = 0:0.01:3; %! p1 = nakacdf (x, 0.5, 1); %! p2 = nakacdf (x, 1, 1); %! p3 = nakacdf (x, 1, 2); %! p4 = nakacdf (x, 1, 3); %! p5 = nakacdf (x, 2, 1); %! p6 = nakacdf (x, 2, 2); %! p7 = nakacdf (x, 5, 1); %! plot (x, p1, "-r", x, p2, "-g", x, p3, "-y", x, p4, "-m", ... %! x, p5, "-k", x, p6, "-b", x, p7, "-c") %! grid on %! xlim ([0, 3]) %! legend ({"μ = 0.5, ω = 1", "μ = 1, ω = 1", "μ = 1, ω = 2", ... %! "μ = 1, ω = 3", "μ = 2, ω = 1", "μ = 2, ω = 2", ... %! "μ = 5, ω = 1"}, "location", "southeast") %! title ("Nakagami CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1, 0, 1, 2, Inf]; %! y = [0, 0, 0.63212055882855778, 0.98168436111126578, 1]; %!assert (nakacdf (x, ones (1,5), ones (1,5)), y, eps) %!assert (nakacdf (x, 1, 1), y, eps) %!assert (nakacdf (x, [1, 1, NaN, 1, 1], 1), [y(1:2), NaN, y(4:5)]) %!assert (nakacdf (x, 1, [1, 1, NaN, 1, 1]), [y(1:2), NaN, y(4:5)]) %!assert (nakacdf ([x, NaN], 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (nakacdf (single ([x, NaN]), 1, 1), single ([y, NaN]), eps("single")) %!assert (nakacdf ([x, NaN], single (1), 1), single ([y, NaN]), eps("single")) %!assert (nakacdf ([x, NaN], 1, single (1)), single ([y, NaN]), eps("single")) ## Test input validation %!error nakacdf () %!error nakacdf (1) %!error nakacdf (1, 2) %!error nakacdf (1, 2, 3, "tail") %!error nakacdf (1, 2, 3, 4) %!error ... %! nakacdf (ones (3), ones (2), ones (2)) %!error ... %! nakacdf (ones (2), ones (3), ones (2)) %!error ... %! nakacdf (ones (2), ones (2), ones (3)) %!error nakacdf (i, 2, 2) %!error nakacdf (2, i, 2) %!error nakacdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nakainv.m000066400000000000000000000125261475240274700217510ustar00rootroot00000000000000## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} nakacdf (@var{x}, @var{mu}, @var{omega}) ## ## Inverse of the Nakagami cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Nakagami distribution with shape parameter @var{mu} and spread parameter ## @var{omega}. The size of @var{x} is the common size of @var{x}, @var{mu}, ## and @var{omega}. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## Both parameters must be positive reals and @qcode{@var{mu} >= 0.5}. For ## @qcode{@var{mu} < 0.5} or @qcode{@var{omega} <= 0}, @qcode{NaN} is returned. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{nakacdf, nakapdf, nakarnd, nakafit, nakalike, nakastat} ## @end deftypefn function x = nakainv (p, mu, omega) ## Check for valid number of input arguments if (nargin < 3) error ("nakainv: function called with too few input arguments."); endif ## Check for common size of P, MU, and OMEGA if (! isscalar (p) || ! isscalar (mu) || ! isscalar (omega)) [retval, p, mu, omega] = common_size (p, mu, omega); if (retval > 0) error ("nakainv: P, MU, and OMEGA must be of common size or scalars."); endif endif ## Check for P, MU, and OMEGA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (omega)) error ("nakainv: P, MU, and OMEGA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (omega, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif ## Force invalid parameters and missing data to NaN k = isnan (p) | ! (p >= 0) | ! (p <= 1) | ! (mu >= 0.5) | ! (omega > 0); x(k) = NaN; ## Handle edge cases k = (p == 1) & (mu >= 0.5) & (mu < Inf) & (omega > 0) & (omega < Inf); x(k) = Inf; ## Find normal cases k = (0 < p) & (p < 1) & (0.5 <= mu) & (mu < Inf) ... & (0 < omega) & (omega < Inf); ## Compute Nakagami iCDF if (isscalar (mu) && isscalar(omega)) m_gamma = mu; w_gamma = omega / mu; x(k) = gaminv (p(k), m_gamma, w_gamma); x(k) = sqrt (x(k)); else m_gamma = mu; w_gamma = omega ./ mu; x(k) = gaminv (p(k), m_gamma(k), w_gamma(k)); x(k) = sqrt (x(k)); endif endfunction %!demo %! ## Plot various iCDFs from the Nakagami distribution %! p = 0.001:0.001:0.999; %! x1 = nakainv (p, 0.5, 1); %! x2 = nakainv (p, 1, 1); %! x3 = nakainv (p, 1, 2); %! x4 = nakainv (p, 1, 3); %! x5 = nakainv (p, 2, 1); %! x6 = nakainv (p, 2, 2); %! x7 = nakainv (p, 5, 1); %! plot (p, x1, "-r", p, x2, "-g", p, x3, "-y", p, x4, "-m", ... %! p, x5, "-k", p, x6, "-b", p, x7, "-c") %! grid on %! ylim ([0, 3]) %! legend ({"μ = 0.5, ω = 1", "μ = 1, ω = 1", "μ = 1, ω = 2", ... %! "μ = 1, ω = 3", "μ = 2, ω = 1", "μ = 2, ω = 2", ... %! "μ = 5, ω = 1"}, "location", "northwest") %! title ("Nakagami iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, y %! p = [-Inf, -1, 0, 1/2, 1, 2, Inf]; %! y = [NaN, NaN, 0, 0.83255461115769769, Inf, NaN, NaN]; %!assert (nakainv (p, ones (1,7), ones (1,7)), y, eps) %!assert (nakainv (p, 1, 1), y, eps) %!assert (nakainv (p, [1, 1, 1, NaN, 1, 1, 1], 1), [y(1:3), NaN, y(5:7)], eps) %!assert (nakainv (p, 1, [1, 1, 1, NaN, 1, 1, 1]), [y(1:3), NaN, y(5:7)], eps) %!assert (nakainv ([p, NaN], 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (nakainv (single ([p, NaN]), 1, 1), single ([y, NaN])) %!assert (nakainv ([p, NaN], single (1), 1), single ([y, NaN])) %!assert (nakainv ([p, NaN], 1, single (1)), single ([y, NaN])) ## Test input validation %!error nakainv () %!error nakainv (1) %!error nakainv (1, 2) %!error ... %! nakainv (ones (3), ones (2), ones(2)) %!error ... %! nakainv (ones (2), ones (3), ones(2)) %!error ... %! nakainv (ones (2), ones (2), ones(3)) %!error nakainv (i, 4, 3) %!error nakainv (1, i, 3) %!error nakainv (1, 4, i) statistics-release-1.7.3/inst/dist_fun/nakapdf.m000066400000000000000000000123561475240274700217270ustar00rootroot00000000000000## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} nakapdf (@var{x}, @var{mu}, @var{omega}) ## ## Nakagami probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Nakagami distribution with shape parameter @var{mu} and spread ## parameter @var{omega}. The size of @var{y} is the common size of @var{x}, ## @var{mu}, and @var{omega}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be positive reals and @qcode{@var{mu} >= 0.5}. For ## @qcode{@var{mu} < 0.5} or @qcode{@var{omega} <= 0}, @qcode{NaN} is returned. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{nakacdf, nakapdf, nakarnd, nakafit, nakalike, nakastat} ## @end deftypefn function y = nakapdf (x, mu, omega) ## Check for valid number of input arguments if (nargin < 3) error ("nakapdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and OMEGA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (omega)) [retval, x, mu, omega] = common_size (x, mu, omega); if (retval > 0) error ("nakapdf: X, MU, and OMEGA must be of common size or scalars."); endif endif ## Check for X, MU, and OMEGA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (omega)) error ("nakapdf: X, MU, and OMEGA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (omega, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Compute Nakagami PDF k = isnan (x) | ! (mu >= 0.5) | ! (omega > 0); y(k) = NaN; k = (0 < x) & (x < Inf) & (0.5 <= mu) & (mu < Inf) ... & (0 < omega) & (omega < Inf); if (isscalar (mu) && isscalar(omega)) y(k) = exp (log (2) + mu * log (mu) - log (gamma (mu)) - ... mu * log (omega) + (2 * mu-1) * ... log (x(k)) - (mu / omega) * x(k) .^ 2); else y(k) = exp (log(2) + mu(k) .* log (mu(k)) - log (gamma (mu(k))) - ... mu(k) .* log (omega(k)) + (2 * mu(k) - 1) ... .* log (x(k)) - (mu(k) ./ omega(k)) .* x(k) .^ 2); endif endfunction %!demo %! ## Plot various PDFs from the Nakagami distribution %! x = 0:0.01:3; %! y1 = nakapdf (x, 0.5, 1); %! y2 = nakapdf (x, 1, 1); %! y3 = nakapdf (x, 1, 2); %! y4 = nakapdf (x, 1, 3); %! y5 = nakapdf (x, 2, 1); %! y6 = nakapdf (x, 2, 2); %! y7 = nakapdf (x, 5, 1); %! plot (x, y1, "-r", x, y2, "-g", x, y3, "-y", x, y4, "-m", ... %! x, y5, "-k", x, y6, "-b", x, y7, "-c") %! grid on %! xlim ([0, 3]) %! ylim ([0, 2]) %! legend ({"μ = 0.5, ω = 1", "μ = 1, ω = 1", "μ = 1, ω = 2", ... %! "μ = 1, ω = 3", "μ = 2, ω = 1", "μ = 2, ω = 2", ... %! "μ = 5, ω = 1"}, "location", "northeast") %! title ("Nakagami PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1, 0, 1, 2, Inf]; %! y = [0, 0, 0.73575888234288467, 0.073262555554936715, 0]; %!assert (nakapdf (x, ones (1,5), ones (1,5)), y, eps) %!assert (nakapdf (x, 1, 1), y, eps) %!assert (nakapdf (x, [1, 1, NaN, 1, 1], 1), [y(1:2), NaN, y(4:5)], eps) %!assert (nakapdf (x, 1, [1, 1, NaN, 1, 1]), [y(1:2), NaN, y(4:5)], eps) %!assert (nakapdf ([x, NaN], 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (nakapdf (single ([x, NaN]), 1, 1), single ([y, NaN])) %!assert (nakapdf ([x, NaN], single (1), 1), single ([y, NaN])) %!assert (nakapdf ([x, NaN], 1, single (1)), single ([y, NaN])) ## Test input validation %!error nakapdf () %!error nakapdf (1) %!error nakapdf (1, 2) %!error ... %! nakapdf (ones (3), ones (2), ones(2)) %!error ... %! nakapdf (ones (2), ones (3), ones(2)) %!error ... %! nakapdf (ones (2), ones (2), ones(3)) %!error nakapdf (i, 4, 3) %!error nakapdf (1, i, 3) %!error nakapdf (1, 4, i) statistics-release-1.7.3/inst/dist_fun/nakarnd.m000066400000000000000000000157031475240274700217400ustar00rootroot00000000000000## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 1995-2015 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} nakarnd (@var{mu}, @var{omega}) ## @deftypefnx {statistics} {@var{r} =} nakarnd (@var{mu}, @var{omega}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} nakarnd (@var{mu}, @var{omega}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} nakarnd (@var{mu}, @var{omega}, [@var{sz}]) ## ## Random arrays from the Nakagami distribution. ## ## @code{@var{r} = nakarnd (@var{mu}, @var{omega})} returns an array of random ## numbers chosen from the Nakagami distribution with shape parameter @var{mu} ## and spread parameter @var{omega}. The size of @var{r} is the common size of ## @var{mu} and @var{omega}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Both parameters must be positive reals and @qcode{@var{mu} >= 0.5}. For ## @qcode{@var{mu} < 0.5} or @qcode{@var{omega} <= 0}, @qcode{NaN} is returned. ## ## When called with a single size argument, @code{nakarnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{nakacdf, nakainv, nakapdf, nakafit, nakalike, nakastat} ## @end deftypefn function r = nakarnd (mu, omega, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("nakarnd: function called with too few input arguments."); endif ## Check for common size of MU and OMEGA if (! isscalar (mu) || ! isscalar (omega)) [retval, mu, omega] = common_size (mu, omega); if (retval > 0) error ("nakarnd: MU and OMEGA must be of common size or scalars."); endif endif ## Check for MU and OMEGA being reals if (iscomplex (mu) || iscomplex (omega)) error ("nakarnd: MU and OMEGA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["nakarnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("nakarnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("nakarnd: MU and OMEGA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (omega, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Nakagami distribution if (isscalar (mu) && isscalar (omega)) if ((0.5 <= mu) && (mu < Inf) && (0 < omega) && (omega < Inf)) m_gamma = mu; w_gamma = omega / mu; r = gamrnd (m_gamma, w_gamma, sz); r = sqrt (r); else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (0.5 <= mu) & (mu < Inf) & (0 < omega) & (omega < Inf); m_gamma = mu; w_gamma = omega ./ mu; r(k) = gamrnd (m_gamma(k), w_gamma(k)); r(k) = sqrt (r(k)); endif endfunction ## Test output %!assert (size (nakarnd (1, 1)), [1 1]) %!assert (size (nakarnd (1, ones (2,1))), [2, 1]) %!assert (size (nakarnd (1, ones (2,2))), [2, 2]) %!assert (size (nakarnd (ones (2,1), 1)), [2, 1]) %!assert (size (nakarnd (ones (2,2), 1)), [2, 2]) %!assert (size (nakarnd (1, 1, 3)), [3, 3]) %!assert (size (nakarnd (1, 1, [4, 1])), [4, 1]) %!assert (size (nakarnd (1, 1, 4, 1)), [4, 1]) %!assert (size (nakarnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (nakarnd (1, 1, 0, 1)), [0, 1]) %!assert (size (nakarnd (1, 1, 1, 0)), [1, 0]) %!assert (size (nakarnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (nakarnd (1, 1)), "double") %!assert (class (nakarnd (1, single (1))), "single") %!assert (class (nakarnd (1, single ([1, 1]))), "single") %!assert (class (nakarnd (single (1), 1)), "single") %!assert (class (nakarnd (single ([1, 1]), 1)), "single") ## Test input validation %!error nakarnd () %!error nakarnd (1) %!error ... %! nakarnd (ones (3), ones (2)) %!error ... %! nakarnd (ones (2), ones (3)) %!error nakarnd (i, 2, 3) %!error nakarnd (1, i, 3) %!error ... %! nakarnd (1, 2, -1) %!error ... %! nakarnd (1, 2, 1.2) %!error ... %! nakarnd (1, 2, ones (2)) %!error ... %! nakarnd (1, 2, [2 -1 2]) %!error ... %! nakarnd (1, 2, [2 0 2.5]) %!error ... %! nakarnd (1, 2, 2, -1, 5) %!error ... %! nakarnd (1, 2, 2, 1.5, 5) %!error ... %! nakarnd (2, ones (2), 3) %!error ... %! nakarnd (2, ones (2), [3, 2]) %!error ... %! nakarnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/nbincdf.m000066400000000000000000000201501475240274700217150ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} nbincdf (@var{x}, @var{r}, @var{ps}) ## @deftypefnx {statistics} {@var{p} =} nbincdf (@var{x}, @var{r}, @var{ps}, @qcode{"upper"}) ## ## Negative binomial cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the negative binomial distribution with parameters @var{r} and ## @var{ps}, where @var{r} is the number of successes until the experiment is ## stopped and @var{ps} is the probability of success in each experiment, given ## the number of failures in @var{x}. The size of @var{p} is the common size of ## @var{x}, @var{r}, and @var{ps}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## The algorithm uses the cumulative sums of the binomial masses. ## ## @code{@var{p} = nbincdf (@var{x}, @var{r}, @var{ps}, "upper")} computes the ## upper tail probability of the negative binomial distribution with parameters ## @var{r} and @var{ps}, at the values in @var{x}. ## ## When @var{r} is an integer, the negative binomial distribution is also known ## as the Pascal distribution and it models the number of failures in @var{x} ## before a specified number of successes is reached in a series of independent, ## identical trials. Its parameters are the probability of success in a single ## trial, @var{ps}, and the number of successes, @var{r}. A special case of the ## negative binomial distribution, when @qcode{@var{r} = 1}, is the geometric ## distribution, which models the number of failures before the first success. ## ## @var{r} can also have non-integer positive values, in which form the negative ## binomial distribution, also known as the Polya distribution, has no ## interpretation in terms of repeated trials, but, like the Poisson ## distribution, it is useful in modeling count data. The negative binomial ## distribution is more general than the Poisson distribution because it has a ## variance that is greater than its mean, making it suitable for count data ## that do not meet the assumptions of the Poisson distribution. In the limit, ## as @var{r} increases to infinity, the negative binomial distribution ## approaches the Poisson distribution. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbininv, nbinpdf, nbinrnd, nbinfit, nbinlike, nbinstat} ## @end deftypefn function p = nbincdf (x, r, ps, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("nbincdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin == 4 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin == 4 && ! strcmpi (uflag, "upper")) error ("nbincdf: invalid argument for upper tail."); else uflag = false; endif ## Check for R and PS being scalars scalarNPS = (isscalar(r) & isscalar(ps)); ## Check for common size of X, R, and PS if (! isscalar (x) || ! isscalar (r) || ! isscalar (ps)) [retval, x, r, ps] = common_size (x, r, ps); if (retval > 0) error ("nbincdf: X, R, and PS must be of common size or scalars."); endif endif ## Check for X, R, and PS being reals if (iscomplex (x) || iscomplex (r) || iscomplex (ps)) error ("nbincdf: X, R, and PS must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (r, "single") || isa (ps, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force NaN for out of range or missing parameters and missing data NaN is_nan = (isnan (x) | isnan (r) | (r <= 0) | (r == Inf) | (ps < 0) | (ps > 1)); p(is_nan) = NaN; ## Compute P for X >= 0 xf = floor (x); k = find (xf >= 0 & ! is_nan); ## Return 1 for positive infinite values of X, unless "upper" is given: p = 0 k1 = find (isinf (xf(k))); if (any (k1)) if (uflag) p(k(k1)) = 0; else p(k(k1)) = 1; endif k(k1) = []; endif ## Return 1 when X < 0 and "upper" is given k1 = (x < 0 & ! is_nan); if (any (k1)) if (uflag) p(k1) = 1; endif endif ## Accumulate probabilities up to the maximum value in X if (any (k)) if (uflag) p(k) = betainc (ps(k), r(k), xf(k) + 1, "upper"); else max_val = max (xf(k)); if (scalarNPS) tmp = cumsum (nbinpdf (0:max_val, r(1), ps(1))); p(k) = tmp(xf(k) + 1); else idx = (0:max_val)'; compare = idx(:, ones (size (k))); index = xf(k); index = index(:); index = index(:, ones (size (idx)))'; n_big = r(k); n_big = n_big(:); n_big = n_big(:, ones (size (idx)))'; ps_big = ps(k); ps_big = ps_big(:); ps_big = ps_big(:, ones (size (idx)))'; p0 = nbinpdf (compare, n_big, ps_big); indicator = find (compare > index); p0(indicator) = zeros (size (indicator)); p(k) = sum(p0,1); endif endif endif ## Prevent round-off errors p(p > 1) = 1; endfunction %!demo %! ## Plot various CDFs from the negative binomial distribution %! x = 0:50; %! p1 = nbincdf (x, 2, 0.15); %! p2 = nbincdf (x, 5, 0.2); %! p3 = nbincdf (x, 4, 0.4); %! p4 = nbincdf (x, 10, 0.3); %! plot (x, p1, "*r", x, p2, "*g", x, p3, "*k", x, p4, "*m") %! grid on %! xlim ([0, 40]) %! legend ({"r = 2, ps = 0.15", "r = 5, ps = 0.2", "r = 4, p = 0.4", ... %! "r = 10, ps = 0.3"}, "location", "southeast") %! title ("Negative binomial CDF") %! xlabel ("values in x (number of failures)") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1 0 1 2 Inf]; %! y = [0 1/2 3/4 7/8 1]; %!assert (nbincdf (x, ones (1,5), 0.5*ones (1,5)), y) %!assert (nbincdf (x, 1, 0.5*ones (1,5)), y) %!assert (nbincdf (x, ones (1,5), 0.5), y) %!assert (nbincdf (x, ones (1,5), 0.5, "upper"), 1 - y, eps) %!assert (nbincdf ([x(1:3) 0 x(5)], [0 1 NaN 1.5 Inf], 0.5), ... %! [NaN 1/2 NaN nbinpdf(0,1.5,0.5) NaN], eps) %!assert (nbincdf (x, 1, 0.5*[-1 NaN 4 1 1]), [NaN NaN NaN y(4:5)]) %!assert (nbincdf ([x(1:2) NaN x(4:5)], 1, 0.5), [y(1:2) NaN y(4:5)]) ## Test class of input preserved %!assert (nbincdf ([x, NaN], 1, 0.5), [y, NaN]) %!assert (nbincdf (single ([x, NaN]), 1, 0.5), single ([y, NaN])) %!assert (nbincdf ([x, NaN], single (1), 0.5), single ([y, NaN])) %!assert (nbincdf ([x, NaN], 1, single (0.5)), single ([y, NaN])) ## Test input validation %!error nbincdf () %!error nbincdf (1) %!error nbincdf (1, 2) %!error nbincdf (1, 2, 3, 4) %!error nbincdf (1, 2, 3, "some") %!error ... %! nbincdf (ones (3), ones (2), ones (2)) %!error ... %! nbincdf (ones (2), ones (3), ones (2)) %!error ... %! nbincdf (ones (2), ones (2), ones (3)) %!error nbincdf (i, 2, 2) %!error nbincdf (2, i, 2) %!error nbincdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nbininv.m000066400000000000000000000207001475240274700217560ustar00rootroot00000000000000## Copyright (C) 1995-2012 Kurt Hornik ## Copyright (C) 2012-2016 Rik Wehbring ## Copyright (C) 2016-2017 Lachlan Andrew ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} nbininv (@var{p}, @var{r}, @var{ps}) ## ## Inverse of the negative binomial cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the negative binomial distribution with parameters @var{r} and @var{ps}, ## where @var{r} is the number of successes until the experiment is stopped and ## @var{ps} is the probability of success in each experiment, given the ## probability in @var{p}. The size of @var{x} is the common size of @var{p}, ## @var{r}, and @var{ps}. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## When @var{r} is an integer, the negative binomial distribution is also known ## as the Pascal distribution and it models the number of failures in @var{x} ## before a specified number of successes is reached in a series of independent, ## identical trials. Its parameters are the probability of success in a single ## trial, @var{ps}, and the number of successes, @var{r}. A special case of the ## negative binomial distribution, when @qcode{@var{r} = 1}, is the geometric ## distribution, which models the number of failures before the first success. ## ## @var{r} can also have non-integer positive values, in which form the negative ## binomial distribution, also known as the Polya distribution, has no ## interpretation in terms of repeated trials, but, like the Poisson ## distribution, it is useful in modeling count data. The negative binomial ## distribution is more general than the Poisson distribution because it has a ## variance that is greater than its mean, making it suitable for count data ## that do not meet the assumptions of the Poisson distribution. In the limit, ## as @var{r} increases to infinity, the negative binomial distribution ## approaches the Poisson distribution. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbininv, nbinpdf, nbinrnd, nbinfit, nbinlike, nbinstat} ## @end deftypefn function x = nbininv (p, r, ps) ## Check for valid number of input arguments if (nargin < 3) error ("nbininv: function called with too few input arguments."); endif ## Check for common size of P, R, and PS if (! isscalar (p) || ! isscalar (r) || ! isscalar (ps)) [retval, p, r, ps] = common_size (p, r, ps); if (retval > 0) error ("nbininv: P, R, and PS must be of common size or scalars."); endif endif ## Check for P, R, and PS being reals if (iscomplex (p) || iscomplex (r) || iscomplex (ps)) error ("nbininv: P, R, and PS must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (r, "single") || isa (ps, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif k = (isnan (p) | (p < 0) | (p > 1) | isnan (r) | (r < 1) | (r == Inf) | isnan (ps) | (ps < 0) | (ps > 1)); x(k) = NaN; k = (p == 1) & (r > 0) & (r < Inf) & (ps >= 0) & (ps <= 1); x(k) = Inf; k = find ((p >= 0) & (p < 1) & (r > 0) & (r < Inf) & (ps > 0) & (ps <= 1)); if (! isempty (k)) p = p(k); m = zeros (size (k)); if (isscalar (r) && isscalar (ps)) [m, unfinished] = scalar_nbininv (p(:), r, ps); m(unfinished) = bin_search_nbininv (p(unfinished), r, ps); else m = bin_search_nbininv (p, r(k), ps(k)); endif x(k) = m; endif endfunction ## Core algorithm to calculate the inverse negative binomial, for r and ps real ## scalars and y a column vector, and for which the output is not NaN or Inf. ## Compute CDF in batches of doubling size until CDF > p, or answer > 500. ## Return the locations of unfinished cases in k. function [m, k] = scalar_nbininv (p, r, ps) k = 1:length (p); m = zeros (size (p)); prev_limit = 0; limit = 10; do cdf = nbincdf (prev_limit:limit, r, ps); rr = bsxfun (@le, p(k), cdf); [v, m(k)] = max (rr, [], 2); # find first instance of p <= cdf m(k) += prev_limit - 1; k = k(v == 0); prev_limit = limit; limit += limit; until (isempty (k) || limit >= 1000) endfunction ## Vectorized binary search. ## Can handle vectors r and ps, and is faster than the scalar case when the ## answer is large. ## Could be optimized to call nbincdf only for a subset of the p at each stage, ## but care must be taken to handle both scalar and vector r,ps. Bookkeeping ## may cost more than the extra computations. function m = bin_search_nbininv (p, r, ps) k = 1:length (p); lower = zeros (size (p)); limit = 1; while (any (k) && limit < 1e100) cdf = nbincdf (limit, r, ps); k = (p > cdf); lower(k) = limit; limit += limit; endwhile upper = max (2*lower, 1); k = find (lower != limit/2); # elements for which above loop finished for i = 1:ceil (log2 (max (lower))) mid = (upper + lower)/2; cdf = nbincdf (floor (mid), r, ps); rr = (p <= cdf); upper(rr) = mid(rr); lower(! rr) = mid(! rr); endfor m = ceil (lower); m(p > nbincdf (m, r, ps)) += 1; # fix off-by-one errors from binary search endfunction %!demo %! ## Plot various iCDFs from the negative binomial distribution %! p = 0.001:0.001:0.999; %! x1 = nbininv (p, 2, 0.15); %! x2 = nbininv (p, 5, 0.2); %! x3 = nbininv (p, 4, 0.4); %! x4 = nbininv (p, 10, 0.3); %! plot (p, x1, "-r", p, x2, "-g", p, x3, "-k", p, x4, "-m") %! grid on %! ylim ([0, 40]) %! legend ({"r = 2, ps = 0.15", "r = 5, ps = 0.2", "r = 4, p = 0.4", ... %! "r = 10, ps = 0.3"}, "location", "northwest") %! title ("Negative binomial iCDF") %! xlabel ("probability") %! ylabel ("values in x (number of failures)") ## Test output %!shared p %! p = [-1 0 3/4 1 2]; %!assert (nbininv (p, ones (1,5), 0.5*ones (1,5)), [NaN 0 1 Inf NaN]) %!assert (nbininv (p, 1, 0.5*ones (1,5)), [NaN 0 1 Inf NaN]) %!assert (nbininv (p, ones (1,5), 0.5), [NaN 0 1 Inf NaN]) %!assert (nbininv (p, [1 0 NaN Inf 1], 0.5), [NaN NaN NaN NaN NaN]) %!assert (nbininv (p, [1 0 1.5 Inf 1], 0.5), [NaN NaN 2 NaN NaN]) %!assert (nbininv (p, 1, 0.5*[1 -Inf NaN Inf 1]), [NaN NaN NaN NaN NaN]) %!assert (nbininv ([p(1:2) NaN p(4:5)], 1, 0.5), [NaN 0 NaN Inf NaN]) ## Test class of input preserved %!assert (nbininv ([p, NaN], 1, 0.5), [NaN 0 1 Inf NaN NaN]) %!assert (nbininv (single ([p, NaN]), 1, 0.5), single ([NaN 0 1 Inf NaN NaN])) %!assert (nbininv ([p, NaN], single (1), 0.5), single ([NaN 0 1 Inf NaN NaN])) %!assert (nbininv ([p, NaN], 1, single (0.5)), single ([NaN 0 1 Inf NaN NaN])) ## Test accuracy, to within +/- 1 since it is a discrete distribution %!shared y, tol %! y = magic (3) + 1; %! tol = 1; %!assert (nbininv (nbincdf (1:10, 3, 0.1), 3, 0.1), 1:10, tol) %!assert (nbininv (nbincdf (1:10, 3./(1:10), 0.1), 3./(1:10), 0.1), 1:10, tol) %!assert (nbininv (nbincdf (y, 3./y, 1./y), 3./y, 1./y), y, tol) ## Test input validation %!error nbininv () %!error nbininv (1) %!error nbininv (1, 2) %!error ... %! nbininv (ones (3), ones (2), ones (2)) %!error ... %! nbininv (ones (2), ones (3), ones (2)) %!error ... %! nbininv (ones (2), ones (2), ones (3)) %!error nbininv (i, 2, 2) %!error nbininv (2, i, 2) %!error nbininv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nbinpdf.m000066400000000000000000000137401475240274700217410ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} nbinpdf (@var{x}, @var{r}, @var{ps}) ## ## Negative binomial probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## at @var{x} of the negative binomial distribution with parameters @var{r} and ## @var{ps}, where @var{r} is the number of successes until the experiment is ## stopped and @var{ps} is the probability of success in each experiment, given ## the number of failures in @var{x}. The size of @var{y} is the common size of ## @var{x}, @var{r}, and @var{ps}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## When @var{r} is an integer, the negative binomial distribution is also known ## as the Pascal distribution and it models the number of failures in @var{x} ## before a specified number of successes is reached in a series of independent, ## identical trials. Its parameters are the probability of success in a single ## trial, @var{ps}, and the number of successes, @var{r}. A special case of the ## negative binomial distribution, when @qcode{@var{r} = 1}, is the geometric ## distribution, which models the number of failures before the first success. ## ## @var{r} can also have non-integer positive values, in which form the negative ## binomial distribution, also known as the Polya distribution, has no ## interpretation in terms of repeated trials, but, like the Poisson ## distribution, it is useful in modeling count data. The negative binomial ## distribution is more general than the Poisson distribution because it has a ## variance that is greater than its mean, making it suitable for count data ## that do not meet the assumptions of the Poisson distribution. In the limit, ## as @var{r} increases to infinity, the negative binomial distribution ## approaches the Poisson distribution. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbininv, nbininv, nbinrnd, nbinfit, nbinlike, nbinstat} ## @end deftypefn function y = nbinpdf (x, r, ps) ## Check for valid number of input arguments if (nargin < 3) error ("nbinpdf: function called with too few input arguments."); endif ## Check for common size of X, R, and PS if (! isscalar (x) || ! isscalar (r) || ! isscalar (ps)) [retval, x, r, ps] = common_size (x, r, ps); if (retval > 0) error ("nbinpdf: X, R, and PS must be of common size or scalars."); endif endif ## Check for X, R, and PS being reals if (iscomplex (x) || iscomplex (r) || iscomplex (ps)) error ("nbinpdf: X, R, and PS must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (r, "single") || isa (ps, "single")) y = NaN (size (x), "single"); else y = NaN (size (x)); endif ok = (x < Inf) & (x == fix (x)) & (r > 0) & (r < Inf) & (ps >= 0) & (ps <= 1); k = (x < 0) & ok; y(k) = 0; k = (x >= 0) & ok; if (isscalar (r) && isscalar (ps)) y(k) = bincoeff (-r, x(k)) .* (ps ^ r) .* ((ps - 1) .^ x(k)); else y(k) = bincoeff (-r(k), x(k)) .* (ps(k) .^ r(k)) .* ((ps(k) - 1) .^ x(k)); endif endfunction %!demo %! ## Plot various PDFs from the negative binomial distribution %! x = 0:40; %! y1 = nbinpdf (x, 2, 0.15); %! y2 = nbinpdf (x, 5, 0.2); %! y3 = nbinpdf (x, 4, 0.4); %! y4 = nbinpdf (x, 10, 0.3); %! plot (x, y1, "*r", x, y2, "*g", x, y3, "*k", x, y4, "*m") %! grid on %! xlim ([0, 40]) %! ylim ([0, 0.12]) %! legend ({"r = 2, ps = 0.15", "r = 5, ps = 0.2", "r = 4, p = 0.4", ... %! "r = 10, ps = 0.3"}, "location", "northeast") %! title ("Negative binomial PDF") %! xlabel ("values in x (number of failures)") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 1 2 Inf]; %! y = [0 1/2 1/4 1/8 NaN]; %!assert (nbinpdf (x, ones (1,5), 0.5*ones (1,5)), y) %!assert (nbinpdf (x, 1, 0.5*ones (1,5)), y) %!assert (nbinpdf (x, ones (1,5), 0.5), y) %!assert (nbinpdf (x, [0 1 NaN 1.5 Inf], 0.5), [NaN 1/2 NaN 1.875*0.5^1.5/4 NaN], eps) %!assert (nbinpdf (x, 1, 0.5*[-1 NaN 4 1 1]), [NaN NaN NaN y(4:5)]) %!assert (nbinpdf ([x, NaN], 1, 0.5), [y, NaN]) ## Test class of input preserved %!assert (nbinpdf (single ([x, NaN]), 1, 0.5), single ([y, NaN])) %!assert (nbinpdf ([x, NaN], single (1), 0.5), single ([y, NaN])) %!assert (nbinpdf ([x, NaN], 1, single (0.5)), single ([y, NaN])) ## Test input validation %!error nbinpdf () %!error nbinpdf (1) %!error nbinpdf (1, 2) %!error ... %! nbinpdf (ones (3), ones (2), ones (2)) %!error ... %! nbinpdf (ones (2), ones (3), ones (2)) %!error ... %! nbinpdf (ones (2), ones (2), ones (3)) %!error nbinpdf (i, 2, 2) %!error nbinpdf (2, i, 2) %!error nbinpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nbinrnd.m000066400000000000000000000201671475240274700217540ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{rnd} =} nbinrnd (@var{r}, @var{ps}) ## @deftypefnx {statistics} {@var{rnd} =} nbinrnd (@var{r}, @var{ps}, @var{rows}) ## @deftypefnx {statistics} {@var{rnd} =} nbinrnd (@var{r}, @var{ps}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{rnd} =} nbinrnd (@var{r}, @var{ps}, [@var{sz}]) ## ## Random arrays from the negative binomial distribution. ## ## @code{@var{rnd} = nbinrnd (@var{r}, @var{ps})} returns an array of random ## numbers chosen from the negative binomial distribution with parameters ## @var{r} and @var{ps}, where @var{r} is the number of successes until the ## experiment is stopped and @var{ps} is the probability of success in each ## experiment, given the number of failures in @var{x}. The size of @var{rnd} ## is the common size of @var{r} and @var{ps}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## When called with a single size argument, return a square matrix with ## the dimension specified. When called with more than one scalar argument the ## first two arguments are taken as the number of rows and columns and any ## further arguments specify additional matrix dimensions. The size may also ## be specified with a vector of dimensions @var{sz}. ## ## When @var{r} is an integer, the negative binomial distribution is also known ## as the Pascal distribution and it models the number of failures in @var{x} ## before a specified number of successes is reached in a series of independent, ## identical trials. Its parameters are the probability of success in a single ## trial, @var{ps}, and the number of successes, @var{r}. A special case of the ## negative binomial distribution, when @qcode{@var{r} = 1}, is the geometric ## distribution, which models the number of failures before the first success. ## ## @var{r} can also have non-integer positive values, in which form the negative ## binomial distribution, also known as the Polya distribution, has no ## interpretation in terms of repeated trials, but, like the Poisson ## distribution, it is useful in modeling count data. The negative binomial ## distribution is more general than the Poisson distribution because it has a ## variance that is greater than its mean, making it suitable for count data ## that do not meet the assumptions of the Poisson distribution. In the limit, ## as @var{r} increases to infinity, the negative binomial distribution ## approaches the Poisson distribution. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbininv, nbininv, nbinpdf, nbinfit, nbinlike, nbinstat} ## @end deftypefn function rnd = nbinrnd (r, ps, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("nbinrnd: function called with too few input arguments."); endif ## Check for common size R and PS if (! isscalar (r) || ! isscalar (ps)) [retval, r, ps] = common_size (r, ps); if (retval > 0) error ("nbinrnd: R and PS must be of common size or scalars."); endif endif ## Check for R and PS being reals if (iscomplex (r) || iscomplex (ps)) error ("nbinrnd: R and PS must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (r); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["nbinrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("nbinrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (r) && ! isequal (size (r), sz)) error ("nbinrnd: R and PS must be scalars or of size SZ."); endif ## Check for class type if (isa (r, "single") || isa (ps, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from negative binomial distribution if (isscalar (r) && isscalar (ps)) if ((r > 0) && (r < Inf) && (ps > 0) && (ps <= 1)) rnd = randp ((1 - ps) ./ ps .* randg (r, sz, cls), cls); elseif ((r > 0) && (r < Inf) && (ps == 0)) rnd = zeros (sz, cls); else rnd = NaN (sz, cls); endif else rnd = NaN (sz, cls); k = (r > 0) & (r < Inf) & (ps == 0); rnd(k) = 0; k = (r > 0) & (r < Inf) & (ps > 0) & (ps <= 1); rnd(k) = randp ((1 - ps(k)) ./ ps(k) .* randg (r(k), cls)); endif endfunction ## Test output %!assert (size (nbinrnd (1, 0.5)), [1 1]) %!assert (size (nbinrnd (1, 0.5 * ones (2,1))), [2, 1]) %!assert (size (nbinrnd (1, 0.5 * ones (2,2))), [2, 2]) %!assert (size (nbinrnd (ones (2,1), 0.5)), [2, 1]) %!assert (size (nbinrnd (ones (2,2), 0.5)), [2, 2]) %!assert (size (nbinrnd (1, 0.5, 3)), [3, 3]) %!assert (size (nbinrnd (1, 0.5, [4, 1])), [4, 1]) %!assert (size (nbinrnd (1, 0.5, 4, 1)), [4, 1]) %!assert (size (nbinrnd (1, 0.5, 4, 1, 5)), [4, 1, 5]) %!assert (size (nbinrnd (1, 0.5, 0, 1)), [0, 1]) %!assert (size (nbinrnd (1, 0.5, 1, 0)), [1, 0]) %!assert (size (nbinrnd (1, 0.5, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (nbinrnd (1, 0.5)), "double") %!assert (class (nbinrnd (1, single (0.5))), "single") %!assert (class (nbinrnd (1, single ([0.5, 0.5]))), "single") %!assert (class (nbinrnd (single (1), 0.5)), "single") %!assert (class (nbinrnd (single ([1, 1]), 0.5)), "single") ## Test input validation %!error nbinrnd () %!error nbinrnd (1) %!error ... %! nbinrnd (ones (3), ones (2)) %!error ... %! nbinrnd (ones (2), ones (3)) %!error nbinrnd (i, 2, 3) %!error nbinrnd (1, i, 3) %!error ... %! nbinrnd (1, 2, -1) %!error ... %! nbinrnd (1, 2, 1.2) %!error ... %! nbinrnd (1, 2, ones (2)) %!error ... %! nbinrnd (1, 2, [2 -1 2]) %!error ... %! nbinrnd (1, 2, [2 0 2.5]) %!error ... %! nbinrnd (1, 2, 2, -1, 5) %!error ... %! nbinrnd (1, 2, 2, 1.5, 5) %!error ... %! nbinrnd (2, ones (2), 3) %!error ... %! nbinrnd (2, ones (2), [3, 2]) %!error ... %! nbinrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/ncfcdf.m000066400000000000000000000226221475240274700215430ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} ncfcdf (@var{x}, @var{df1}, @var{df2}, @var{lambda}) ## @deftypefnx {statistics} {@var{p} =} ncfcdf (@var{x}, @var{df1}, @var{df2}, @var{lambda}, @qcode{"upper"}) ## ## Noncentral @math{F}-cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the noncentral @math{F}-distribution with @var{df1} and @var{df2} ## degrees of freedom and noncentrality parameter @var{lambda}. The size of ## @var{p} is the common size of @var{x}, @var{df1}, @var{df2}, and ## @var{lambda}. A scalar input functions as a constant matrix of the same size ## as the other inputs. ## ## @code{@var{p} = ncfcdf (@var{x}, @var{df1}, @var{df2}, @var{lambda}, "upper")} ## computes the upper tail probability of the noncentral @math{F}-distribution ## with parameters @var{df1}, @var{df2}, and @var{lambda}, at the values in ## @var{x}. ## ## Further information about the noncentral @math{F}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_F-distribution} ## ## @seealso{ncfinv, ncfpdf, ncfrnd, ncfstat, fcdf} ## @end deftypefn function p = ncfcdf (x, df1, df2, lambda, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("ncfcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 4) if (! strcmpi (uflag, "upper")) error ("ncfcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, DF1, DF2, and LAMBDA [err, x, df1, df2, lambda] = common_size (x, df1, df2, lambda); if (err > 0) error ("ncfcdf: X, DF1, DF2, and LAMBDA must be of common size or scalars."); endif ## Check for X, DF1, DF2, and LAMBDA being reals if (iscomplex (x) || iscomplex (df1) || iscomplex (df2) || iscomplex (lambda)) error ("ncfcdf: X, DF1, DF2, and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df1, "single") || ... isa (df2, "single") || isa (lambda, "single")) p = zeros (size (x), "single"); c_eps = eps ("single") .^ (3/4); else p = zeros (size (x)); c_eps = eps .^ (3/4); endif ## Find NaNs in input arguments (if any) and propagate them to p is_nan = isnan (x) | isnan (df1) | isnan (df1) | isnan (lambda); p(is_nan) = NaN; ## For "upper" option, force p = 1 for x <= 0, and p = 0 for x == Inf, ## otherwise, force p = 1 for x == Inf. if (uflag) p(x == Inf & ! is_nan) = 0; p(x <= 0 & ! is_nan) = 1; else p(x == Inf & ! is_nan) = 1; endif ## Find invalid values of parameters and propagate them to p as NaN k = (df1 <= 0 | df2 <= 0 | lambda < 0); p(k) = NaN; ## Compute central distribution (lambda == 0) k0 = (lambda==0); if (any (k0(:))) if (uflag) p(k0) = fcdf (x(k0), df1(k0), df2(k0), "upper"); else p(k0) = fcdf (x(k0), df1(k0), df2(k0)); endif endif ## Check if there are remaining elements and reset variables k1 = ! (k0 | k | x == Inf | x <= 0 | is_nan); if (! any (k1(:))) return; else x = x(k1); df1 = df1(k1); df2 = df2(k1); lambda = lambda(k1); endif ## Prepare variables x = x(:); df1 = df1(:) / 2; df2 = df2(:) / 2; lambda = lambda(:) / 2; ## Value passed to Beta distribution function. tmp = df1 .* x ./ (df2 + df1 .* x); logtmp = log (tmp); nu2const = df2 .* log (1 - tmp) - localgammaln (df2); ## Sum the series. The general idea is that we are going to sum terms ## of the form 'poisspdf(j,lambda) .* betacdf(tmp,j+df1,df2)' j0 = floor (lambda(:)); ## Compute Poisson pdf and beta cdf at the starting point if (uflag) bcdf0 = betainc (tmp, j0 + df1, df2, "upper"); else bcdf0 = betacdf (tmp, j0 + df1, df2); endif ppdf0 = exp (-lambda + j0 .* log (lambda) - localgammaln (j0 + 1)); ## Set up for loop over values less than j0 y = ppdf0 .* bcdf0; ppdf = ppdf0; bcdf = bcdf0; olddy = zeros (size (lambda)); delty = zeros (size (lambda)); j = j0 - 1; ok = j >= 0; while (any (ok)) ## Use recurrence relation to compute new pdf and cdf ppdf(ok) = ppdf(ok) .* (j(ok) + 1) ./ lambda(ok); if (uflag) bcdf(ok) = betainc (tmp(ok), j(ok) + df1(ok), df2(ok), "upper"); else db = exp ((j + df1) .* logtmp + nu2const + ... localgammaln (j + df1 + df2) - localgammaln (j + df1 + 1)); bcdf(ok) = bcdf(ok) + db(ok); endif delty(ok) = ppdf(ok) .* bcdf(ok); y(ok) = y(ok) + delty(ok); ## Convergence test: change must be small and not increasing ok = ok & (delty > y*c_eps | abs (delty) > olddy); j = j - 1; ok = ok & j >= 0; olddy(ok) = abs (delty(ok)); endwhile ## Set up again for loop upward from j0 ppdf = ppdf0; bcdf = bcdf0; olddy = zeros (size (lambda)); j = j0 + 1; ok = true(size(j)); ## Set up for loop to avoid endless loop for jj = 1:5000 ppdf = ppdf .* lambda ./ j; if (uflag) bcdf = betainc (tmp, j + df1, df2, "upper"); else bcdf = bcdf - exp ((j + df1 - 1) .* logtmp + nu2const + ... localgammaln (j + df1 + df2 - 1) - localgammaln (j + df1)); endif delty = ppdf.*bcdf; ## ok = indices not converged y(ok) = y(ok) + delty(ok); ## Convergence test: change must be small and not increasing ok = ok & (delty>y*c_eps | abs(delty)>olddy); ## Break if all indices converged if (! any (ok)) break; endif olddy(ok) = abs (delty(ok)); j = j + 1; endfor if (jj == 5000) warning ("ncfcdf: no convergence."); endif ## Save returning p-value p(k1) = y; endfunction function x = localgammaln (y) x = Inf (size (y), class (y)); x(! (y < 0)) = gammaln (y(! (y < 0))); endfunction %!demo %! ## Plot various CDFs from the noncentral F distribution %! x = 0:0.01:5; %! p1 = ncfcdf (x, 2, 5, 1); %! p2 = ncfcdf (x, 2, 5, 2); %! p3 = ncfcdf (x, 5, 10, 1); %! p4 = ncfcdf (x, 10, 20, 10); %! plot (x, p1, "-r", x, p2, "-g", x, p3, "-k", x, p4, "-m") %! grid on %! xlim ([0, 5]) %! legend ({"df1 = 2, df2 = 5, λ = 1", "df1 = 2, df2 = 5, λ = 2", ... %! "df1 = 5, df2 = 10, λ = 1", "df1 = 10, df2 = 20, λ = 10"}, ... %! "location", "southeast") %! title ("Noncentral F CDF") %! xlabel ("values in x") %! ylabel ("probability") %!demo %! ## Compare the noncentral F CDF with LAMBDA = 10 to the F CDF with the %! ## same number of numerator and denominator degrees of freedom (5, 20) %! %! x = 0.01:0.1:10.01; %! p1 = ncfcdf (x, 5, 20, 10); %! p2 = fcdf (x, 5, 20); %! plot (x, p1, "-", x, p2, "-"); %! grid on %! xlim ([0, 10]) %! legend ({"Noncentral F(5,20,10)", "F(5,20)"}, "location", "southeast") %! title ("Noncentral F vs F CDFs") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!test %! x = -2:0.1:2; %! p = ncfcdf (x, 10, 1, 3); %! assert (p([1:21]), zeros (1, 21), 1e-76); %! assert (p(22), 0.004530737275319753, 1e-14); %! assert (p(30), 0.255842099135669, 1e-14); %! assert (p(41), 0.4379890998457305, 1e-14); %!test %! p = ncfcdf (12, 10, 3, 2); %! assert (p, 0.9582287900447416, 1e-14); %!test %! p = ncfcdf (2, 3, 2, 1); %! assert (p, 0.5731985522994989, 1e-14); %!test %! p = ncfcdf (2, 3, 2, 1, "upper"); %! assert (p, 0.4268014477004823, 1e-14); %!test %! p = ncfcdf ([3, 6], 3, 2, 5, "upper"); %! assert (p, [0.530248523596927, 0.3350482341323044], 1e-14); ## Test input validation %!error ncfcdf () %!error ncfcdf (1) %!error ncfcdf (1, 2) %!error ncfcdf (1, 2, 3) %!error ncfcdf (1, 2, 3, 4, "tail") %!error ncfcdf (1, 2, 3, 4, 5) %!error ... %! ncfcdf (ones (3), ones (2), ones (2), ones (2)) %!error ... %! ncfcdf (ones (2), ones (3), ones (2), ones (2)) %!error ... %! ncfcdf (ones (2), ones (2), ones (3), ones (2)) %!error ... %! ncfcdf (ones (2), ones (2), ones (2), ones (3)) %!error ncfcdf (i, 2, 2, 2) %!error ncfcdf (2, i, 2, 2) %!error ncfcdf (2, 2, i, 2) %!error ncfcdf (2, 2, 2, i) statistics-release-1.7.3/inst/dist_fun/ncfinv.m000066400000000000000000000171771475240274700216140ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} ncfinv (@var{p}, @var{df1}, @var{df2}, @var{lambda}) ## ## Inverse of the noncentral @math{F}-cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the noncentral @math{F}-distribution with @var{df1} and @var{df2} degrees of ## freedom and noncentrality parameter @var{lambda}. The size of @var{x} is the ## common size of @var{p}, @var{df1}, @var{df2}, and @var{lambda}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## @code{ncfinv} uses Newton's method to converge to the solution. ## ## Further information about the noncentral @math{F}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_F-distribution} ## ## @seealso{ncfcdf, ncfpdf, ncfrnd, ncfstat, finv} ## @end deftypefn function x = ncfinv (p, df1, df2, lambda) ## Check for valid number of input arguments if (nargin < 4) error ("ncfinv: function called with too few input arguments."); endif ## Check for common size of P, DF1, DF2, and LAMBDA [err, p, df1, df2, lambda] = common_size (p, df1, df2, lambda); if (err > 0) error ("ncfinv: P, DF1, DF2, and LAMBDA must be of common size or scalars."); endif ## Check for P, DF1, DF2, and LAMBDA being reals if (iscomplex (p) || iscomplex (df1) || iscomplex (df2) || iscomplex (lambda)) error ("ncfinv: P, DF1, DF2, and LAMBDA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (df1, "single") || ... isa (df2, "single") || isa (lambda, "single")) x = NaN (size (p), "single"); crit = sqrt (eps ("single")); else x = NaN (size (p), "double"); crit = sqrt (eps ("double")); endif ## For lambda == 0, call finv d0 = lambda == 0; if (any (d0(:))) x(d0) = finv (p(d0), df1(d0), df2(d0)); endif ## For lambda > 0 and valid dfs valid = df1 > 0 & df2 > 0 & lambda > 0; ## Force x = 0 for p == 0 ax = Inf for p ==1 x(p == 0 & valid) = 0; x(p == 1 & valid) = Inf; ## Find remaining valid cases within the range of 0 < p < 1 k = find (p > 0 & p < 1 & valid); ## Return if nothing left if isempty(k) return; endif ## Reset input variables to remaining cases p = p(k); df1 = df1(k); df2 = df2(k); lambda = lambda(k); ## Initialize counter count_limit = 100; count = 0; ## Start at the mean (if it exists) mu0 = df2.*(df1+lambda) ./ (df1.*max(1,df2-2)); next = mu0; prev = 0; F = ncfcdf (mu0, df1, df2, lambda); while(count < count_limit) count += 1; next = (F - p) ./ ncfpdf (mu0, df1, df2, lambda); ## Prevent oscillations if (length (next) == length (prev)) t = sign (next) == -sign (prev); next(t) = sign (next(t)) .* min (abs (next(t)), abs (prev(t))) / 2; endif ## Prepare for next step mu1 = max (mu0 / 5, min (5 * mu0, mu0 - next)); ## Check that next step improves, otherwise abort F1 = ncfcdf (mu1, df1, df2, lambda); while (true) worse = (abs (F1-p) > abs (F - p) * (1 + crit)) & ... (abs (mu0 - mu1) > crit * mu0); if (! any (worse)) break; endif mu1(worse) = 0.5 * (mu1(worse) + mu0(worse)); F1(worse) = ncfcdf (mu1(worse), df1(worse), df2(worse), lambda(worse)); endwhile x(k) = mu1; ## Find elements that are not converged yet next = mu0 - mu1; mask = (abs (next) > crit * abs (mu0)); if (! any (mask)) break; endif ## Save parameters for these elements only F = F1(mask); mu0 = mu1(mask); prev = next(mask); if (! all(mask)) df1 = df1(mask); df2 = df2(mask); lambda = lambda(mask); p = p(mask); k = k(mask); endif endwhile if (count == count_limit) warning ("ncfinv: did not converge."); endif endfunction %!demo %! ## Plot various iCDFs from the noncentral F distribution %! p = 0.001:0.001:0.999; %! x1 = ncfinv (p, 2, 5, 1); %! x2 = ncfinv (p, 2, 5, 2); %! x3 = ncfinv (p, 5, 10, 1); %! x4 = ncfinv (p, 10, 20, 10); %! plot (p, x1, "-r", p, x2, "-g", p, x3, "-k", p, x4, "-m") %! grid on %! ylim ([0, 5]) %! legend ({"df1 = 2, df2 = 5, λ = 1", "df1 = 2, df2 = 5, λ = 2", ... %! "df1 = 5, df2 = 10, λ = 1", "df1 = 10, df2 = 20, λ = 10"}, ... %! "location", "northwest") %! title ("Noncentral F iCDF") %! xlabel ("probability") %! ylabel ("values in x") %!demo %! ## Compare the noncentral F iCDF with LAMBDA = 10 to the F iCDF with the %! ## same number of numerator and denominator degrees of freedom (5, 20) %! %! p = 0.001:0.001:0.999; %! x1 = ncfinv (p, 5, 20, 10); %! x2 = finv (p, 5, 20); %! plot (p, x1, "-", p, x2, "-"); %! grid on %! ylim ([0, 10]) %! legend ({"Noncentral F(5,20,10)", "F(5,20)"}, "location", "northwest") %! title ("Noncentral F vs F quantile functions") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!test %! x = [0,0.1775,0.3864,0.6395,0.9564,1.3712,1.9471,2.8215,4.3679,8.1865,Inf]; %! assert (ncfinv ([0:0.1:1], 2, 3, 1), x, 1e-4); %!test %! x = [0,0.7492,1.3539,2.0025,2.7658,3.7278,5.0324,6.9826,10.3955,18.7665,Inf]; %! assert (ncfinv ([0:0.1:1], 2, 3, 5), x, 1e-4); %!test %! x = [0,0.2890,0.8632,1.5653,2.4088,3.4594,4.8442,6.8286,10.0983,17.3736,Inf]; %! assert (ncfinv ([0:0.1:1], 1, 4, 3), x, 1e-4); %!test %! x = [0.078410, 0.212716, 0.288618, 0.335752, 0.367963, 0.391460]; %! assert (ncfinv (0.05, [1, 2, 3, 4, 5, 6], 10, 3), x, 1e-6); %!test %! x = [0.2574, 0.2966, 0.3188, 0.3331, 0.3432, 0.3507]; %! assert (ncfinv (0.05, 5, [1, 2, 3, 4, 5, 6], 3), x, 1e-4); %!test %! x = [1.6090, 1.8113, 1.9215, 1.9911, NaN, 2.0742]; %! assert (ncfinv (0.05, 1, [1, 2, 3, 4, -1, 6], 10), x, 1e-4); %!test %! assert (ncfinv (0.996, 3, 5, 8), 58.0912074080671, 4e-12); ## Test input validation %!error ncfinv () %!error ncfinv (1) %!error ncfinv (1, 2) %!error ncfinv (1, 2, 3) %!error ... %! ncfinv (ones (3), ones (2), ones (2), ones (2)) %!error ... %! ncfinv (ones (2), ones (3), ones (2), ones (2)) %!error ... %! ncfinv (ones (2), ones (2), ones (3), ones (2)) %!error ... %! ncfinv (ones (2), ones (2), ones (2), ones (3)) %!error ncfinv (i, 2, 2, 2) %!error ncfinv (2, i, 2, 2) %!error ncfinv (2, 2, i, 2) %!error ncfinv (2, 2, 2, i) statistics-release-1.7.3/inst/dist_fun/ncfpdf.m000066400000000000000000000371101475240274700215560ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} ncfpdf (@var{x}, @var{df1}, @var{df2}, @var{lambda}) ## ## Noncentral @math{F}-probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the noncentral @math{F}-distribution with @var{df1} and @var{df2} degrees ## of freedom and noncentrality parameter @var{lambda}. The size of @var{y} is ## the common size of @var{x}, @var{df1}, @var{df2}, and @var{lambda}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## Further information about the noncentral @math{F}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_F-distribution} ## ## @seealso{ncfcdf, ncfinv, ncfrnd, ncfstat, fpdf} ## @end deftypefn function y = ncfpdf (x, df1, df2, lambda) ## Check for valid number of input arguments if (nargin < 4) error ("ncfpdf: function called with too few input arguments."); endif ## Check for common size of X, DF1, DF2, and LAMBDA [err, x, df1, df2, lambda] = common_size (x, df1, df2, lambda); if (err > 0) error ("ncfpdf: X, DF1, DF2, and LAMBDA must be of common size or scalars."); endif ## Check for X, DF1, DF2, and LAMBDA being reals if (iscomplex (x) || iscomplex (df1) || iscomplex (df2) || iscomplex (lambda)) error ("ncfpdf: X, DF1, DF2, and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df1, "single") || ... isa (df2, "single") || isa (lambda, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Find NaNs in input arguments (if any) and propagate them to p is_nan = isnan (x) | isnan (df1) | isnan (df2) | isnan (lambda); y(is_nan) = NaN; ## Force invalid parameter cases to NaN k1 = df1 <= 0 | df2 <= 0 | lambda < 0; y(k1) = NaN; ## Handle edge cases where x == 0 k2 = x == 0 & df1 < 2 & ! k1; y(k2) = Inf; k3 = x == 0 & df1 == 2 & ! k1; if (any (k3(:))) y(k3) = exp (-lambda(k3) / 2); endif ## Handle central distribution where lambda == 0 k4 = lambda == 0 & ! k1 & x > 0; if any(k4(:)) y(k4) = fpdf (x(k4), df1(k4), df2(k4)); endif ## Handle normal cases td = find (x > 0 & ! (k1 | k4)); ## Return if finished all normal cases if (isempty (td)) return; endif ## Reset input variables to remaining cases and pre-divide df1, df2 and lambda x = x(td); df1 = df1(td) / 2; df2 = df2(td) / 2; lambda = lambda(td) / 2; ## Use z and scaled x for convenience z = df1 .* x ./ (df1 .* x + df2); z1 = df2 ./ (df1 .* x + df2); xs = lambda .* z; % Find max K at which we start the recursion series K = zeros (size (x)); termK = zeros (size (x)); rsum = zeros (size (x)); ## Handy constant lnsr2pi = 0.9189385332046727; ## Process integer and non-integer df2 separately df2int = df2 == floor (df2); if (any (df2int(:))) # integers smallx = xs <= df1 ./ df2; largex = xs >= df2 .* (df1 + df2 - 1) & ! smallx; K(df2int & largex) = df2(df2int & largex); ## Compute K idx = df2int & ! (smallx | largex); if (any (idx(:))) d = 0.5 * (1 - xs(idx) - df1(idx)); K(idx) = floor (d + sqrt (d .^ 2 + xs(idx) .* (df2(idx) + 1))); endif ## For K == df2 K_df2 = df2int & K == df2; idz1 = K_df2 & z < 0.9; termK(idz1) = (df1(idz1) + df2(idz1) - 1) .* log (z(idz1)); idz2 = K_df2 & ! idz1; termK(idz2) = (df1(idz2) + df2(idz2) - 1) .* log1p (-z1(idz2)); ## For K == 0 Kzero = df2int & (df1 + K) <= 1; termK(Kzero) = StirlingError (df1(Kzero) + df2(Kzero)) - ... StirlingError (df1(Kzero)) - StirlingError (df2(Kzero)) - ... BinoPoisson (df1(Kzero), ... (df1(Kzero) + df2(Kzero)) .* z(Kzero)) - ... BinoPoisson (df2(Kzero), ... (df1(Kzero) + df2(Kzero)) .* z1(Kzero)); ## For all other K K_all = df2int & ! (K_df2 | Kzero); termK(K_all) = StirlingError (df1(K_all) + df2(K_all) - 1) - ... StirlingError (df1(K_all) + K(K_all) -1) - ... StirlingError (df2(K_all) - K(K_all)) - ... BinoPoisson (df1(K_all) + K(K_all) - 1, ... (df1(K_all) + df2(K_all) - 1) .* z(K_all)) - ... BinoPoisson (df2(K_all) - K(K_all), ... (df1(K_all) + df2(K_all) - 1) .* z1(K_all)); ## Poisson density for the leading term x1 = lambda .* z1; smallk = df2int & K <= x1 * realmin; y(td(smallk)) = termK(smallk) - x1(smallk); otherk = df2int & ! smallk; y(td(otherk)) = termK(otherk) - lnsr2pi - 0.5 * log (K(otherk)) - ... StirlingError (K(otherk)) - ... BinoPoisson (K(otherk), x1(otherk)); ## Sum recursively downwards term = ones (size (x)); k = K; ok = df2int & k > 0; while (any (ok(:))) k(ok) = k(ok) - 1; term(ok) = term(ok) .* (k(ok) + 1) .* ... (k(ok) + df1(ok)) ./ (df2(ok) - k(ok)) ./ xs(ok); ok = ok & term >= eps (rsum); rsum(ok) = rsum(ok) + term(ok); endwhile ## Sum recursively upwards term = ones (size (x)); k = K; ok = df2int & k < df2; while any(ok(:)) term(ok) = term(ok) .* xs(ok) .* ... (df2(ok) - k(ok)) ./ (k(ok) + df1(ok)) ./ (k(ok) + 1); ok = ok & term >= eps(rsum); rsum(ok) = rsum(ok) + term(ok); k(ok) = k(ok) + 1; endwhile endif if (any (! df2int(:))) # non-integers ## Compute K largex = ! df2int & xs > df1 ./ (df1 + df2); d = 0.5 * (1 + xs(largex) - df1(largex)); K(largex) = floor (d + sqrt (d .^ 2 + xs(largex) .* ... (df1(largex) + df2(largex) - 1))); ## For K == 0 Kzero = ! df2int & (df1 + K) <= 1; termK(Kzero) = StirlingError (df1(Kzero) + df2(Kzero)) - ... StirlingError (df1(Kzero)) - ... StirlingError (df2(Kzero)) - ... BinoPoisson (df1(Kzero), ... (df1(Kzero) + df2(Kzero)) .* z(Kzero)) - ... BinoPoisson (df2(Kzero), ... (df1(Kzero) + df2(Kzero)) .* z1(Kzero)); ## For K != 0 K_all = ! df2int & ! Kzero; termK(K_all) = StirlingError (df1(K_all) + df2(K_all) + K(K_all) - 1) - ... StirlingError (df1(K_all) + K(K_all) - 1) - ... StirlingError (df2(K_all)) - ... BinoPoisson (df1(K_all) + K(K_all) - 1, ... (df1(K_all) + df2(K_all) + K(K_all) - 1) .* ... z(K_all)) - ... BinoPoisson (df2(K_all), ... (df1(K_all) + df2(K_all) + K(K_all) - 1) .* ... z1(K_all)); ## Poisson density for the leading term smallk = ! df2int & K <= lambda * realmin; y(td(smallk)) = termK(smallk) - lambda(smallk); K_all = ! df2int & ! smallk; y(td(K_all)) = termK(K_all) - lnsr2pi - 0.5 * log (K(K_all)) - ... StirlingError (K(K_all)) - ... BinoPoisson (K(K_all), lambda(K_all)); ## Sum recursively downwards term = ones (size (x)); k = K; ok = ! df2int & k > 0; while (any (ok(:))) k(ok) = k(ok) - 1; term(ok) = term(ok) .* (k(ok) + 1) .* (k(ok) + df1(ok)) ./ ... (k(ok) + df1(ok) + df2(ok)) ./ xs(ok); ok = ok & term >= eps (rsum); rsum(ok) = rsum(ok) + term(ok); endwhile ## Sum recursively upwards term = ones (size (x)); k = K; ok = ! df2int; while (any (ok(:))) term(ok) = term(ok) .* xs(ok) .* (k(ok) + df1(ok) + df2(ok)) ./ ... (k(ok) + df1(ok)) ./ (k(ok) + 1); ok = ok & term >= eps (rsum); rsum(ok) = rsum(ok) + term(ok); k(ok) = k(ok)+1; endwhile endif ## Compute density pi2 = 2 * pi; Kzero = (df1 + K) <= 1; y(td(Kzero)) = exp (y(td(Kzero))) .* (1 + rsum(Kzero)) .* ... sqrt (df1(Kzero) .* df2(Kzero) ./ ... (df1(Kzero) + df2(Kzero)) / pi2) ./ ... x(Kzero); K_df2 = ! Kzero & df2int & K == df2; y(td(K_df2)) = exp (y(td(K_df2))) .* (1 + rsum(K_df2)) .* ... df1(K_df2) .* z1(K_df2); idx = ! Kzero & df2int & ! K_df2; y(td(idx)) = exp (y(td(idx))) .* (1 + rsum(idx)) .* df1(idx) .* z1(idx) .* ... sqrt((df1(idx) + df2(idx) - 1) ./ (df2(idx) - K(idx)) ./ ... (df1(idx) + K(idx) - 1) / pi2); idx = ! df2int & ! Kzero; y(td(idx)) = exp (y(td(idx))) .* (1 + rsum(idx)) .* df1(idx) .* z1(idx) .* ... sqrt ((df1(idx) + df2(idx) + K(idx) - 1) ./ ... df2(idx) ./ (df1(idx) + K(idx) - 1) / pi2); endfunction ## Error of Stirling-De Moivre approximation to n factorial. function lambda = StirlingError (n) is_class = class (n); lambda = zeros (size (n), is_class); nn = n .* n; ## Define S0=1/12 S1=1/360 S2=1/1260 S3=1/1680 S4=1/1188 S0 = 8.333333333333333e-02; S1 = 2.777777777777778e-03; S2 = 7.936507936507937e-04; S3 = 5.952380952380952e-04; S4 = 8.417508417508418e-04; ## Define lambda(n) for n<0:0.5:15 sfe=[ 0; 1.534264097200273e-01;... 8.106146679532726e-02; 5.481412105191765e-02;... 4.134069595540929e-02; 3.316287351993629e-02;... 2.767792568499834e-02; 2.374616365629750e-02;... 2.079067210376509e-02; 1.848845053267319e-02;... 1.664469118982119e-02; 1.513497322191738e-02;... 1.387612882307075e-02; 1.281046524292023e-02;... 1.189670994589177e-02; 1.110455975820868e-02;... 1.041126526197210e-02; 9.799416126158803e-03;... 9.255462182712733e-03; 8.768700134139385e-03;... 8.330563433362871e-03; 7.934114564314021e-03;... 7.573675487951841e-03; 7.244554301320383e-03;... 6.942840107209530e-03; 6.665247032707682e-03;... 6.408994188004207e-03; 6.171712263039458e-03;... 5.951370112758848e-03; 5.746216513010116e-03;... 5.554733551962801e-03]; k = find (n <= 15); if (any (k)) n1 = n(k); n2 = 2 * n1; if (all (n2 == round (n2))) lambda(k) = sfe(n2+1); else lnsr2pi = 0.9189385332046728; lambda(k) = gammaln(n1+1)-(n1+0.5).*log(n1)+n1-lnsr2pi; endif endif k = find (n > 15 & n <= 35); if (any (k)) lambda(k) = (S0 - (S1 - (S2 - (S3 - S4 ./ nn(k)) ./ nn(k)) ./ ... nn(k)) ./ nn(k)) ./ n(k); endif k = find (n > 35 & n <= 80); if (any (k)) lambda(k) = (S0 - (S1 - (S2 - S3 ./ nn(k)) ./ nn(k)) ./ nn(k)) ./ n(k); endif k = find(n > 80 & n <= 500); if (any (k)) lambda(k) = (S0 - (S1 - S2 ./ nn(k)) ./ nn(k)) ./ n(k); endif k = find(n > 500); if (any (k)) lambda(k) = (S0 - S1 ./ nn(k)) ./ n(k); endif endfunction ## Deviance term for binomial and Poisson probability calculation. function BP = BinoPoisson (x, np) if (isa (x,'single') || isa (np,'single')) BP = zeros (size (x), "single"); else BP = zeros (size (x)); endif k = abs (x - np) < 0.1 * (x + np); if any(k(:)) s = (x(k) - np(k)) .* (x(k) - np(k)) ./ (x(k) + np(k)); v = (x(k) - np(k)) ./ (x(k) + np(k)); ej = 2 .* x(k) .* v; is_class = class (s); s1 = zeros (size (s), is_class); ok = true (size (s)); j = 0; while any(ok(:)) ej(ok) = ej(ok) .* v(ok) .* v(ok); j = j + 1; s1(ok) = s(ok) + ej(ok) ./ (2 * j + 1); ok = ok & s1 != s; s(ok) = s1(ok); endwhile BP(k) = s; endif k = ! k; if (any (k(:))) BP(k) = x(k) .* log (x(k) ./ np(k)) + np(k) - x(k); endif endfunction %!demo %! ## Plot various PDFs from the noncentral F distribution %! x = 0:0.01:5; %! y1 = ncfpdf (x, 2, 5, 1); %! y2 = ncfpdf (x, 2, 5, 2); %! y3 = ncfpdf (x, 5, 10, 1); %! y4 = ncfpdf (x, 10, 20, 10); %! plot (x, y1, "-r", x, y2, "-g", x, y3, "-k", x, y4, "-m") %! grid on %! xlim ([0, 5]) %! ylim ([0, 0.8]) %! legend ({"df1 = 2, df2 = 5, λ = 1", "df1 = 2, df2 = 5, λ = 2", ... %! "df1 = 5, df2 = 10, λ = 1", "df1 = 10, df2 = 20, λ = 10"}, ... %! "location", "northeast") %! title ("Noncentral F PDF") %! xlabel ("values in x") %! ylabel ("density") %!demo %! ## Compare the noncentral F PDF with LAMBDA = 10 to the F PDF with the %! ## same number of numerator and denominator degrees of freedom (5, 20) %! %! x = 0.01:0.1:10.01; %! y1 = ncfpdf (x, 5, 20, 10); %! y2 = fpdf (x, 5, 20); %! plot (x, y1, "-", x, y2, "-"); %! grid on %! xlim ([0, 10]) %! ylim ([0, 0.8]) %! legend ({"Noncentral F(5,20,10)", "F(5,20)"}, "location", "northeast") %! title ("Noncentral F vs F PDFs") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x1, df1, df2, lambda %! x1 = [-Inf, 2, NaN, 4, Inf]; %! df1 = [2, 0, -1, 1, 4]; %! df2 = [2, 4, 5, 6, 8]; %! lambda = [1, NaN, 3, -1, 2]; %!assert (ncfpdf (x1, df1, df2, lambda), [0, NaN, NaN, NaN, NaN]); %!assert (ncfpdf (x1, df1, df2, 1), [0, NaN, NaN, ... %! 0.05607937264237208, NaN], 1e-14); %!assert (ncfpdf (x1, df1, df2, 3), [0, NaN, NaN, ... %! 0.080125760971946518, NaN], 1e-14); %!assert (ncfpdf (x1, df1, df2, 2), [0, NaN, NaN, ... %! 0.0715902008258656, NaN], 1e-14); %!assert (ncfpdf (x1, 3, 5, lambda), [0, NaN, NaN, NaN, NaN]); %!assert (ncfpdf (2, df1, df2, lambda), [0.1254046999837947, NaN, NaN, ... %! NaN, 0.2152571783045893], 1e-14); %!assert (ncfpdf (4, df1, df2, lambda), [0.05067089541001374, NaN, NaN, ... %! NaN, 0.05560846335398539], 1e-14); ## Test input validation %!error ncfpdf () %!error ncfpdf (1) %!error ncfpdf (1, 2) %!error ncfpdf (1, 2, 3) %!error ... %! ncfpdf (ones (3), ones (2), ones (2), ones (2)) %!error ... %! ncfpdf (ones (2), ones (3), ones (2), ones (2)) %!error ... %! ncfpdf (ones (2), ones (2), ones (3), ones (2)) %!error ... %! ncfpdf (ones (2), ones (2), ones (2), ones (3)) %!error ncfpdf (i, 2, 2, 2) %!error ncfpdf (2, i, 2, 2) %!error ncfpdf (2, 2, i, 2) %!error ncfpdf (2, 2, 2, i) statistics-release-1.7.3/inst/dist_fun/ncfrnd.m000066400000000000000000000166171475240274700216010ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} ncfrnd (@var{df1}, @var{df2}, @var{lambda}) ## @deftypefnx {statistics} {@var{r} =} ncfrnd (@var{df1}, @var{df2}, @var{lambda}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} ncfrnd (@var{df1}, @var{df2}, @var{lambda}, [@var{sz}]) ## ## Random arrays from the noncentral @math{F}-distribution. ## ## @code{@var{x} = ncfrnd (@var{p}, @var{df1}, @var{df2}, @var{lambda})} returns ## an array of random numbers chosen from the noncentral @math{F}-distribution with ## @var{df1} and @var{df2} degrees of freedom and noncentrality parameter ## @var{lambda}. The size of @var{r} is the common size of @var{df1}, ## @var{df2}, and @var{lambda}. A scalar input functions as a constant matrix ## of the same size as the other input. ## ## @code{ncfrnd} generates values using the definition of a noncentral @math{F} ## random variable, as the ratio of a noncentral chi-squared distribution and a ## (central) chi-squared distribution. ## ## When called with a single size argument, @code{ncfrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the noncentral @math{F}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_F-distribution} ## ## @seealso{ncfcdf, ncfinv, ncfpdf, ncfstat, frnd, ncx2rnd, chi2rnd} ## @end deftypefn function r = ncfrnd (df1, df2, lambda, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("ncfrnd: function called with too few input arguments."); endif ## Check for common size of DF1, DF2, and LAMBDA if (! isscalar (df1) || ! isscalar (df2) || ! isscalar (lambda)) [retval, df1, df2, lambda] = common_size (df1, df2, lambda); if (retval > 0) error ("ncfrnd: DF1, DF2, and LAMBDA must be of common size or scalars."); endif endif ## Check for DF1, DF2, and LAMBDA being reals if (iscomplex (df1) || iscomplex (df2) || iscomplex (lambda)) error ("ncfrnd: DF1, DF2, and LAMBDA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (df1); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["ncfrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("ncfrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (df1) && ! isequal (size (df1), sz)) error ("ncfrnd: DF1, DF2, and LAMBDA must be scalars or of size SZ."); endif ## Check for class type if (isa (df1, "single") || isa (df2, "single") || isa (lambda, "single")); cls = "single"; else cls = "double"; endif ## Return NaNs for out of range values of DF1, DF2, and LAMBDA df1(df1 <= 0) = NaN; df2(df2 <= 0) = NaN; lambda(lambda <= 0) = NaN; ## Generate random sample from noncentral F distribution r = (ncx2rnd (df1, lambda, sz) ./ df1) ./ ... (2 .* randg (df2 ./ 2, sz) ./ df2); ## Cast to appropriate class r = cast (r, cls); endfunction ## Test output %!assert (size (ncfrnd (1, 1, 1)), [1 1]) %!assert (size (ncfrnd (1, ones (2,1), 1)), [2, 1]) %!assert (size (ncfrnd (1, ones (2,2), 1)), [2, 2]) %!assert (size (ncfrnd (ones (2,1), 1, 1)), [2, 1]) %!assert (size (ncfrnd (ones (2,2), 1, 1)), [2, 2]) %!assert (size (ncfrnd (1, 1, 1, 3)), [3, 3]) %!assert (size (ncfrnd (1, 1, 1, [4, 1])), [4, 1]) %!assert (size (ncfrnd (1, 1, 1, 4, 1)), [4, 1]) %!assert (size (ncfrnd (1, 1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (ncfrnd (1, 1, 1, 0, 1)), [0, 1]) %!assert (size (ncfrnd (1, 1, 1, 1, 0)), [1, 0]) %!assert (size (ncfrnd (1, 1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (ncfrnd (1, 1, 1)), "double") %!assert (class (ncfrnd (1, single (1), 1)), "single") %!assert (class (ncfrnd (1, 1, single (1))), "single") %!assert (class (ncfrnd (1, single ([1, 1]), 1)), "single") %!assert (class (ncfrnd (1, 1, single ([1, 1]))), "single") %!assert (class (ncfrnd (single (1), 1, 1)), "single") %!assert (class (ncfrnd (single ([1, 1]), 1, 1)), "single") ## Test input validation %!error ncfrnd () %!error ncfrnd (1) %!error ncfrnd (1, 2) %!error ... %! ncfrnd (ones (3), ones (2), ones (2)) %!error ... %! ncfrnd (ones (2), ones (3), ones (2)) %!error ... %! ncfrnd (ones (2), ones (2), ones (3)) %!error ncfrnd (i, 2, 3) %!error ncfrnd (1, i, 3) %!error ncfrnd (1, 2, i) %!error ... %! ncfrnd (1, 2, 3, -1) %!error ... %! ncfrnd (1, 2, 3, 1.2) %!error ... %! ncfrnd (1, 2, 3, ones (2)) %!error ... %! ncfrnd (1, 2, 3, [2 -1 2]) %!error ... %! ncfrnd (1, 2, 3, [2 0 2.5]) %!error ... %! ncfrnd (1, 2, 3, 2, -1, 5) %!error ... %! ncfrnd (1, 2, 3, 2, 1.5, 5) %!error ... %! ncfrnd (2, ones (2), 2, 3) %!error ... %! ncfrnd (2, ones (2), 2, [3, 2]) %!error ... %! ncfrnd (2, ones (2), 2, 3, 2) statistics-release-1.7.3/inst/dist_fun/nctcdf.m000066400000000000000000000277401475240274700215670ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} nctcdf (@var{x}, @var{df}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} nctcdf (@var{x}, @var{df}, @var{mu}, @qcode{"upper"}) ## ## Noncentral @math{t}-cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the noncentral @math{t}-distribution with @var{df} degrees of ## freedom and noncentrality parameter @var{mu}. The size of @var{p} is the ## common size of @var{x}, @var{df}, and @var{mu}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## @code{@var{p} = nctcdf (@var{x}, @var{df}, @var{mu}, "upper")} computes ## the upper tail probability of the noncentral @math{t}-distribution with ## parameters @var{df} and @var{mu}, at the values in @var{x}. ## ## Further information about the noncentral @math{t}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_t-distribution} ## ## @seealso{nctinv, nctpdf, nctrnd, nctstat, tcdf} ## @end deftypefn function p = nctcdf (x, df, mu, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("nctcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("nctcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, DF, and MU [err, x, df, mu] = common_size (x, df, mu); if (err > 0) error ("nctcdf: X, DF, and MU must be of common size or scalars."); endif ## Check for X, DF, and MU being reals if (iscomplex (x) || iscomplex (df) || iscomplex (mu)) error ("nctcdf: X, DF, and MU must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df, "single") || isa (mu, "single")) p = zeros (size (x), "single"); c_eps = eps ("single"); else p = zeros (size (x)); c_eps = eps; endif ## Find NaNs in input arguments (if any) and propagate them to p is_nan = isnan (x) | isnan (df) | isnan (mu); p(is_nan) = NaN; ## Find special cases for mu==0 and x<0; and x = Inf. case_Dinf = (df <= 0 | isinf(mu)) & ! is_nan; case_Dzero = mu == 0 & ! case_Dinf & ! is_nan; case_Xzero = x < 0 & ! case_Dzero & ! case_Dinf & ! is_nan; case_Xinf = x == Inf & ! case_Dzero & ! case_Dinf & ! is_nan; case_DFbig = df > 2e6 & ! case_Dzero & ! case_Dinf & ! case_Xinf & ! is_nan; flag_Dinf = any (case_Dinf(:)); flag_Dzero = any (case_Dzero(:)); flag_Xzero = any (case_Xzero(:)); flag_Xinf = any (case_Xinf(:)); flag_DFbig = any (case_DFbig(:)); ## Handle special cases if (flag_Dinf || flag_Dzero || flag_Xzero || flag_Xinf || flag_DFbig) if (flag_Dinf) p(case_Dinf) = NaN; endif if (flag_Dzero) if (uflag) p(case_Dzero) = tcdf (x(case_Dzero), df(case_Dzero), "upper"); else p(case_Dzero) = tcdf (x(case_Dzero), df(case_Dzero)); endif endif if (flag_Xinf) if (uflag) p(case_Xinf) = 0; else p(case_Xinf) = 1; endif endif if (flag_DFbig) s = 1 - 1 ./ (4 * df); d = sqrt (1 + x .^ 2 ./ (2 * df)); if (uflag) p(case_DFbig) = normcdf (x(case_DFbig) .* s(case_DFbig), ... mu(case_DFbig), d(case_DFbig), "upper"); else p(case_DFbig) = normcdf (x(case_DFbig) .* s(case_DFbig), ... mu(case_DFbig), d(case_DFbig)); endif endif fp = ! (case_Dinf | case_Dzero | case_Xzero | case_Xinf | case_DFbig); if (any (fp(:))) if (uflag) p(fp) = nctcdf (x(fp), df(fp), mu(fp), "upper"); else p(fp) = nctcdf (x(fp), df(fp), mu(fp)); endif endif if (flag_Xzero) if (uflag) p(case_Xzero) = nctcdf (-x(case_Xzero), df(case_Xzero), ... -mu(case_Xzero)); else p(case_Xzero) = nctcdf (-x(case_Xzero), df(case_Xzero), ... -mu(case_Xzero), "upper"); endif endif return endif ## Compute value for betainc function. x_square = x .^ 2; denom = df + x_square; P = x_square ./ denom; Q = df ./ denom; ## Initialize infinite sum. d_square = mu .^ 2; ## Compute probability P[TD<0] (first term) if (uflag) x_zero = x == 0 & ! is_nan; if (any (x_zero(:))) fx = normcdf (- mu, 0, 1, "upper"); p(x_zero)= fx(x_zero); endif else p(! is_nan) = normcdf (- mu(! is_nan), 0, 1); endif ## Compute probability P[0 (abs (subtotal(TD)) + c_eps) * c_eps); if (! any (TD)) break; end ## Update for next iteration jj = jj+2; E1(TD) = E1(TD) .* d_square(TD) ./ (jj(TD)); E2(TD) = E2(TD) .* d_square(TD) ./ (jj(TD) + 1); if (uflag) B1(TD) = betainc (P(TD), (jj(TD) + 1) / 2, df(TD) / 2, "upper"); B2(TD) = betainc (P(TD), (jj(TD) + 2) / 2, df(TD) / 2, "upper"); else B1(TD) = B1(TD) - R1(TD); B2(TD) = B2(TD) - R2(TD); R1(TD) = R1(TD) .* P(TD) .* (jj(TD)+df(TD)-1) ./ (jj(TD)+1); R2(TD) = R2(TD) .* P(TD) .* (jj(TD)+df(TD) ) ./ (jj(TD)+2); endif endwhile ## Go back to the peak and start looping downward as far as necessary. E1 = E10; E2 = E20; B1 = B10; B2 = B20; R1 = R10; R2 = R20; jj = j0; TD = (jj > 0); while (any (TD)) JJ = jj(TD); E1(TD) = E1(TD) .* (JJ ) ./ d_square(TD); E2(TD) = E2(TD) .* (JJ+1) ./ d_square(TD); R1(TD) = R1(TD) .* (JJ+1) ./ ((JJ+df(TD)-1) .* P(TD)); R2(TD) = R2(TD) .* (JJ+2) ./ ((JJ+df(TD)) .* P(TD)); if (uflag) B1(TD) = betainc (P(TD), (JJ - 1) / 2, df(TD) / 2, "upper"); B2(TD) = betainc (P(TD), JJ / 2, df(TD) / 2, "upper"); else B1(TD) = B1(TD) + R1(TD); B2(TD) = B2(TD) + R2(TD); end twoterms = E1(TD) .* B1(TD) + E2(TD) .* B2(TD); subtotal(TD) = subtotal(TD) + twoterms; jj = jj - 2; TD(TD) = (abs (twoterms) > (abs (subtotal(TD)) + c_eps) * c_eps) & ... (jj(TD) > 0); endwhile p(x_notzero) = min (1, max (0, p(x_notzero) + subtotal / 2)); endif endfunction %!demo %! ## Plot various CDFs from the noncentral Τ distribution %! x = -5:0.01:5; %! p1 = nctcdf (x, 1, 0); %! p2 = nctcdf (x, 4, 0); %! p3 = nctcdf (x, 1, 2); %! p4 = nctcdf (x, 4, 2); %! plot (x, p1, "-r", x, p2, "-g", x, p3, "-k", x, p4, "-m") %! grid on %! xlim ([-5, 5]) %! legend ({"df = 1, μ = 0", "df = 4, μ = 0", ... %! "df = 1, μ = 2", "df = 4, μ = 2"}, "location", "southeast") %! title ("Noncentral Τ CDF") %! xlabel ("values in x") %! ylabel ("probability") %!demo %! ## Compare the noncentral T CDF with MU = 1 to the T CDF %! ## with the same number of degrees of freedom (10). %! %! x = -5:0.1:5; %! p1 = nctcdf (x, 10, 1); %! p2 = tcdf (x, 10); %! plot (x, p1, "-", x, p2, "-") %! grid on %! xlim ([-5, 5]) %! legend ({"Noncentral T(10,1)", "T(10)"}, "location", "southeast") %! title ("Noncentral T vs T CDFs") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!test %! x = -2:0.1:2; %! p = nctcdf (x, 10, 1); %! assert (p(1), 0.003302485766631558, 1e-14); %! assert (p(2), 0.004084668193532631, 1e-14); %! assert (p(3), 0.005052800319478737, 1e-14); %! assert (p(41), 0.8076115625303751, 1e-14); %!test %! p = nctcdf (12, 10, 3); %! assert (p, 0.9997719343243797, 1e-14); %!test %! p = nctcdf (2, 3, 2); %! assert (p, 0.4430757822176028, 1e-14); %!test %! p = nctcdf (2, 3, 2, "upper"); %! assert (p, 0.5569242177823971, 1e-14); %!test %! p = nctcdf ([3, 6], 3, 2, "upper"); %! assert (p, [0.3199728259444777, 0.07064855592441913], 1e-14); ## Test input validation %!error nctcdf () %!error nctcdf (1) %!error nctcdf (1, 2) %!error nctcdf (1, 2, 3, "tail") %!error nctcdf (1, 2, 3, 4) %!error ... %! nctcdf (ones (3), ones (2), ones (2)) %!error ... %! nctcdf (ones (2), ones (3), ones (2)) %!error ... %! nctcdf (ones (2), ones (2), ones (3)) %!error nctcdf (i, 2, 2) %!error nctcdf (2, i, 2) %!error nctcdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nctinv.m000066400000000000000000000152151475240274700216210ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} ncx2inv (@var{p}, @var{df}, @var{mu}) ## ## Inverse of the non-central @math{t}-cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the noncentral @math{t}-distribution with @var{df} degrees of freedom and ## noncentrality parameter @var{mu}. The size of @var{x} is the common size ## of @var{p}, @var{df}, and @var{mu}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## @code{nctinv} uses Newton's method to converge to the solution. ## ## Further information about the noncentral @math{t}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_t-distribution} ## ## @seealso{nctcdf, nctpdf, nctrnd, nctstat, tinv} ## @end deftypefn function x = nctinv (p, df, mu) ## Check for valid number of input arguments if (nargin < 3) error ("nctinv: function called with too few input arguments."); endif ## Check for common size of P, DF, and MU [err, p, df, mu] = common_size (p, df, mu); if (err > 0) error ("nctinv: P, DF, and MU must be of common size or scalars."); endif ## Check for P, DF, and MU being reals if (iscomplex (p) || iscomplex (df) || iscomplex (mu)) error ("nctinv: P, DF, and MU must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (df, "single") || isa (mu, "single")) x = NaN (size (p), "single"); crit = sqrt (eps ("single")); else x = NaN (size (p), "double"); crit = sqrt (eps ("double")); endif ## For mu == 0, call chi2inv m0 = mu == 0; if (any (m0(:))) x(m0) = tinv (p(m0), df(m0)); ## If mu == 0 for all entries, then return if (all (m0(:))) return; endif endif ## For all valid entries valid = df > 0 & ! isnan (mu) & ! isinf (mu); ## Force x = -Inf for p == 0 and x = Inf for p == 1 x(p == 0 & valid) = -Inf; x(p == 1 & valid) = Inf; ## Find valid samples within the range of 0 < p < 1 k = find (p > 0 & p < 1 & valid); p_k = p(k); df_k = df(k); mu_k = mu(k); ## Initialize counter count_limit = 100; count = 0; ## Supply a starting guess for the iteration with norminv x_k = norminv (p_k, mu_k, 1); h_k = ones (size (x_k), class (x_k)); ## Start iteration with a break out loop F = nctcdf (x_k, df_k, mu_k); while (any (abs (h_k) > crit * abs (x_k)) && ... max (abs (h_k)) > crit && count < count_limit) count = count + 1; h_k = (F - p_k) ./ nctpdf (x_k, df_k, mu_k); ## Prevent Infs - NaNs infnan = isinf(h_k) | isnan(h_k); if (any (infnan(:))) h_k(infnan) = x_k(infnan) / 10; endif ## Prepare for next step xnew = max (-5 * abs (x_k), min (5 * abs (x_k), x_k - h_k)); ## Check that next step improves, otherwise abort Fnew = nctcdf (xnew, df_k, mu_k); while (true) worse = (abs (Fnew - p_k) > abs (F - p_k) * (1 + crit)) & ... (abs (x_k - xnew) > crit * abs (x_k)); if (! any (worse)) break; endif xnew(worse) = 0.5 * (xnew(worse) + x_k(worse)); Fnew(worse) = nctcdf (xnew(worse), df_k(worse), mu_k(worse)); endwhile x_k = xnew; F = Fnew; endwhile ## Return the converged value(s). x(k) = x_k; if (count == count_limit) warning ("nctinv: did not converge."); endif endfunction %!demo %! ## Plot various iCDFs from the noncentral T distribution %! p = 0.001:0.001:0.999; %! x1 = nctinv (p, 1, 0); %! x2 = nctinv (p, 4, 0); %! x3 = nctinv (p, 1, 2); %! x4 = nctinv (p, 4, 2); %! plot (p, x1, "-r", p, x2, "-g", p, x3, "-k", p, x4, "-m") %! grid on %! ylim ([-5, 5]) %! legend ({"df = 1, μ = 0", "df = 4, μ = 0", ... %! "df = 1, μ = 2", "df = 4, μ = 2"}, "location", "northwest") %! title ("Noncentral T iCDF") %! xlabel ("probability") %! ylabel ("values in x") %!demo %! ## Compare the noncentral T iCDF with MU = 1 to the T iCDF %! ## with the same number of degrees of freedom (10). %! %! p = 0.001:0.001:0.999; %! x1 = nctinv (p, 10, 1); %! x2 = tinv (p, 10); %! plot (p, x1, "-", p, x2, "-"); %! grid on %! ylim ([-5, 5]) %! legend ({"Noncentral T(10,1)", "T(10)"}, "location", "northwest") %! title ("Noncentral T vs T quantile functions") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!test %! x = [-Inf,-0.3347,0.1756,0.5209,0.8279,1.1424,1.5021,1.9633,2.6571,4.0845,Inf]; %! assert (nctinv ([0:0.1:1], 2, 1), x, 1e-4); %!test %! x = [-Inf,1.5756,2.0827,2.5343,3.0043,3.5406,4.2050,5.1128,6.5510,9.6442,Inf]; %! assert (nctinv ([0:0.1:1], 2, 3), x, 1e-4); %!test %! x = [-Inf,2.2167,2.9567,3.7276,4.6464,5.8455,7.5619,10.3327,15.7569,31.8159,Inf]; %! assert (nctinv ([0:0.1:1], 1, 4), x, 1e-4); %!test %! x = [1.7791 1.9368 2.0239 2.0801 2.1195 2.1489]; %! assert (nctinv (0.05, [1, 2, 3, 4, 5, 6], 4), x, 1e-4); %!test %! x = [-0.7755, 0.3670, 1.2554, 2.0239, 2.7348, 3.4154]; %! assert (nctinv (0.05, 3, [1, 2, 3, 4, 5, 6]), x, 1e-4); %!test %! x = [-0.7183, 0.3624, 1.2878, 2.1195, -3.5413, 3.6430]; %! assert (nctinv (0.05, 5, [1, 2, 3, 4, -1, 6]), x, 1e-4); %!test %! assert (nctinv (0.996, 5, 8), 30.02610554063658, 2e-11); ## Test input validation %!error nctinv () %!error nctinv (1) %!error nctinv (1, 2) %!error ... %! nctinv (ones (3), ones (2), ones (2)) %!error ... %! nctinv (ones (2), ones (3), ones (2)) %!error ... %! nctinv (ones (2), ones (2), ones (3)) %!error nctinv (i, 2, 2) %!error nctinv (2, i, 2) %!error nctinv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nctpdf.m000066400000000000000000000142571475240274700216030ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} nctpdf (@var{x}, @var{df}, @var{mu}) ## ## Noncentral @math{t}-probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the noncentral @math{t}-distribution with @var{df} degrees of freedom and ## noncentrality parameter @var{mu}. The size of @var{y} is the common size ## of @var{x}, @var{df}, and @var{mu}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## Further information about the noncentral @math{t}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_t-distribution} ## ## @seealso{nctcdf, nctinv, nctrnd, nctstat, tpdf} ## @end deftypefn function y = nctpdf (x, df, mu) ## Check for valid number of input arguments if (nargin < 3) error ("nctpdf: function called with too few input arguments."); endif ## Check for common size of X, DF, and MU [err, x, df, mu] = common_size (x, df, mu); if (err > 0) error ("nctpdf: X, DF, and MU must be of common size or scalars."); endif ## Check for X, DF, and MU being reals if (iscomplex (x) || iscomplex (df) || iscomplex (mu)) error ("nctpdf: X, DF, and MU must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df, "single") || isa (mu, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Find NaNs in input arguments (if any) and propagate them to p is_nan = isnan (x) | isnan (df) | isnan (mu); y(is_nan) = NaN; ## Force invalid parameter cases to NaN invalid = df <= 0 | ! isfinite (mu); y(invalid) = NaN; ## Use normal approximation for df > 1e6 bigDF = df > 1e6 & ! is_nan & ! invalid; if (any (bigDF(:))) s = 1 - 1 ./ (4 * df); d = sqrt (1 + x .^ 2 ./ (2 * df)); y(bigDF) = normpdf (x(bigDF) .* s(bigDF), mu(bigDF), d(bigDF)); endif ## For negative x use left tail cdf x_neg = find ((x < 0) & isfinite (x) & df <= 1e6 & ! is_nan & ! invalid); if (any (x_neg)) y(x_neg) = (df(x_neg) ./ x(x_neg)) .* ... (nctcdf (x(x_neg) .* sqrt ((df(x_neg) + 2) ./ df(x_neg)), ... df(x_neg) + 2, mu(x_neg)) - ... nctcdf (x(x_neg), df(x_neg), mu(x_neg))); endif ## For positive x reflect about zero and use left tail cdf x_pos = find ((x > 0) & isfinite (x) & df <= 1e6 & ! is_nan & ! invalid); if (any (x_pos)) y(x_pos) = (-df(x_pos) ./ x(x_pos)) .* ... (nctcdf (-x(x_pos) .* sqrt ((df(x_pos) + 2) ./ df(x_pos)), ... df(x_pos) + 2, -mu(x_pos)) - ... nctcdf (-x(x_pos), df(x_pos), -mu(x_pos))); endif ## For x == 0 use power series xzero = find ((x == 0) & df <= 1e6 & ! is_nan & ! invalid); if (any (xzero)) y(xzero) = exp (-0.5 * mu(xzero) .^ 2 - 0.5 * log (pi * df(xzero)) + ... gammaln (0.5 * (df(xzero) + 1)) - gammaln (0.5 * df(xzero))); endif endfunction %!demo %! ## Plot various PDFs from the noncentral T distribution %! x = -5:0.01:10; %! y1 = nctpdf (x, 1, 0); %! y2 = nctpdf (x, 4, 0); %! y3 = nctpdf (x, 1, 2); %! y4 = nctpdf (x, 4, 2); %! plot (x, y1, "-r", x, y2, "-g", x, y3, "-k", x, y4, "-m") %! grid on %! xlim ([-5, 10]) %! ylim ([0, 0.4]) %! legend ({"df = 1, μ = 0", "df = 4, μ = 0", ... %! "df = 1, μ = 2", "df = 4, μ = 2"}, "location", "northeast") %! title ("Noncentral T PDF") %! xlabel ("values in x") %! ylabel ("density") %!demo %! ## Compare the noncentral T PDF with MU = 1 to the T PDF %! ## with the same number of degrees of freedom (10). %! %! x = -5:0.1:5; %! y1 = nctpdf (x, 10, 1); %! y2 = tpdf (x, 10); %! plot (x, y1, "-", x, y2, "-"); %! grid on %! xlim ([-5, 5]) %! ylim ([0, 0.4]) %! legend ({"Noncentral χ^2(4,2)", "χ^2(4)"}, "location", "northwest") %! title ("Noncentral T vs T PDFs") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x1, df, mu %! x1 = [-Inf, 2, NaN, 4, Inf]; %! df = [2, 0, -1, 1, 4]; %! mu = [1, NaN, 3, -1, 2]; %!assert (nctpdf (x1, df, mu), [0, NaN, NaN, 0.00401787561306999, 0], 1e-14); %!assert (nctpdf (x1, df, 1), [0, NaN, NaN, 0.0482312135423008, 0], 1e-14); %!assert (nctpdf (x1, df, 3), [0, NaN, NaN, 0.1048493126401585, 0], 1e-14); %!assert (nctpdf (x1, df, 2), [0, NaN, NaN, 0.08137377919890307, 0], 1e-14); %!assert (nctpdf (x1, 3, mu), [0, NaN, NaN, 0.001185305171654381, 0], 1e-14); %!assert (nctpdf (2, df, mu), [0.1791097459405861, NaN, NaN, ... %! 0.0146500727180389, 0.3082302682110299], 1e-14); %!assert (nctpdf (4, df, mu), [0.04467929612254971, NaN, NaN, ... %! 0.00401787561306999, 0.0972086534042828], 1e-14); ## Test input validation %!error nctpdf () %!error nctpdf (1) %!error nctpdf (1, 2) %!error ... %! nctpdf (ones (3), ones (2), ones (2)) %!error ... %! nctpdf (ones (2), ones (3), ones (2)) %!error ... %! nctpdf (ones (2), ones (2), ones (3)) %!error nctpdf (i, 2, 2) %!error nctpdf (2, i, 2) %!error nctpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/nctrnd.m000066400000000000000000000151231475240274700216060ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} nctrnd (@var{df}, @var{mu}) ## @deftypefnx {statistics} {@var{r} =} nctrnd (@var{df}, @var{mu}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} nctrnd (@var{df}, @var{mu}, [@var{sz}]) ## ## Random arrays from the noncentral @math{t}-distribution. ## ## @code{@var{x} = nctrnd (@var{p}, @var{df}, @var{mu})} returns an array of ## random numbers chosen from the noncentral @math{t}-distribution with @var{df} ## degrees of freedom and noncentrality parameter @var{mu}. The size of ## @var{r} is the common size of @var{df} and @var{mu}. A scalar input ## functions as a constant matrix of the same size as the other input. ## ## @code{nctrnd} generates values using the definition of a noncentral @math{t} ## random variable, as the ratio of a normal distribution with non-zero mean and ## the sqrt of a chi-squared distribution. ## ## When called with a single size argument, @code{nctrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the noncentral @math{t}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_t-distribution} ## ## @seealso{nctcdf, nctinv, nctpdf, nctstat, trnd, normrnd, chi2rnd} ## @end deftypefn function r = nctrnd (df, mu, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("nctrnd: function called with too few input arguments."); endif ## Check for common size of DF and MU if (! isscalar (df) || ! isscalar (mu)) [retval, df, mu] = common_size (df, mu); if (retval > 0) error ("nctrnd: DF and MU must be of common size or scalars."); endif endif ## Check for DF and MU being reals if (iscomplex (df) || iscomplex (mu)) error ("nctrnd: DF and MU must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (df); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["nctrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("nctrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (df) && ! isequal (size (df), sz)) error ("nctrnd: DF and MU must be scalars or of size SZ."); endif ## Check for class type if (isa (df, "single") || isa (mu, "single")); cls = "single"; else cls = "double"; endif ## Return NaNs for out of range values of DF df(df <= 0) = NaN; ## Prevent Inf/Inf==NaN for the standardized chi-square in the denom. df(isinf (df)) = realmax; ## Generate random sample from noncentral F distribution r = (randn (sz) + mu) ./ sqrt (2 .* randg (df ./ 2, sz) ./ df); ## Cast to appropriate class r = cast (r, cls); endfunction ## Test output %!assert (size (nctrnd (1, 1)), [1 1]) %!assert (size (nctrnd (1, ones (2,1))), [2, 1]) %!assert (size (nctrnd (1, ones (2,2))), [2, 2]) %!assert (size (nctrnd (ones (2,1), 1)), [2, 1]) %!assert (size (nctrnd (ones (2,2), 1)), [2, 2]) %!assert (size (nctrnd (1, 1, 3)), [3, 3]) %!assert (size (nctrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (nctrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (nctrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (nctrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (nctrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (nctrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (nctrnd (1, 1)), "double") %!assert (class (nctrnd (1, single (1))), "single") %!assert (class (nctrnd (1, single ([1, 1]))), "single") %!assert (class (nctrnd (single (1), 1)), "single") %!assert (class (nctrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error nctrnd () %!error nctrnd (1) %!error ... %! nctrnd (ones (3), ones (2)) %!error ... %! nctrnd (ones (2), ones (3)) %!error nctrnd (i, 2) %!error nctrnd (1, i) %!error ... %! nctrnd (1, 2, -1) %!error ... %! nctrnd (1, 2, 1.2) %!error ... %! nctrnd (1, 2, ones (2)) %!error ... %! nctrnd (1, 2, [2 -1 2]) %!error ... %! nctrnd (1, 2, [2 0 2.5]) %!error ... %! nctrnd (1, 2, 2, -1, 5) %!error ... %! nctrnd (1, 2, 2, 1.5, 5) %!error ... %! nctrnd (2, ones (2), 3) %!error ... %! nctrnd (2, ones (2), [3, 2]) %!error ... %! nctrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/ncx2cdf.m000066400000000000000000000232561475240274700216530ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} ncx2cdf (@var{x}, @var{df}, @var{lambda}) ## @deftypefnx {statistics} {@var{p} =} ncx2cdf (@var{x}, @var{df}, @var{lambda}, @qcode{"upper"}) ## ## Noncentral chi-squared cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the noncentral chi-squared distribution with @var{df} degrees of ## freedom and noncentrality parameter @var{lambda}. The size of @var{p} is the ## common size of @var{x}, @var{df}, and @var{lambda}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## @code{@var{p} = ncx2cdf (@var{x}, @var{df}, @var{lambda}, "upper")} computes ## the upper tail probability of the noncentral chi-squared distribution with ## parameters @var{df} and @var{lambda}, at the values in @var{x}. ## ## Further information about the noncentral chi-squared distribution can be ## found at @url{https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution} ## ## @seealso{ncx2inv, ncx2pdf, ncx2rnd, ncx2stat, chi2cdf} ## @end deftypefn function p = ncx2cdf (x, df, lambda, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("ncx2cdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("ncx2cdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, DF, and LAMBDA [err, x, df, lambda] = common_size (x, df, lambda); if (err > 0) error ("ncx2cdf: X, DF, and LAMBDA must be of common size or scalars."); endif ## Check for X, DF, and LAMBDA being reals if (iscomplex (x) || iscomplex (df) || iscomplex (lambda)) error ("ncx2cdf: X, DF, and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df, "single") || isa (lambda, "single")) p = zeros (size (x), "single"); c_eps = eps ("single"); c_min = realmin ("single"); else p = zeros (size (x)); c_eps = eps; c_min = realmin; endif ## Find NaNs in input arguments (if any) and propagate them to p is_nan = isnan (x) | isnan (df) | isnan (lambda); p(is_nan) = NaN; if (uflag) p(x == Inf & ! is_nan) = 0; p(x <= 0 & ! is_nan) = 1; else p(x == Inf & ! is_nan) = 1; endif ## Make P = NaN for negative values of noncentrality parameter and DF p(lambda < 0) = NaN; p(df < 0) = NaN; ## For DF == 0 at x == 0 k = df == 0 & x == 0 & lambda >= 0 & ! is_nan; if (uflag) p(k) = -expm1 (-lambda(k) / 2); else p(k) = exp (-lambda(k) / 2); endif ## Central chi2cdf k = df >= 0 & x > 0 & lambda == 0 & isfinite (x) & ! is_nan; if (uflag) p(k) = chi2cdf (x(k), df(k), "upper"); else p(k) = chi2cdf (x(k), df(k)); endif ## Keep only valid samples td = find (df >= 0 & x > 0 & lambda > 0 & isfinite (x) & ! is_nan); lambda = lambda(td) / 2; df = df(td) / 2; x = x(td) / 2; ## Compute Chernoff bounds e0 = log(c_min); e1 = log(c_eps/4); t = 1 - (df + sqrt (df .^ 2 + 4 * lambda .* x)) ./ (2 * x); q = lambda .* t ./ (1 - t) - df .* log(1 - t) - t .* x; peq0 = x < lambda + df & q < e0; peq1 = x > lambda + df & q < e1; if (uflag) p(td(peq0)) = 1; else p(td(peq1)) = 1; endif td(peq0 | peq1) = []; x(peq0 | peq1) = []; df(peq0 | peq1) = []; lambda(peq0 | peq1) = []; ## Find index K of the maximal term in the summation series. ## K1 and K2 are lower and upper bounds for K, respectively. ## Indexing of terms in the summation series starts at 0. K1 = ceil ((sqrt ((df + x) .^ 2 + 4 * x .* lambda) - (df + x)) / 2); K = zeros (size (x)); k1above1 = find (K1 > 1); K2 = floor (lambda(k1above1) .* gammaincratio (x(k1above1), K1(k1above1))); fixK2 = isnan(K2) | isinf(K2); K2(fixK2) = K1(k1above1(fixK2)); K(k1above1) = K2; ## Find Poisson and Poisson*chi2cdf parts for the maximal terms in the ## summation series. if (uflag) k0 = (K==0 & df==0); K(k0) = 1; endif pois = poisspdf (K, lambda); if (uflag) full = pois .* gammainc (x, df + K, "upper"); else full = pois .* gammainc (x, df + K); endif ## Sum the series. First go downward from K and then go upward. ## The term for K is added afterwards - it is not included in either sum. sumK = zeros (size (x)); ## Downward. poisspdf(k-1,lambda)/poisspdf(k,lambda) = k/lambda poisterm = pois; fullterm = full; keep = K > 0 & fullterm > 0; k = K; while any(keep) poisterm(keep) = poisterm(keep) .* k(keep) ./ lambda(keep); k(keep) = k(keep) - 1; if (uflag) fullterm(keep) = poisterm(keep) .* ... gammainc (x(keep), df(keep) + k(keep), "upper"); else fullterm(keep) = poisterm(keep) .* ... gammainc (x(keep), df(keep) + k(keep)); endif sumK(keep) = sumK(keep) + fullterm(keep); keep = keep & k > 0 & fullterm > eps(sumK); endwhile ## Upward. poisspdf(k+1,lambda)/poisspdf(k,lambda) = lambda/(k+1) poisterm = pois; fullterm = full; keep = fullterm > 0; k = K; while any(keep) k(keep) = k(keep)+1; poisterm(keep) = poisterm(keep) .* lambda(keep) ./ k(keep); if (uflag) fullterm(keep) = poisterm(keep) .* ... gammainc (x(keep), df(keep) + k(keep), "upper"); else fullterm(keep) = poisterm(keep) .* ... gammainc (x(keep), df(keep) + k(keep)); end sumK(keep) = sumK(keep) + fullterm(keep); keep = keep & fullterm > eps(sumK); endwhile ## Get probabilities p(td) = full + sumK; p(p > 1) = 1; endfunction ## Ratio of incomplete gamma function values at S and S-1. function r = gammaincratio (x, s) ## Initialize r = zeros (size (s)); ## Finf small small = s < 2 | s <= x; ## For small S, use the ratio computed directly if (any (small(:))) r(small) = gammainc (x(small), s(small)) ./ ... gammainc (x(small), s(small) - 1); endif ## For large S, estimate numerator and denominator using 'scaledlower' option if (any (! small(:))) idx = find (! small); x = x(idx); s = s(idx); r(idx) = gammainc (x, s, "scaledlower") ./ ... gammainc (x, s - 1, "scaledlower") .* x ./ s; endif endfunction %!demo %! ## Plot various CDFs from the noncentral chi-squared distribution %! x = 0:0.1:10; %! p1 = ncx2cdf (x, 2, 1); %! p2 = ncx2cdf (x, 2, 2); %! p3 = ncx2cdf (x, 2, 3); %! p4 = ncx2cdf (x, 4, 1); %! p5 = ncx2cdf (x, 4, 2); %! p6 = ncx2cdf (x, 4, 3); %! plot (x, p1, "-r", x, p2, "-g", x, p3, "-k", ... %! x, p4, "-m", x, p5, "-c", x, p6, "-y") %! grid on %! xlim ([0, 10]) %! legend ({"df = 2, λ = 1", "df = 2, λ = 2", ... %! "df = 2, λ = 3", "df = 4, λ = 1", ... %! "df = 4, λ = 2", "df = 4, λ = 3"}, "location", "southeast") %! title ("Noncentral chi-squared CDF") %! xlabel ("values in x") %! ylabel ("probability") %!demo %! ## Compare the noncentral chi-squared CDF with LAMBDA = 2 to the %! ## chi-squared CDF with the same number of degrees of freedom (4). %! %! x = 0:0.1:10; %! p1 = ncx2cdf (x, 4, 2); %! p2 = chi2cdf (x, 4); %! plot (x, p1, "-", x, p2, "-") %! grid on %! xlim ([0, 10]) %! legend ({"Noncentral χ^2(4,2)", "χ^2(4)"}, "location", "northwest") %! title ("Noncentral chi-squared vs chi-squared CDFs") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!test %! x = -2:0.1:2; %! p = ncx2cdf (x, 10, 1); %! assert (p([1:21]), zeros (1, 21), 3e-84); %! assert (p(22), 1.521400636466575e-09, 1e-14); %! assert (p(30), 6.665480510026046e-05, 1e-14); %! assert (p(41), 0.002406447308399836, 1e-14); %!test %! p = ncx2cdf (12, 10, 3); %! assert (p, 0.4845555602398649, 1e-14); %!test %! p = ncx2cdf (2, 3, 2); %! assert (p, 0.2207330870741212, 1e-14); %!test %! p = ncx2cdf (2, 3, 2, "upper"); %! assert (p, 0.7792669129258789, 1e-14); %!test %! p = ncx2cdf ([3, 6], 3, 2, "upper"); %! assert (p, [0.6423318186400054, 0.3152299878943012], 1e-14); ## Test input validation %!error ncx2cdf () %!error ncx2cdf (1) %!error ncx2cdf (1, 2) %!error ncx2cdf (1, 2, 3, "tail") %!error ncx2cdf (1, 2, 3, 4) %!error ... %! ncx2cdf (ones (3), ones (2), ones (2)) %!error ... %! ncx2cdf (ones (2), ones (3), ones (2)) %!error ... %! ncx2cdf (ones (2), ones (2), ones (3)) %!error ncx2cdf (i, 2, 2) %!error ncx2cdf (2, i, 2) %!error ncx2cdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/ncx2inv.m000066400000000000000000000161671475240274700217160ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} ncx2inv (@var{p}, @var{df}, @var{lambda}) ## ## Inverse of the noncentral chi-squared cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the noncentral chi-squared distribution with @var{df} degrees of freedom and ## noncentrality parameter @var{mu}. The size of @var{x} is the common size of ## @var{p}, @var{df}, and @var{mu}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## @code{ncx2inv} uses Newton's method to converge to the solution. ## ## Further information about the noncentral chi-squared distribution can be ## found at @url{https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution} ## ## @seealso{ncx2cdf, ncx2pdf, ncx2rnd, ncx2stat, chi2inv} ## @end deftypefn function x = ncx2inv (p, df, lambda) ## Check for valid number of input arguments if (nargin < 3) error ("ncx2inv: function called with too few input arguments."); endif ## Check for common size of P, DF, and LAMBDA [err, p, df, lambda] = common_size (p, df, lambda); if (err > 0) error ("ncx2inv: P, DF, and LAMBDA must be of common size or scalars."); endif ## Check for P, DF, and LAMBDA being reals if (iscomplex (p) || iscomplex (df) || iscomplex (lambda)) error ("ncx2inv: P, DF, and LAMBDA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (df, "single") || isa (lambda, "single")) x = NaN (size (p), "single"); crit = sqrt (eps ("single")); else x = NaN (size (p), "double"); crit = sqrt (eps ("double")); endif ## For lambda == 0, call chi2inv d0 = lambda == 0; if (any (d0(:))) x(d0) = chi2inv (p(d0), df(d0)); ## If lambda == 0 for all entries, then return if (all (d0(:))) return; endif endif ## CDF with 0 d.d0. has a step at x=0. ## Check if CDF at x=0 exceeds the requested p. df0 = df==0 & lambda > 0; if (any (df0(:))) p0 = zeros (size (p)); p0(df0) = ncx2cdf (0, df(df0), lambda(df0)); df0 = df0 & p0 >= p; x(df0) = 0; endif valid = ! df0 & df > 0 & lambda > 0; ## Force x = 0 for p == 0 and x = Inf for p == 1 x(p == 0 & valid) = 0; x(p == 1 & valid) = Inf; ## Find valid samples within the range of 0 < p < 1 k = find (p > 0 & p < 1 & valid); pk = p(k); ## Initialize counter count_limit = 100; count = 0; ## Supply a starting guess for the iteration. mn = df(k) + lambda(k); variance = 2 * (df(k) + 2 * lambda(k)); temp = log (variance + mn .^ 2); mu = 2 * log (mn) - 0.5 * temp; sigma = -2 * log (mn) + temp; xk = exp (norminv (pk, mu, sigma)); F = ncx2cdf (xk, df(k), lambda(k)); h = ones(size(xk), class (xk)); ## Start iteration with a break out loop while (count < count_limit) count = count + 1; h = (F - pk) ./ ncx2pdf (xk, df(k), lambda(k)); xnew = max (xk / 50, min (5 * xk, xk - h)); newF = ncx2cdf (xnew, df(k), lambda(k)); while (true) worse = (abs (newF - pk) > abs (F - pk) * (1 + crit)) & ... (abs (xk - xnew) > crit * xk); if (! any (worse)) break; endif xnew(worse) = 0.5 * (xnew(worse) + xk(worse)); newF(worse) = ncx2cdf (xnew(worse), df(k(worse)), lambda(k(worse))); endwhile h = xk - xnew; x(k) = xnew; mask = (abs (h) > crit * abs (xk)); if (! any (mask)) break; endif k = k(mask); xk = xnew(mask); F = newF(mask); pk = pk(mask); endwhile if (count == count_limit) warning ("ncx2inv: did not converge."); endif endfunction %!demo %! ## Plot various iCDFs from the noncentral chi-squared distribution %! p = 0.001:0.001:0.999; %! x1 = ncx2inv (p, 2, 1); %! x2 = ncx2inv (p, 2, 2); %! x3 = ncx2inv (p, 2, 3); %! x4 = ncx2inv (p, 4, 1); %! x5 = ncx2inv (p, 4, 2); %! x6 = ncx2inv (p, 4, 3); %! plot (p, x1, "-r", p, x2, "-g", p, x3, "-k", ... %! p, x4, "-m", p, x5, "-c", p, x6, "-y") %! grid on %! ylim ([0, 10]) %! legend ({"df = 2, λ = 1", "df = 2, λ = 2", ... %! "df = 2, λ = 3", "df = 4, λ = 1", ... %! "df = 4, λ = 2", "df = 4, λ = 3"}, "location", "northwest") %! title ("Noncentral chi-squared iCDF") %! xlabel ("probability") %! ylabel ("values in x") %!demo %! ## Compare the noncentral chi-squared CDF with LAMBDA = 2 to the %! ## chi-squared CDF with the same number of degrees of freedom (4). %! %! p = 0.001:0.001:0.999; %! x1 = ncx2inv (p, 4, 2); %! x2 = chi2inv (p, 4); %! plot (p, x1, "-", p, x2, "-"); %! grid on %! ylim ([0, 10]) %! legend ({"Noncentral χ^2(4,2)", "χ^2(4)"}, "location", "northwest") %! title ("Noncentral chi-squared vs chi-squared quantile functions") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!test %! x = [0,0.3443,0.7226,1.1440,1.6220,2.1770,2.8436,3.6854,4.8447,6.7701,Inf]; %! assert (ncx2inv ([0:0.1:1], 2, 1), x, 1e-4); %!test %! x = [0,0.8295,1.6001,2.3708,3.1785,4.0598,5.0644,6.2765,7.8763,10.4199,Inf]; %! assert (ncx2inv ([0:0.1:1], 2, 3), x, 1e-4); %!test %! x = [0,0.5417,1.3483,2.1796,3.0516,4.0003,5.0777,6.3726,8.0748,10.7686,Inf]; %! assert (ncx2inv ([0:0.1:1], 1, 4), x, 1e-4); %!test %! x = [0.1808, 0.6456, 1.1842, 1.7650, 2.3760, 3.0105]; %! assert (ncx2inv (0.05, [1, 2, 3, 4, 5, 6], 4), x, 1e-4); %!test %! x = [0.4887, 0.6699, 0.9012, 1.1842, 1.5164, 1.8927]; %! assert (ncx2inv (0.05, 3, [1, 2, 3, 4, 5, 6]), x, 1e-4); %!test %! x = [1.3941, 1.6824, 2.0103, 2.3760, NaN, 3.2087]; %! assert (ncx2inv (0.05, 5, [1, 2, 3, 4, -1, 6]), x, 1e-4); %!test %! assert (ncx2inv (0.996, 5, 8), 35.51298862765576, 3e-13); ## Test input validation %!error ncx2inv () %!error ncx2inv (1) %!error ncx2inv (1, 2) %!error ... %! ncx2inv (ones (3), ones (2), ones (2)) %!error ... %! ncx2inv (ones (2), ones (3), ones (2)) %!error ... %! ncx2inv (ones (2), ones (2), ones (3)) %!error ncx2inv (i, 2, 2) %!error ncx2inv (2, i, 2) %!error ncx2inv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/ncx2pdf.m000066400000000000000000000303201475240274700216560ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} ncx2pdf (@var{x}, @var{df}, @var{lambda}) ## ## Noncentral chi-squared probability distribution function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the noncentral chi-squared distribution with @var{df} degrees of freedom ## and noncentrality parameter @var{lambda}. The size of @var{y} is the common ## size of @var{x}, @var{df}, and @var{lambda}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## Further information about the noncentral chi-squared distribution can be ## found at @url{https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution} ## ## @seealso{ncx2cdf, ncx2inv, ncx2rnd, ncx2stat, chi2pdf} ## @end deftypefn function y = ncx2pdf (x, df, lambda) ## Check for valid number of input arguments if (nargin < 3) error ("ncx2pdf: function called with too few input arguments."); endif ## Check for common size of X, DF, and LAMBDA [err, x, df, lambda] = common_size (x, df, lambda); if (err > 0) error ("ncx2pdf: X, DF, and LAMBDA must be of common size or scalars."); endif ## Check for X, DF, and LAMBDA being reals if (iscomplex (x) || iscomplex (df) || iscomplex (lambda)) error ("ncx2pdf: X, DF, and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df, "single") || isa (lambda, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Find NaNs in input arguments (if any) and propagate them to p is_nan = isnan (x) | isnan (df) | isnan (lambda); y(is_nan) = NaN; ## Make input arguments column vectors and half DF x = x(:); df = df(:); df = df / 2; lambda = lambda(:); ## Handle special cases k1 = x == 0 & df == 1; y(k1) = 0.5 * exp (-0.5 * lambda(k1)); k2 = x == 0 & df < 1; y(k2) = Inf; y(lambda < 0) = NaN; y(df < 0) = NaN; k3 = lambda == 0 & df > 0; y(k3) = gampdf (x(k3), df(k3), 2); ## Handle normal cases td = find(x>0 & x0 & df>=0); ## Return if finished all normal cases if (isempty (td)) return; endif ## Reset input variables to remaining cases x = x(td); lambda = lambda(td); df = df(td) - 1; x_sqrt = sqrt (x); d_sqrt = sqrt (lambda); ## Upper Limit on density small_DF = df <= -0.5; large_DF = ! small_DF; ul = zeros (size (x)); ul(small_DF) = -0.5 * (lambda(small_DF) + x(small_DF)) + ... 0.5 * x_sqrt(small_DF) .* d_sqrt (small_DF) ./ ... (df(small_DF) + 1) + df(small_DF) .* ... (log (x(small_DF)) - log (2)) - log (2) - ... gammaln (df(small_DF) + 1); ul(large_DF) = -0.5 * (d_sqrt(large_DF) - x_sqrt(large_DF)) .^ 2 + ... df(large_DF) .* (log (x(large_DF)) - log (2)) - log (2) - ... gammaln (df(large_DF) + 1) + (df(large_DF) + 0.5) .* ... log ((df(large_DF) + 0.5) ./ (x_sqrt(large_DF) .* ... d_sqrt(large_DF) + df(large_DF) + 0.5)); ULunderflow = ul < log (realmin); y(td(ULunderflow)) = 0; td(ULunderflow) = []; ## Return if finished all normal cases if (isempty (td)) return; endif x(ULunderflow) = []; lambda(ULunderflow) = []; df(ULunderflow) = []; x_sqrt(ULunderflow) = []; d_sqrt(ULunderflow) = []; ## Try the scaled Bess function scaleB = besseli (df, d_sqrt .* x_sqrt, 1); use_SB = scaleB > 0 & scaleB < Inf; y(td(use_SB)) = exp (-log (2) -0.5 * (x_sqrt(use_SB) - ... d_sqrt(use_SB)) .^ 2 + df(use_SB) .* ... log (x_sqrt(use_SB) ./ d_sqrt(use_SB))) .* scaleB(use_SB); td(use_SB) = []; ## Return if finished all normal cases if (isempty (td)) return; endif x(use_SB) = []; lambda(use_SB) = []; df(use_SB) = []; x_sqrt(use_SB) = []; d_sqrt(use_SB) = []; ## Try the Bess function Bess = besseli (df, d_sqrt .* x_sqrt); useB = Bess > 0 & Bess < Inf; y(td(useB)) = exp (-log (2) - 0.5 * (x(useB) + lambda(useB)) + ... df(useB) .* log (x_sqrt(useB) ./ d_sqrt(useB))) .* Bess(useB); td(useB) = []; ## Return if finished all normal cases if isempty(td) return; endif x(useB) = []; lambda(useB) = []; df(useB) = []; ## If neither Bess function works, use recursion. When non-centrality ## parameter is very large, the initial values of the Poisson numbers used ## in the approximation are very small, smaller than epsilon. This would ## cause premature convergence. To avoid that, we start from the peak of the ## Poisson numbers, and go in both directions. lnsr2pi = 0.9189385332046727; % log(sqrt(2*pi)) dx = lambda .* x / 4; K = max (0, floor (0.5 * (sqrt (df .^ 2 + 4 * dx) - df))); lntK = zeros(size(K)); K0 = K == 0; lntK(K0) = -lnsr2pi -0.5 * (lambda(K0) + log(df(K0))) - ... StirlingError (df(K0)) - BinoPoisson (df(K0), x(K0) / 2); K0 = ! K0; lntK(K0) = -2 * lnsr2pi - 0.5 * (log (K(K0)) + log (df(K0) + K(K0))) - ... StirlingError (K(K0)) - StirlingError (df(K0) + K(K0)) - ... BinoPoisson (K(K0), lambda(K0) / 2) - ... BinoPoisson (df(K0) + K(K0), x(K0) / 2); sumK = ones (size (K)); keep = K>0; term = ones (size (K)); k = K; while (any (keep)) term(keep) = term(keep) .* (df(keep) + k(keep)) .* k(keep) ./ dx(keep); sumK(keep) = sumK(keep) + term(keep); keep = keep & k > 0 & term > eps (sumK); k = k - 1; endwhile keep = true (size (K)); term = ones (size (K)); k = K + 1; while (any (keep)) term(keep) = term(keep) ./ (df(keep) + k(keep)) ./ k(keep) .* dx(keep); sumK(keep) = sumK(keep) + term(keep); keep = keep & term > eps (sumK); k = k + 1; end y(td) = 0.5 * exp (lntK + log (sumK)); endfunction ## Error of Stirling-De Moivre approximation to n factorial. function lambda = StirlingError (n) is_class = class (n); lambda = zeros (size (n), is_class); nn = n .* n; ## Define S0=1/12 S1=1/360 S2=1/1260 S3=1/1680 S4=1/1188 S0 = 8.333333333333333e-02; S1 = 2.777777777777778e-03; S2 = 7.936507936507937e-04; S3 = 5.952380952380952e-04; S4 = 8.417508417508418e-04; ## Define lambda(n) for n<0:0.5:15 sfe=[ 0; 1.534264097200273e-01;... 8.106146679532726e-02; 5.481412105191765e-02;... 4.134069595540929e-02; 3.316287351993629e-02;... 2.767792568499834e-02; 2.374616365629750e-02;... 2.079067210376509e-02; 1.848845053267319e-02;... 1.664469118982119e-02; 1.513497322191738e-02;... 1.387612882307075e-02; 1.281046524292023e-02;... 1.189670994589177e-02; 1.110455975820868e-02;... 1.041126526197210e-02; 9.799416126158803e-03;... 9.255462182712733e-03; 8.768700134139385e-03;... 8.330563433362871e-03; 7.934114564314021e-03;... 7.573675487951841e-03; 7.244554301320383e-03;... 6.942840107209530e-03; 6.665247032707682e-03;... 6.408994188004207e-03; 6.171712263039458e-03;... 5.951370112758848e-03; 5.746216513010116e-03;... 5.554733551962801e-03]; k = find (n <= 15); if (any (k)) n1 = n(k); n2 = 2 * n1; if (all (n2 == round (n2))) lambda(k) = sfe(n2+1); else lnsr2pi = 0.9189385332046728; lambda(k) = gammaln(n1+1)-(n1+0.5).*log(n1)+n1-lnsr2pi; endif endif k = find (n > 15 & n <= 35); if (any (k)) lambda(k) = (S0 - (S1 - (S2 - (S3 - S4 ./ nn(k)) ./ nn(k)) ./ ... nn(k)) ./ nn(k)) ./ n(k); endif k = find (n > 35 & n <= 80); if (any (k)) lambda(k) = (S0 - (S1 - (S2 - S3 ./ nn(k)) ./ nn(k)) ./ nn(k)) ./ n(k); endif k = find(n > 80 & n <= 500); if (any (k)) lambda(k) = (S0 - (S1 - S2 ./ nn(k)) ./ nn(k)) ./ n(k); endif k = find(n > 500); if (any (k)) lambda(k) = (S0 - S1 ./ nn(k)) ./ n(k); endif endfunction ## Deviance term for binomial and Poisson probability calculation. function BP = BinoPoisson (x, np) if (isa (x,'single') || isa (np,'single')) BP = zeros (size (x), "single"); else BP = zeros (size (x)); endif k = abs (x - np) < 0.1 * (x + np); if any(k(:)) s = (x(k) - np(k)) .* (x(k) - np(k)) ./ (x(k) + np(k)); v = (x(k) - np(k)) ./ (x(k) + np(k)); ej = 2 .* x(k) .* v; is_class = class (s); s1 = zeros (size (s), is_class); ok = true (size (s)); j = 0; while any(ok(:)) ej(ok) = ej(ok) .* v(ok) .* v(ok); j = j + 1; s1(ok) = s(ok) + ej(ok) ./ (2 * j + 1); ok = ok & s1 != s; s(ok) = s1(ok); endwhile BP(k) = s; endif k = ! k; if (any (k(:))) BP(k) = x(k) .* log (x(k) ./ np(k)) + np(k) - x(k); endif endfunction %!demo %! ## Plot various PDFs from the noncentral chi-squared distribution %! x = 0:0.1:10; %! y1 = ncx2pdf (x, 2, 1); %! y2 = ncx2pdf (x, 2, 2); %! y3 = ncx2pdf (x, 2, 3); %! y4 = ncx2pdf (x, 4, 1); %! y5 = ncx2pdf (x, 4, 2); %! y6 = ncx2pdf (x, 4, 3); %! plot (x, y1, "-r", x, y2, "-g", x, y3, "-k", ... %! x, y4, "-m", x, y5, "-c", x, y6, "-y") %! grid on %! xlim ([0, 10]) %! ylim ([0, 0.32]) %! legend ({"df = 2, λ = 1", "df = 2, λ = 2", ... %! "df = 2, λ = 3", "df = 4, λ = 1", ... %! "df = 4, λ = 2", "df = 4, λ = 3"}, "location", "northeast") %! title ("Noncentral chi-squared PDF") %! xlabel ("values in x") %! ylabel ("density") %!demo %! ## Compare the noncentral chi-squared PDF with LAMBDA = 2 to the %! ## chi-squared PDF with the same number of degrees of freedom (4). %! %! x = 0:0.1:10; %! y1 = ncx2pdf (x, 4, 2); %! y2 = chi2pdf (x, 4); %! plot (x, y1, "-", x, y2, "-"); %! grid on %! xlim ([0, 10]) %! ylim ([0, 0.32]) %! legend ({"Noncentral T(10,1)", "T(10)"}, "location", "northwest") %! title ("Noncentral chi-squared vs chi-squared PDFs") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x1, df, d1 %! x1 = [-Inf, 2, NaN, 4, Inf]; %! df = [2, 0, -1, 1, 4]; %! d1 = [1, NaN, 3, -1, 2]; %!assert (ncx2pdf (x1, df, d1), [0, NaN, NaN, NaN, 0]); %!assert (ncx2pdf (x1, df, 1), [0, 0.07093996461786045, NaN, ... %! 0.06160064323277038, 0], 1e-14); %!assert (ncx2pdf (x1, df, 3), [0, 0.1208364909271113, NaN, ... %! 0.09631299762429098, 0], 1e-14); %!assert (ncx2pdf (x1, df, 2), [0, 0.1076346446244688, NaN, ... %! 0.08430464047296625, 0], 1e-14); %!assert (ncx2pdf (x1, 2, d1), [0, NaN, NaN, NaN, 0]); %!assert (ncx2pdf (2, df, d1), [0.1747201674611283, NaN, NaN, ... %! NaN, 0.1076346446244688], 1e-14); %!assert (ncx2pdf (4, df, d1), [0.09355987820265799, NaN, NaN, ... %! NaN, 0.1192317192431485], 1e-14); ## Test input validation %!error ncx2pdf () %!error ncx2pdf (1) %!error ncx2pdf (1, 2) %!error ... %! ncx2pdf (ones (3), ones (2), ones (2)) %!error ... %! ncx2pdf (ones (2), ones (3), ones (2)) %!error ... %! ncx2pdf (ones (2), ones (2), ones (3)) %!error ncx2pdf (i, 2, 2) %!error ncx2pdf (2, i, 2) %!error ncx2pdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/ncx2rnd.m000066400000000000000000000153071475240274700217000ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} ncx2rnd (@var{df}, @var{lambda}) ## @deftypefnx {statistics} {@var{r} =} ncx2rnd (@var{df}, @var{lambda}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} ncx2rnd (@var{df}, @var{lambda}, [@var{sz}]) ## ## Random arrays from the noncentral chi-squared distribution. ## ## @code{@var{r} = ncx2rnd (@var{df}, @var{lambda})} returns an array of random ## numbers chosen from the noncentral chi-squared distribution with @var{df} ## degrees of freedom and noncentrality parameter @var{lambda}. The size of ## @var{r} is the common size of @var{df} and @var{lambda}. A scalar input ## functions as a constant matrix of the same size as the other input. ## ## When called with a single size argument, @code{ncx2rnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the noncentral chi-squared distribution can be ## found at @url{https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution} ## ## @seealso{ncx2cdf, ncx2inv, ncx2pdf, ncx2stat} ## @end deftypefn function r = ncx2rnd (df, lambda, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("ncx2rnd: function called with too few input arguments."); endif ## Check for common size of DF and LAMBDA if (! isscalar (df) || ! isscalar (lambda)) [retval, df, lambda] = common_size (df, lambda); if (retval > 0) error ("ncx2rnd: DF and LAMBDA must be of common size or scalars."); endif endif ## Check for DF and LAMBDA being reals if (iscomplex (df) || iscomplex (lambda)) error ("ncx2rnd: DF and LAMBDA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (df); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["ncx2rnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("ncx2rnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (df) && ! isequal (size (df), sz)) error ("ncx2rnd: DF and LAMBDA must be scalars or of size SZ."); endif ## Check for class type if (isa (df, "single") || isa (lambda, "single")); cls = "single"; else cls = "double"; endif ## Return NaNs for out of range values of DF and LAMBDA df(df <= 0) = NaN; lambda(lambda <= 0) = NaN; ## Force DF and LAMBDA into the same size as SZ (if necessary) if (isscalar (df)) df = repmat (df, sz); endif if (isscalar (lambda)) lambda = repmat (lambda, sz); endif ## Generate random sample from noncentral chi-squared distribution r = randp (lambda ./ 2); r(r > 0) = 2 * randg (r(r > 0)); r(df > 0) += 2 * randg (df(df > 0) / 2); ## Cast to appropriate class r = cast (r, cls); endfunction ## Test output %!assert (size (ncx2rnd (1, 1)), [1 1]) %!assert (size (ncx2rnd (1, ones (2,1))), [2, 1]) %!assert (size (ncx2rnd (1, ones (2,2))), [2, 2]) %!assert (size (ncx2rnd (ones (2,1), 1)), [2, 1]) %!assert (size (ncx2rnd (ones (2,2), 1)), [2, 2]) %!assert (size (ncx2rnd (1, 1, 3)), [3, 3]) %!assert (size (ncx2rnd (1, 1, [4, 1])), [4, 1]) %!assert (size (ncx2rnd (1, 1, 4, 1)), [4, 1]) %!assert (size (ncx2rnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (ncx2rnd (1, 1, 0, 1)), [0, 1]) %!assert (size (ncx2rnd (1, 1, 1, 0)), [1, 0]) %!assert (size (ncx2rnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (ncx2rnd (1, 1)), "double") %!assert (class (ncx2rnd (1, single (1))), "single") %!assert (class (ncx2rnd (1, single ([1, 1]))), "single") %!assert (class (ncx2rnd (single (1), 1)), "single") %!assert (class (ncx2rnd (single ([1, 1]), 1)), "single") ## Test input validation %!error ncx2rnd () %!error ncx2rnd (1) %!error ... %! ncx2rnd (ones (3), ones (2)) %!error ... %! ncx2rnd (ones (2), ones (3)) %!error ncx2rnd (i, 2) %!error ncx2rnd (1, i) %!error ... %! ncx2rnd (1, 2, -1) %!error ... %! ncx2rnd (1, 2, 1.2) %!error ... %! ncx2rnd (1, 2, ones (2)) %!error ... %! ncx2rnd (1, 2, [2 -1 2]) %!error ... %! ncx2rnd (1, 2, [2 0 2.5]) %!error ... %! ncx2rnd (1, 2, 2, -1, 5) %!error ... %! ncx2rnd (1, 2, 2, 1.5, 5) %!error ... %! ncx2rnd (2, ones (2), 3) %!error ... %! ncx2rnd (2, ones (2), [3, 2]) %!error ... %! ncx2rnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/normcdf.m000066400000000000000000000225331475240274700217510ustar00rootroot00000000000000## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} normcdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} normcdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{p} =} normcdf (@var{x}, @var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} normcdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} normcdf (@var{x}, @var{mu}, @var{sigma}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} normcdf (@var{x}, @var{mu}, @var{sigma}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} normcdf (@dots{}, @qcode{"upper"}) ## ## Normal cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the normal distribution with mean @var{mu} and standard deviation ## @var{sigma}. The size of @var{p} is the common size of @var{x}, @var{mu} and ## @var{sigma}. A scalar input functions as a constant matrix of the same size ## as the other inputs. ## ## Default values are @var{mu} = 0, @var{sigma} = 1. ## ## When called with three output arguments, i.e. @qcode{[@var{p}, @var{plo}, ## @var{pup}]}, @code{normcdf} computes the confidence bounds for @var{p} when ## the input parameters @var{mu} and @var{sigma} are estimates. In such case, ## @var{pcov}, a @math{2x2} matrix containing the covariance matrix of the ## estimated parameters, is necessary. Optionally, @var{alpha}, which has a ## default value of 0.05, specifies the @qcode{100 * (1 - @var{alpha})} percent ## confidence bounds. @var{plo} and @var{pup} are arrays of the same size as ## @var{p} containing the lower and upper confidence bounds. ## ## @code{[@dots{}] = normcdf (@dots{}, "upper")} computes the upper tail ## probability of the normal distribution with parameters @var{mu} and ## @var{sigma}, at the values in @var{x}. This can be used to compute a ## right-tailed p-value. To compute a two-tailed p-value, use ## @code{2 * normcdf (-abs (@var{x}), @var{mu}, @var{sigma})}. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{norminv, normpdf, normrnd, normfit, normlike, normstat} ## @end deftypefn function [varargout] = normcdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 6) error ("normcdf: invalid number of input arguments."); endif ## Check for "upper" flag if (nargin > 1 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 1 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("normcdf: invalid argument for upper tail."); else uflag = false; endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) mu = varargin{1}; else mu = 0; endif if (numel (varargin) > 1) sigma = varargin{2}; else sigma = 1; endif if (numel (varargin) > 2) pcov = varargin{3}; ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("normcdf: invalid size of covariance matrix."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("normcdf: covariance matrix is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 3) alpha = varargin{4}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("normcdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [err, x, mu, sigma] = common_size (x, mu, sigma); if (err > 0) error ("normcdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("normcdf: X, MU, and SIGMA must not be complex."); endif ## Compute normal CDF z = (x - mu) ./ sigma; if (uflag) z = -z; endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output p = NaN (size (z), is_class); if (nargout > 1) plo = NaN (size (z), is_class); pup = NaN (size (z), is_class); endif ## Check SIGMA if (isscalar (sigma)) if (sigma > 0) sigma_p = true (size (z)); sigma_z = false (size (z)); elseif (sigma == 0) sigma_z = true (size (z)); sigma_p = false (size (z)); else if (nargout <= 1) varargout{1} = p; elseif (nargout == 3) varargout{1} = p; varargout{2} = plo; varargout{3} = pup; endif return; endif else sigma_p = sigma > 0; sigma_z = sigma == 0; endif ## Set edge cases when SIGMA = 0 if (uflag) p(sigma_z & x < mu) = 1; p(sigma_z & x >= mu) = 0; if (nargout > 1) plo(sigma_z & x < mu) = 1; plo(sigma_z & x >= mu) = 0; pup(sigma_z & x < mu) = 1; pup(sigma_z & x >= mu) = 0; endif else p(sigma_z & x < mu) = 0; p(sigma_z & x >= mu) = 1; if (nargout >= 2) plo(sigma_z & x < mu) = 0; plo(sigma_z & x >= mu) = 1; pup(sigma_z & x < mu) = 0; pup(sigma_z & x >= mu) = 1; endif endif ## Compute cases when SIGMA > 0 p(sigma_p) = 0.5 * erfc (-z(sigma_p) ./ sqrt (2)); varargout{1} = p; ## Compute confidence bounds (if requested) if (nargout >= 2) zvar = (pcov(1,1) + 2 * pcov(1,2) * z(sigma_p) + ... pcov(2,2) * z(sigma_p) .^ 2) ./ (sigma .^ 2); if (any (zvar < 0)) error ("normcdf: bad covariance matrix."); endif normz = -norminv (alpha / 2); halfwidth = normz * sqrt (zvar); zlo = z(sigma_p) - halfwidth; zup = z(sigma_p) + halfwidth; plo(sigma_p) = 0.5 * erfc (-zlo ./ sqrt (2)); pup(sigma_p) = 0.5 * erfc (-zup ./ sqrt (2)); varargout{2} = plo; varargout{3} = pup; endif endfunction %!demo %! ## Plot various CDFs from the normal distribution %! x = -5:0.01:5; %! p1 = normcdf (x, 0, 0.5); %! p2 = normcdf (x, 0, 1); %! p3 = normcdf (x, 0, 2); %! p4 = normcdf (x, -2, 0.8); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! xlim ([-5, 5]) %! legend ({"μ = 0, σ = 0.5", "μ = 0, σ = 1", ... %! "μ = 0, σ = 2", "μ = -2, σ = 0.8"}, "location", "southeast") %! title ("Normal CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-Inf 1 2 Inf]; %! y = [0, 0.5, 1/2*(1+erf(1/sqrt(2))), 1]; %!assert (normcdf (x, ones (1,4), ones (1,4)), y) %!assert (normcdf (x, 1, ones (1,4)), y) %!assert (normcdf (x, ones (1,4), 1), y) %!assert (normcdf (x, [0, -Inf, NaN, Inf], 1), [0, 1, NaN, NaN]) %!assert (normcdf (x, 1, [Inf, NaN, -1, 0]), [NaN, NaN, NaN, 1]) %!assert (normcdf ([x(1:2), NaN, x(4)], 1, 1), [y(1:2), NaN, y(4)]) %!assert (normcdf (x, "upper"), [1, 0.1587, 0.0228, 0], 1e-4) ## Test class of input preserved %!assert (normcdf ([x, NaN], 1, 1), [y, NaN]) %!assert (normcdf (single ([x, NaN]), 1, 1), single ([y, NaN]), eps ("single")) %!assert (normcdf ([x, NaN], single (1), 1), single ([y, NaN]), eps ("single")) %!assert (normcdf ([x, NaN], 1, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error normcdf () %!error normcdf (1,2,3,4,5,6,7) %!error normcdf (1, 2, 3, 4, "uper") %!error ... %! normcdf (ones (3), ones (2), ones (2)) %!error normcdf (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = normcdf (1, 2, 3) %!error [p, plo, pup] = ... %! normcdf (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! normcdf (1, 2, 3, [1, 0; 0, 1], 1.22) %!error [p, plo, pup] = ... %! normcdf (1, 2, 3, [1, 0; 0, 1], "alpha", "upper") %!error normcdf (i, 2, 2) %!error normcdf (2, i, 2) %!error normcdf (2, 2, i) %!error ... %! [p, plo, pup] =normcdf (1, 2, 3, [1, 0; 0, -inf], 0.04) statistics-release-1.7.3/inst/dist_fun/norminv.m000066400000000000000000000122621475240274700220070ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} norminv (@var{p}) ## @deftypefnx {statistics} {@var{x} =} norminv (@var{p}, @var{mu}) ## @deftypefnx {statistics} {@var{x} =} norminv (@var{p}, @var{mu}, @var{sigma}) ## ## Inverse of the normal cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the normal distribution with mean @var{mu} and standard deviation @var{sigma}. ## The size of @var{p} is the common size of @var{p}, @var{mu} and @var{sigma}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## Default values are @var{mu} = 0, @var{sigma} = 1. ## ## The default values correspond to the standard normal distribution and ## computing its quantile function is also possible with the @code{probit} ## function, which is faster but it does not perform any input validation. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{norminv, normpdf, normrnd, normfit, normlike, normstat, probit} ## @end deftypefn function x = norminv (p, mu, sigma) ## Check for valid number of input arguments if (nargin < 1) error ("norminv: function called with too few input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif if (nargin < 3) sigma = 1; endif ## Check for common size of P, MU, and SIGMA if (! isscalar (p) || ! isscalar (mu) || ! isscalar (sigma)) [retval, p, mu, sigma] = common_size (p, mu, sigma); if (retval > 0) error ("norminv: P, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for P, MU, and SIGMA being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma)) error ("norminv: P, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Compute normal iCDF if (isscalar (mu) && isscalar (sigma)) if (isfinite (mu) && (sigma > 0) && (sigma < Inf)) x = mu + sigma * (-sqrt (2) * erfcinv (2 * p)); endif else k = isfinite (mu) & (sigma > 0) & (sigma < Inf); x(k) = mu(k) + sigma(k) .* (-sqrt (2) * erfcinv (2 * p(k))); endif endfunction %!demo %! ## Plot various iCDFs from the normal distribution %! p = 0.001:0.001:0.999; %! x1 = norminv (p, 0, 0.5); %! x2 = norminv (p, 0, 1); %! x3 = norminv (p, 0, 2); %! x4 = norminv (p, -2, 0.8); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([-5, 5]) %! legend ({"μ = 0, σ = 0.5", "μ = 0, σ = 1", ... %! "μ = 0, σ = 2", "μ = -2, σ = 0.8"}, "location", "northwest") %! title ("Normal iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (norminv (p, ones (1,5), ones (1,5)), [NaN -Inf 1 Inf NaN]) %!assert (norminv (p, 1, ones (1,5)), [NaN -Inf 1 Inf NaN]) %!assert (norminv (p, ones (1,5), 1), [NaN -Inf 1 Inf NaN]) %!assert (norminv (p, [1 -Inf NaN Inf 1], 1), [NaN NaN NaN NaN NaN]) %!assert (norminv (p, 1, [1 0 NaN Inf 1]), [NaN NaN NaN NaN NaN]) %!assert (norminv ([p(1:2) NaN p(4:5)], 1, 1), [NaN -Inf NaN Inf NaN]) %!assert (norminv (p), probit (p)) %!assert (norminv (0.31254), probit (0.31254)) ## Test class of input preserved %!assert (norminv ([p, NaN], 1, 1), [NaN -Inf 1 Inf NaN NaN]) %!assert (norminv (single ([p, NaN]), 1, 1), single ([NaN -Inf 1 Inf NaN NaN])) %!assert (norminv ([p, NaN], single (1), 1), single ([NaN -Inf 1 Inf NaN NaN])) %!assert (norminv ([p, NaN], 1, single (1)), single ([NaN -Inf 1 Inf NaN NaN])) ## Test input validation %!error norminv () %!error ... %! norminv (ones (3), ones (2), ones (2)) %!error ... %! norminv (ones (2), ones (3), ones (2)) %!error ... %! norminv (ones (2), ones (2), ones (3)) %!error norminv (i, 2, 2) %!error norminv (2, i, 2) %!error norminv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/normpdf.m000066400000000000000000000120041475240274700217560ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} normpdf (@var{x}) ## @deftypefnx {statistics} {@var{y} =} normpdf (@var{x}, @var{mu}) ## @deftypefnx {statistics} {@var{y} =} normpdf (@var{x}, @var{mu}, @var{sigma}) ## ## Normal probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the normal distribution with mean @var{mu} and standard deviation ## @var{sigma}. The size of @var{y} is the common size of @var{p}, @var{mu} and ## @var{sigma}. A scalar input functions as a constant matrix of the same size ## as the other inputs. ## ## Default values are @var{mu} = 0, @var{sigma} = 1. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{norminv, norminv, normrnd, normfit, normlike, normstat} ## @end deftypefn function y = normpdf (x, mu, sigma) ## Check for valid number of input arguments if (nargin < 1 || nargin > 3) error ("normpdf: function called with too few input arguments."); endif ## Add defaults (if missing input arguments) if (nargin < 2) mu = 0; endif if (nargin < 3) sigma = 1; endif ## Check for common size of X, MU, and SIGMA if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma)) [retval, x, mu, sigma] = common_size (x, mu, sigma); if (retval > 0) error ("normpdf: X, MU, and SIGMA must be of common size or scalars."); endif endif ## Check for X, MU, and SIGMA being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma)) error ("normpdf: X, MU, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Compute normal PDF if (isscalar (mu) && isscalar (sigma)) if (isfinite (mu) && (sigma > 0) && (sigma < Inf)) y = stdnormal_pdf ((x - mu) / sigma) / sigma; else y = NaN (size (x), class (y)); endif else k = isinf (mu) | !(sigma > 0) | !(sigma < Inf); y(k) = NaN; k = ! isinf (mu) & (sigma > 0) & (sigma < Inf); y(k) = stdnormal_pdf ((x(k) - mu(k)) ./ sigma(k)) ./ sigma(k); endif endfunction function y = stdnormal_pdf (x) y = (2 * pi)^(- 1/2) * exp (- x .^ 2 / 2); endfunction %!demo %! ## Plot various PDFs from the normal distribution %! x = -5:0.01:5; %! y1 = normpdf (x, 0, 0.5); %! y2 = normpdf (x, 0, 1); %! y3 = normpdf (x, 0, 2); %! y4 = normpdf (x, -2, 0.8); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! xlim ([-5, 5]) %! ylim ([0, 0.9]) %! legend ({"μ = 0, σ = 0.5", "μ = 0, σ = 1", ... %! "μ = 0, σ = 2", "μ = -2, σ = 0.8"}, "location", "northeast") %! title ("Normal PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-Inf, 1, 2, Inf]; %! y = 1 / sqrt (2 * pi) * exp (-(x - 1) .^ 2 / 2); %!assert (normpdf (x, ones (1,4), ones (1,4)), y, eps) %!assert (normpdf (x, 1, ones (1,4)), y, eps) %!assert (normpdf (x, ones (1,4), 1), y, eps) %!assert (normpdf (x, [0 -Inf NaN Inf], 1), [y(1) NaN NaN NaN], eps) %!assert (normpdf (x, 1, [Inf NaN -1 0]), [NaN NaN NaN NaN], eps) %!assert (normpdf ([x, NaN], 1, 1), [y, NaN], eps) ## Test class of input preserved %!assert (normpdf (single ([x, NaN]), 1, 1), single ([y, NaN]), eps ("single")) %!assert (normpdf ([x, NaN], single (1), 1), single ([y, NaN]), eps ("single")) %!assert (normpdf ([x, NaN], 1, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error normpdf () %!error ... %! normpdf (ones (3), ones (2), ones (2)) %!error ... %! normpdf (ones (2), ones (3), ones (2)) %!error ... %! normpdf (ones (2), ones (2), ones (3)) %!error normpdf (i, 2, 2) %!error normpdf (2, i, 2) %!error normpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/normrnd.m000066400000000000000000000153161475240274700220010ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} normrnd (@var{mu}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} normrnd (@var{mu}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} normrnd (@var{mu}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} normrnd (@var{mu}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the normal distribution. ## ## @code{@var{r} = normrnd (@var{mu}, @var{sigma})} returns an array of random ## numbers chosen from the normal distribution with mean @var{mu} and standard ## deviation @var{sigma}. The size of @var{r} is the common size of @var{mu} ## and @var{sigma}. A scalar input functions as a constant matrix of the same ## size as the other inputs. Both parameters must be finite real numbers and ## @var{sigma} > 0, otherwise NaN is returned. ## ## When called with a single size argument, @code{normrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{norminv, norminv, normpdf, normfit, normlike, normstat} ## @end deftypefn function r = normrnd (mu, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("normrnd: function called with too few input arguments."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("normrnd: MU and SIGMA must be of common size or scalars."); endif endif ## Check for MU and SIGMA being reals if (iscomplex (mu) || iscomplex (sigma)) error ("normrnd: MU and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["normrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("normrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (mu), sz)) error ("normrnd: MU and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from normal distribution if (isscalar (mu) && isscalar (sigma)) if (isfinite (mu) && (sigma >= 0) && (sigma < Inf)) r = mu + sigma * randn (sz, cls); else r = NaN (sz, cls); endif else r = mu + sigma .* randn (sz, cls); k = ! isfinite (mu) | ! (sigma >= 0) | ! (sigma < Inf); r(k) = NaN; endif endfunction ## Test output %!assert (size (normrnd (1, 1)), [1 1]) %!assert (size (normrnd (1, ones (2,1))), [2, 1]) %!assert (size (normrnd (1, ones (2,2))), [2, 2]) %!assert (size (normrnd (ones (2,1), 1)), [2, 1]) %!assert (size (normrnd (ones (2,2), 1)), [2, 2]) %!assert (size (normrnd (1, 1, 3)), [3, 3]) %!assert (size (normrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (normrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (normrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (normrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (normrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (normrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (normrnd (1, 1)), "double") %!assert (class (normrnd (1, single (1))), "single") %!assert (class (normrnd (1, single ([1, 1]))), "single") %!assert (class (normrnd (single (1), 1)), "single") %!assert (class (normrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error normrnd () %!error normrnd (1) %!error ... %! normrnd (ones (3), ones (2)) %!error ... %! normrnd (ones (2), ones (3)) %!error normrnd (i, 2, 3) %!error normrnd (1, i, 3) %!error ... %! normrnd (1, 2, -1) %!error ... %! normrnd (1, 2, 1.2) %!error ... %! normrnd (1, 2, ones (2)) %!error ... %! normrnd (1, 2, [2 -1 2]) %!error ... %! normrnd (1, 2, [2 0 2.5]) %!error ... %! normrnd (1, 2, 2, -1, 5) %!error ... %! normrnd (1, 2, 2, 1.5, 5) %!error ... %! normrnd (2, ones (2), 3) %!error ... %! normrnd (2, ones (2), [3, 2]) %!error ... %! normrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/plcdf.m000066400000000000000000000126321475240274700214100ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} plcdf (@var{data}, @var{x}, @var{Fx}) ## @deftypefnx {statistics} {@var{p} =} plcdf (@var{data}, @var{x}, @var{Fx}, @qcode{"upper"}) ## ## Piecewise linear cumulative distribution function (CDF). ## ## For each element of @var{data}, compute the cumulative distribution function ## (CDF) of the piecewise linear distribution with a vector of @var{x} values at ## which the CDF changes slope and a vector of CDF values @var{Fx} that ## correspond to each value in @var{x}. Both @var{x} and @var{Fx} must be ## vectors of the same size and at least 2-elements long. The size of @var{p} ## is the same as @var{data}. ## ## @code{@var{p} = plcdf (@var{data}, @var{x}, @var{Fx}, "upper")} computes ## the upper tail probability of the piecewise linear distribution with ## parameters @var{x} and @var{Fx}, at the values in @var{data}. ## ## Further information about the piecewise linear distribution can be found at ## @url{https://en.wikipedia.org/wiki/Piecewise_linear_function} ## ## @seealso{plinv, plpdf, plrnd, plstat} ## @end deftypefn function p = plcdf (data, x, Fx, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("plcdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin == 4 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin == 4 && ! strcmpi (uflag, "upper")) error ("plcdf: invalid argument for upper tail."); else uflag = false; endif ## Check for common size of X and FX if (! isvector (x) || ! isvector (Fx) || ! isequal (size (x), size (Fx))) error ("plcdf: X and FX must be vectors of equal size."); endif ## Check for X and FX being at least 2-elements long if (length (x) < 2 || length (Fx) < 2) error ("plcdf: X and FX must be at least two-elements long."); endif ## Check for Fx being bounded in [0, 1] if (any (Fx < 0) || any (Fx > 1)) error ("plcdf: FX must be bounded in the range [0, 1]."); endif ## Check for DATA, X, and FX being reals if (iscomplex (data) || iscomplex (x) || iscomplex (Fx)) error ("plcdf: DATA, X, and FX must not be complex."); endif ## Check for class type if (isa (data, "single") || isa (x, "single") || isa (Fx, "single")); p = zeros (size (data), "single"); else p = zeros (size (data)); endif ## Find data within supported range support = (data >= x(1) & data <= x(end)); p(support) = interp1 (x, Fx, data(support), "linear"); ## Force right side outside support to 1 and invalid data to NaN p(data > x(end)) = 1; p(isnan(data)) = NaN; ## Return upper tail (if requested) if (uflag) p = 1 - p; endif endfunction %!demo %! ## Plot various CDFs from the Piecewise linear distribution %! data = 0:0.01:10; %! x1 = [0, 1, 3, 4, 7, 10]; %! Fx1 = [0, 0.2, 0.5, 0.6, 0.7, 1]; %! x2 = [0, 2, 5, 6, 7, 8]; %! Fx2 = [0, 0.1, 0.3, 0.6, 0.9, 1]; %! p1 = plcdf (data, x1, Fx1); %! p2 = plcdf (data, x2, Fx2); %! plot (data, p1, "-b", data, p2, "g") %! grid on %! ylim ([0, 1]) %! xlim ([0, 10]) %! legend ({"x1, Fx1", "x2, Fx2"}, "location", "southeast") %! title ("Piecewise linear CDF") %! xlabel ("values in data") %! ylabel ("probability") ## Test output %!test %! data = 0:0.2:1; %! p = plcdf (data, [0, 1], [0, 1]); %! assert (p, data); %!test %! data = 0:0.2:1; %! p = plcdf (data, [0, 2], [0, 1]); %! assert (p, 0.5 * data); %!test %! data = 0:0.2:1; %! p = plcdf (data, [0, 1], [0, 0.5]); %! assert (p, 0.5 * data); %!test %! data = 0:0.2:1; %! p = plcdf (data, [0, 0.5], [0, 1]); %! assert (p, [0, 0.4, 0.8, 1, 1, 1]); %!test %! data = 0:0.2:1; %! p = plcdf (data, [0, 1], [0, 1], "upper"); %! assert (p, 1 - data); ## Test input validation %!error plcdf () %!error plcdf (1) %!error plcdf (1, 2) %!error plcdf (1, 2, 3, "uper") %!error plcdf (1, 2, 3, 4) %!error ... %! plcdf (1, [0, 1, 2], [0, 1]) %!error ... %! plcdf (1, [0], [1]) %!error ... %! plcdf (1, [0, 1, 2], [0, 1, 1.5]) %!error ... %! plcdf (1, [0, 1, 2], [0, i, 1]) %!error ... %! plcdf (i, [0, 1, 2], [0, 0.5, 1]) %!error ... %! plcdf (1, [0, i, 2], [0, 0.5, 1]) %!error ... %! plcdf (1, [0, 1, 2], [0, 0.5i, 1]) statistics-release-1.7.3/inst/dist_fun/plinv.m000066400000000000000000000115241475240274700214470ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{data} =} plinv (@var{p}, @var{x}, @var{Fx}) ## ## Inverse of the piecewise linear distribution (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) ## of the piecewise linear distribution with a vector of @var{x} values at ## which the CDF changes slope and a vector of CDF values @var{Fx} that ## correspond to each value in @var{x}. Both @var{x} and @var{Fx} must be ## vectors of the same_p size and at least 2-elements long.. The size of ## @var{data} is the same_p as @var{p}. ## ## Further information about the piecewise linear distribution can be found at ## @url{https://en.wikipedia.org/wiki/Piecewise_linear_function} ## ## @seealso{plcdf, plpdf, plrnd, plstat} ## @end deftypefn function data = plinv (p, x, Fx) ## Check for valid number of input arguments if (nargin < 3) error ("plinv: function called with too few input arguments."); endif ## Check for common size of X and FX if (! isvector (x) || ! isvector (Fx) || ! isequal (size (x), size (Fx))) error ("plinv: X and FX must be vectors of equal size."); endif ## Check for X and FX being at least 2-elements long if (length (x) < 2 || length (Fx) < 2) error ("plinv: X and FX must be at least two-elements long."); endif ## Check for Fx being bounded in [0, 1] if (any (Fx < 0) || any (Fx > 1)) error ("plinv: FX must be bounded in the range [0, 1]."); endif ## Check for P, X, and FX being reals if (iscomplex (p) || iscomplex (x) || iscomplex (Fx)) error ("plinv: P, X, and FX must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (x, "single") || isa (Fx, "single")); data = zeros (size (p), "single"); else data = zeros (size (p)); endif ## Remove consecutive bins with almost zero probability pw_diff = diff (Fx); if any(pw_diff==0) zero_p = 2 * eps(Fx); same_p = pw_diff <= zero_p(1:end-1); remove = same_p(1:end-1) & same_p(2:end); while (any(remove)) idx = find (remove); same_p(idx) = []; Fx(idx+1) = []; x(idx+1) = []; pw_diff = diff (Fx); remove = same_p(1:end-1) & same_p(2:end); endwhile idx = find (pw_diff==0); Fx(idx+1) = Fx(idx) + eps(Fx(idx)); endif p(p < 0 | 1 < p) = NaN; data = interp1 (Fx, x, p, "linear"); endfunction %!demo %! ## Plot various iCDFs from the Piecewise linear distribution %! p = 0.001:0.001:0.999; %! x1 = [0, 1, 3, 4, 7, 10]; %! Fx1 = [0, 0.2, 0.5, 0.6, 0.7, 1]; %! x2 = [0, 2, 5, 6, 7, 8]; %! Fx2 = [0, 0.1, 0.3, 0.6, 0.9, 1]; %! data1 = plinv (p, x1, Fx1); %! data2 = plinv (p, x2, Fx2); %! plot (p, data1, "-b", p, data2, "-g") %! grid on %! legend ({"x1, Fx1", "x2, Fx2"}, "location", "northwest") %! title ("Piecewise linear iCDF") %! xlabel ("probability") %! ylabel ("values in data") ## Test output %!test %! p = 0:0.2:1; %! data = plinv (p, [0, 1], [0, 1]); %! assert (data, p); %!test %! p = 0:0.2:1; %! data = plinv (p, [0, 2], [0, 1]); %! assert (data, 2 * p); %!test %! p = 0:0.2:1; %! data_out = 1:6; %! data = plinv (p, [0, 1], [0, 0.5]); %! assert (data, [0, 0.4, 0.8, NA, NA, NA]); %!test %! p = 0:0.2:1; %! data_out = 1:6; %! data = plinv (p, [0, 0.5], [0, 1]); %! assert (data, [0:0.1:0.5]); ## Test input validation %!error plinv () %!error plinv (1) %!error plinv (1, 2) %!error ... %! plinv (1, [0, 1, 2], [0, 1]) %!error ... %! plinv (1, [0], [1]) %!error ... %! plinv (1, [0, 1, 2], [0, 1, 1.5]) %!error ... %! plinv (1, [0, 1, 2], [0, i, 1]) %!error ... %! plinv (i, [0, 1, 2], [0, 0.5, 1]) %!error ... %! plinv (1, [0, i, 2], [0, 0.5, 1]) %!error ... %! plinv (1, [0, 1, 2], [0, 0.5i, 1]) statistics-release-1.7.3/inst/dist_fun/plpdf.m000066400000000000000000000111621475240274700214220ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} plpdf (@var{data}, @var{x}, @var{Fx}) ## ## Piecewise linear probability density function (PDF). ## ## For each element of @var{data}, compute the probability density function ## (PDF) of the piecewise linear distribution with a vector of @var{x} values at ## which the CDF changes slope and a vector of CDF values @var{Fx} that ## correspond to each value in @var{x}. Both @var{x} and @var{Fx} must be ## vectors of the same size and at least 2-elements long. The size of @var{p} ## is the same as @var{data}. ## ## Further information about the piecewise linear distribution can be found at ## @url{https://en.wikipedia.org/wiki/Piecewise_linear_function} ## ## @seealso{plcdf, plinv, plrnd, plstat} ## @end deftypefn function y = plpdf (data, x, Fx) ## Check for valid number of input arguments if (nargin < 3) error ("plpdf: function called with too few input arguments."); endif ## Check for common size of X and FX if (! isvector (x) || ! isvector (Fx) || ! isequal (size (x), size (Fx))) error ("plpdf: X and FX must be vectors of equal size."); endif ## Check for X and FX being at least 2-elements long if (length (x) < 2 || length (Fx) < 2) error ("plpdf: X and FX must be at least two-elements long."); endif ## Check for Fx being bounded in [0, 1] if (any (Fx < 0) || any (Fx > 1)) error ("plpdf: FX must be bounded in the range [0, 1]."); endif ## Check for DATA, X, and FX being reals if (iscomplex (data) || iscomplex (x) || iscomplex (Fx)) error ("plpdf: DATA, X, and FX must not be complex."); endif ## Force DATA, X, and FX into row vectors data = data(:)'; x = x(:)'; Fx = Fx(:)'; ## Check for class type if (isa (data, "single") || isa (x, "single") || isa (Fx, "single")); y = zeros (size (data), "single"); else y = zeros (size (data)); endif ## Bin data according to X [~, bin] = histc (data, [-Inf, x, Inf]); ## Compute piecewise densities dense = diff(Fx) ./ diff(x); bin_d = [0, dense, 0]; ## Fix densities xlen = length (x); bin(bin > xlen) = xlen + 1; y(bin>0) = bin_d(bin(bin>0)); ## Force invalid data to NaN y(isnan(data)) = NaN; endfunction %!demo %! ## Plot various PDFs from the Piecewise linear distribution %! data = 0:0.01:10; %! x1 = [0, 1, 3, 4, 7, 10]; %! Fx1 = [0, 0.2, 0.5, 0.6, 0.7, 1]; %! x2 = [0, 2, 5, 6, 7, 8]; %! Fx2 = [0, 0.1, 0.3, 0.6, 0.9, 1]; %! y1 = plpdf (data, x1, Fx1); %! y2 = plpdf (data, x2, Fx2); %! plot (data, y1, "-b", data, y2, "g") %! grid on %! ylim ([0, 0.6]) %! xlim ([0, 10]) %! legend ({"x1, Fx1", "x2, Fx2"}, "location", "northeast") %! title ("Piecewise linear CDF") %! xlabel ("values in data") %! ylabel ("density") ## Test output %!shared x, Fx %! x = [0, 1, 3, 4, 7, 10]; %! Fx = [0, 0.2, 0.5, 0.6, 0.7, 1]; %!assert (plpdf (0.5, x, Fx), 0.2, eps); %!assert (plpdf (1.5, x, Fx), 0.15, eps); %!assert (plpdf (3.5, x, Fx), 0.1, eps); %!assert (plpdf (5, x, Fx), 0.1/3, eps); %!assert (plpdf (8, x, Fx), 0.1, eps); ## Test input validation %!error plpdf () %!error plpdf (1) %!error plpdf (1, 2) %!error ... %! plpdf (1, [0, 1, 2], [0, 1]) %!error ... %! plpdf (1, [0], [1]) %!error ... %! plpdf (1, [0, 1, 2], [0, 1, 1.5]) %!error ... %! plpdf (1, [0, 1, 2], [0, i, 1]) %!error ... %! plpdf (i, [0, 1, 2], [0, 0.5, 1]) %!error ... %! plpdf (1, [0, i, 2], [0, 0.5, 1]) %!error ... %! plpdf (1, [0, 1, 2], [0, 0.5i, 1]) statistics-release-1.7.3/inst/dist_fun/plrnd.m000066400000000000000000000142521475240274700214370ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} plrnd (@var{x}, @var{Fx}) ## @deftypefnx {statistics} {@var{r} =} plrnd (@var{x}, @var{Fx}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} plrnd (@var{x}, @var{Fx}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} plrnd (@var{x}, @var{Fx}, [@var{sz}]) ## ## Random arrays from the piecewise linear distribution. ## ## @code{@var{r} = plrnd (@var{x}, @var{Fx})} returns a random number chosen ## from the piecewise linear distribution with a vector of @var{x} values at ## which the CDF changes slope and a vector of CDF values @var{Fx} that ## correspond to each value in @var{x}. Both @var{x} and @var{Fx} must be ## vectors of the same size and at least 2-elements long. ## ## When called with a single size argument, @code{plrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the piecewise linear distribution can be found at ## @url{https://en.wikipedia.org/wiki/Piecewise_linear_function} ## ## @seealso{plcdf, plinv, plpdf, plstat} ## @end deftypefn function r = plrnd (x, Fx, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("plrnd: function called with too few input arguments."); endif ## Check for common size of X and FX if (! isvector (x) || ! isvector (Fx) || ! isequal (size (x), size (Fx))) error ("plrnd: X and FX must be vectors of equal size."); endif ## Check for X and FX being at least 2-elements long if (length (x) < 2 || length (Fx) < 2) error ("plrnd: X and FX must be at least two-elements long."); endif ## Check for Fx being bounded in [0, 1] if (any (Fx < 0) || any (Fx > 1)) error ("plrnd: FX must be bounded in the range [0, 1]."); endif ## Check for X and FX being reals if (iscomplex (x) || iscomplex (Fx)) error ("plrnd: X and FX must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = 1; elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["plrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("plrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Force X and FX into row vectors x = x(:)'; Fx = Fx(:)'; ## Check for class type if (isa (x, "single") || isa (Fx, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from the piecewise linear distribution u = rand (sz); r = zeros (sz); [~, bin] = histc (u(:)', Fx); r0 = x(bin); dx = diff (x); dF = diff (Fx); dr = (u(:)' - Fx(bin)) .* dx(bin) ./ dF(bin); r(:) = r0 + dr; ## Cast to appropriate class r = cast (r, cls); endfunction ## Test output %!shared x, Fx %! x = [0, 1, 3, 4, 7, 10]; %! Fx = [0, 0.2, 0.5, 0.6, 0.7, 1]; %!assert (size (plrnd (x, Fx)), [1, 1]) %!assert (size (plrnd (x, Fx, 3)), [3, 3]) %!assert (size (plrnd (x, Fx, [4, 1])), [4, 1]) %!assert (size (plrnd (x, Fx, 4, 1)), [4, 1]) %!assert (size (plrnd (x, Fx, 4, 1, 5)), [4, 1, 5]) %!assert (size (plrnd (x, Fx, 0, 1)), [0, 1]) %!assert (size (plrnd (x, Fx, 1, 0)), [1, 0]) %!assert (size (plrnd (x, Fx, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (plrnd (x, Fx)), "double") %!assert (class (plrnd (x, single (Fx))), "single") %!assert (class (plrnd (single (x), Fx)), "single") ## Test input validation %!error plrnd () %!error plrnd (1) %!error ... %! plrnd ([0, 1, 2], [0, 1]) %!error ... %! plrnd ([0], [1]) %!error ... %! plrnd ([0, 1, 2], [0, 1, 1.5]) %!error ... %! plrnd ([0, 1, 2], [0, i, 1]) %!error ... %! plrnd ([0, i, 2], [0, 0.5, 1]) %!error ... %! plrnd ([0, i, 2], [0, 0.5i, 1]) %!error ... %! plrnd (x, Fx, -1) %!error ... %! plrnd (x, Fx, 1.2) %!error ... %! plrnd (x, Fx, ones (2)) %!error ... %! plrnd (x, Fx, [2 -1 2]) %!error ... %! plrnd (x, Fx, [2 0 2.5]) %!error ... %! plrnd (x, Fx, 2, -1, 5) %!error ... %! plrnd (x, Fx, 2, 1.5, 5) statistics-release-1.7.3/inst/dist_fun/poisscdf.m000066400000000000000000000122341475240274700221300ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} poisscdf (@var{x}, @var{lambda}) ## @deftypefnx {statistics} {@var{p} =} poisscdf (@var{x}, @var{lambda}, @qcode{"upper"}) ## ## Poisson cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Poisson distribution with rate parameter @var{lambda}. The ## size of @var{p} is the common size of @var{x} and @var{lambda}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## @code{@var{p} = poisscdf (@var{x}, @var{lambda}, "upper")} computes the ## upper tail probability of the Poisson distribution with parameter ## @var{lambda}, at the values in @var{x}. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poissinv, poisspdf, poissrnd, poissfit, poisslike, poisstat} ## @end deftypefn function p = poisscdf (x, lambda, uflag) ## Check for valid number of input arguments if (nargin < 2) error ("poisscdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin == 3 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin == 3 && ! strcmpi (uflag, "upper")) error ("poisscdf: invalid argument for upper tail."); else uflag = false; endif ## Check for common size of X and LAMBDA if (! isscalar (x) || ! isscalar (lambda)) [retval, x, lambda] = common_size (x, lambda); if (retval > 0) error ("poisscdf: X and LAMBDA must be of common size or scalars."); endif endif ## Check for X and LAMBDA being reals if (iscomplex (x) || iscomplex (lambda)) error ("poisscdf: X and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (lambda, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force NaN for out of range parameters or missing data NaN is_nan = isnan (x) | isnan (lambda) | (lambda < 0) ... | (isinf (x) & isinf (lambda)); p(is_nan) = NaN; ## Compute P for X >= 0 x = floor (x); k = x >= 0 & ! is_nan & isfinite (lambda); ## Return 1 for positive infinite values of X, unless "upper" is given: p = 0 k1 = isinf (x) & lambda > 0 & isfinite (lambda); if (any (k1)) if (uflag) p(k1) = 0; else p(k1) = 1; endif endif ## Return 1 when X < 0 and "upper" is given k1 = x < 0 & lambda > 0 & isfinite (lambda); if (any (k1)) if (uflag) p(k1) = 1; endif endif ## Compute Poisson CDF for remaining cases x = x(k); lambda = lambda(k); if (uflag) p(k) = gammainc (lambda, x + 1); else p(k) = gammainc (lambda, x + 1, "upper"); endif endfunction %!demo %! ## Plot various CDFs from the Poisson distribution %! x = 0:20; %! p1 = poisscdf (x, 1); %! p2 = poisscdf (x, 4); %! p3 = poisscdf (x, 10); %! plot (x, p1, "*b", x, p2, "*g", x, p3, "*r") %! grid on %! ylim ([0, 1]) %! legend ({"λ = 1", "λ = 4", "λ = 10"}, "location", "southeast") %! title ("Poisson CDF") %! xlabel ("values in x (number of occurences)") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1 0 1 2 Inf]; %! y = [0, gammainc(1, (x(2:4) +1), "upper"), 1]; %!assert (poisscdf (x, ones (1,5)), y) %!assert (poisscdf (x, 1), y) %!assert (poisscdf (x, [1 0 NaN 1 1]), [y(1) 1 NaN y(4:5)]) %!assert (poisscdf ([x(1:2) NaN Inf x(5)], 1), [y(1:2) NaN 1 y(5)]) ## Test class of input preserved %!assert (poisscdf ([x, NaN], 1), [y, NaN]) %!assert (poisscdf (single ([x, NaN]), 1), single ([y, NaN]), eps ("single")) %!assert (poisscdf ([x, NaN], single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error poisscdf () %!error poisscdf (1) %!error poisscdf (1, 2, 3) %!error poisscdf (1, 2, "tail") %!error ... %! poisscdf (ones (3), ones (2)) %!error ... %! poisscdf (ones (2), ones (3)) %!error poisscdf (i, 2) %!error poisscdf (2, i) statistics-release-1.7.3/inst/dist_fun/poissinv.m000066400000000000000000000165751475240274700222040ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2014 Mike Giles ## Copyright (C) 2016 Lachlan Andrew ## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} poissinv (@var{p}, @var{lambda}) ## ## Inverse of the Poisson cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Poisson distribution with rate parameter @var{lambda}. The size of ## @var{x} is the common size of @var{p} and @var{lambda}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poisscdf, poisspdf, poissrnd, poissfit, poisslike, poisstat} ## @end deftypefn function x = poissinv (p, lambda) ## Check for valid number of input arguments if (nargin < 2) error ("poissinv: function called with too few input arguments."); endif ## Check for common size of P and LAMBDA if (! isscalar (p) || ! isscalar (lambda)) [retval, p, lambda] = common_size (p, lambda); if (retval > 0) error ("poissinv: P and LAMBDA must be of common size or scalars."); endif endif ## Check for P and LAMBDA being reals if (iscomplex (p) || iscomplex (lambda)) error ("poissinv: P and LAMBDA must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (lambda, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif ## Force NaN for out of range parameters or p-values k = (p < 0) | (p > 1) | isnan (p) | ! (lambda > 0); x(k) = NaN; k = (p == 1) & (lambda > 0); x(k) = Inf; k = (p > 0) & (p < 1) & (lambda > 0); if (any (k(:))) limit = 20; # After 'limit' iterations, use approx if (isscalar (lambda)) cdf = [(cumsum (poisspdf (0:limit-1,lambda))), 2]; y = p(:); # force to column r = bsxfun (@le, y(k), cdf); [~, x(k)] = max (r, [], 2); # find first instance of p <= cdf x(k) -= 1; else kk = find (k); cdf = exp (-lambda(kk)); for i = 1:limit m = find (cdf < p(kk)); if (isempty (m)) break; else x(kk(m)) += 1; cdf(m) += poisspdf (i, lambda(kk(m))); endif endfor endif ## Use Mike Giles's magic when x isn't < limit k &= (x == limit); if (any (k(:))) if (isscalar (lambda)) lam = repmat (lambda, size (p)); else lam = lambda; endif x(k) = analytic_approx (p(k), lam(k)); endif endif endfunction ## The following is based on Mike Giles's CUDA implementation, ## [http://people.maths.ox.ac.uk/gilesm/codes/poissinv/poissinv_cuda.h] ## which is copyright by the University of Oxford ## and is provided under the terms of the GNU GPLv3 license: ## http://www.gnu.org/licenses/gpl.html function x = analytic_approx (p, lambda) s = norminv (p, 0, 1) ./ sqrt (lambda); k = (s > -0.6833501) & (s < 1.777993); ## use polynomial approximations in central region if (any (k)) lam = lambda(k); if (isscalar (s)) sk = s; else sk = s(k); endif ## polynomial approximation to f^{-1}(s) - 1 rm = 2.82298751e-07; rm = -2.58136133e-06 + rm.*sk; rm = 1.02118025e-05 + rm.*sk; rm = -2.37996199e-05 + rm.*sk; rm = 4.05347462e-05 + rm.*sk; rm = -6.63730967e-05 + rm.*sk; rm = 0.000124762566 + rm.*sk; rm = -0.000256970731 + rm.*sk; rm = 0.000558953132 + rm.*sk; rm = -0.00133129194 + rm.*sk; rm = 0.00370367937 + rm.*sk; rm = -0.0138888706 + rm.*sk; rm = 0.166666667 + rm.*sk; rm = sk + sk.*(rm.*sk); ## polynomial approximation to correction c0(r) t = 1.86386867e-05; t = -0.000207319499 + t.*rm; t = 0.0009689451 + t.*rm; t = -0.00247340054 + t.*rm; t = 0.00379952985 + t.*rm; t = -0.00386717047 + t.*rm; t = 0.00346960934 + t.*rm; t = -0.00414125511 + t.*rm; t = 0.00586752093 + t.*rm; t = -0.00838583787 + t.*rm; t = 0.0132793933 + t.*rm; t = -0.027775536 + t.*rm; t = 0.333333333 + t.*rm; ## O(1/lam) correction y = -0.00014585224; y = 0.00146121529 + y.*rm; y = -0.00610328845 + y.*rm; y = 0.0138117964 + y.*rm; y = -0.0186988746 + y.*rm; y = 0.0168155118 + y.*rm; y = -0.013394797 + y.*rm; y = 0.0135698573 + y.*rm; y = -0.0155377333 + y.*rm; y = 0.0174065334 + y.*rm; y = -0.0198011178 + y.*rm; y ./= lam; x(k) = floor (lam + (y+t)+lam.*rm); endif k = ! k & (s > -sqrt (2)); if (any (k)) ## Newton iteration r = 1 + s(k); r2 = r + 1; while (any (abs (r - r2) > 1e-5)) t = log (r); r2 = r; s2 = sqrt (2 * ((1-r) + r.*t)); s2(r<1) *= -1; r = r2 - (s2 - s(k)) .* s2 ./ t; if (r < 0.1 * r2) r = 0.1 * r2; endif endwhile t = log (r); y = lambda(k) .* r + log (sqrt (2*r.*((1-r) + r.*t)) ./ abs (r-1)) ./ t; x(k) = floor (y - 0.0218 ./ (y + 0.065 * lambda(k))); endif endfunction %!demo %! ## Plot various iCDFs from the Poisson distribution %! p = 0.001:0.001:0.999; %! x1 = poissinv (p, 13); %! x2 = poissinv (p, 4); %! x3 = poissinv (p, 10); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r") %! grid on %! ylim ([0, 20]) %! legend ({"λ = 1", "λ = 4", "λ = 10"}, "location", "northwest") %! title ("Poisson iCDF") %! xlabel ("probability") %! ylabel ("values in x (number of occurences)") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (poissinv (p, ones (1,5)), [NaN 0 1 Inf NaN]) %!assert (poissinv (p, 1), [NaN 0 1 Inf NaN]) %!assert (poissinv (p, [1 0 NaN 1 1]), [NaN NaN NaN Inf NaN]) %!assert (poissinv ([p(1:2) NaN p(4:5)], 1), [NaN 0 NaN Inf NaN]) ## Test class of input preserved %!assert (poissinv ([p, NaN], 1), [NaN 0 1 Inf NaN NaN]) %!assert (poissinv (single ([p, NaN]), 1), single ([NaN 0 1 Inf NaN NaN])) %!assert (poissinv ([p, NaN], single (1)), single ([NaN 0 1 Inf NaN NaN])) ## Test input validation %!error poissinv () %!error poissinv (1) %!error ... %! poissinv (ones (3), ones (2)) %!error ... %! poissinv (ones (2), ones (3)) %!error poissinv (i, 2) %!error poissinv (2, i) statistics-release-1.7.3/inst/dist_fun/poisspdf.m000066400000000000000000000076751475240274700221620ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} poisspdf (@var{x}, @var{lambda}) ## ## Poisson probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Poisson distribution with rate parameter @var{lambda}. The size of ## @var{y} is the common size of @var{x} and @var{lambda}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poisscdf, poissinv, poissrnd, poissfit, poisslike, poisstat} ## @end deftypefn function y = poisspdf (x, lambda) ## Check for valid number of input arguments if (nargin < 2) error ("poisspdf: function called with too few input arguments."); endif ## Check for common size of X and LAMBDA if (! isscalar (x) || ! isscalar (lambda)) [retval, x, lambda] = common_size (x, lambda); if (retval > 0) error ("poisspdf: X and LAMBDA must be of common size or scalars."); endif endif ## Check for X and LAMBDA being reals if (iscomplex (x) || iscomplex (lambda)) error ("poisspdf: X and LAMBDA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (lambda, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Force NaN for out of range parameters or missing data NaN k = isnan (x) | ! (lambda > 0); y(k) = NaN; k = (x >= 0) & (x < Inf) & (x == fix (x)) & (lambda > 0); if (isscalar (lambda)) y(k) = exp (x(k) * log (lambda) - lambda - gammaln (x(k) + 1)); else y(k) = exp (x(k) .* log (lambda(k)) - lambda(k) - gammaln (x(k) + 1)); endif endfunction %!demo %! ## Plot various PDFs from the Poisson distribution %! x = 0:20; %! y1 = poisspdf (x, 1); %! y2 = poisspdf (x, 4); %! y3 = poisspdf (x, 10); %! plot (x, y1, "*b", x, y2, "*g", x, y3, "*r") %! grid on %! ylim ([0, 0.4]) %! legend ({"λ = 1", "λ = 4", "λ = 10"}, "location", "northeast") %! title ("Poisson PDF") %! xlabel ("values in x (number of occurences)") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 1 2 Inf]; %! y = [0, exp(-1)*[1 1 0.5], 0]; %!assert (poisspdf (x, ones (1,5)), y, eps) %!assert (poisspdf (x, 1), y, eps) %!assert (poisspdf (x, [1 0 NaN 1 1]), [y(1) NaN NaN y(4:5)], eps) %!assert (poisspdf ([x, NaN], 1), [y, NaN], eps) ## Test class of input preserved %!assert (poisspdf (single ([x, NaN]), 1), single ([y, NaN]), eps ("single")) %!assert (poisspdf ([x, NaN], single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error poisspdf () %!error poisspdf (1) %!error ... %! poisspdf (ones (3), ones (2)) %!error ... %! poisspdf (ones (2), ones (3)) %!error poisspdf (i, 2) %!error poisspdf (2, i) statistics-release-1.7.3/inst/dist_fun/poissrnd.m000066400000000000000000000136561475240274700221700ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} poissrnd (@var{lambda}) ## @deftypefnx {statistics} {@var{r} =} poissrnd (@var{lambda}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} poissrnd (@var{lambda}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} poissrnd (@var{lambda}, [@var{sz}]) ## ## Random arrays from the Poisson distribution. ## ## @code{@var{r} = normrnd (@var{lambda})} returns an array of random numbers ## chosen from the Poisson distribution with rate parameter @var{lambda}. The ## size of @var{r} is the common size of @var{lambda}. A scalar input functions ## as a constant matrix of the same size as the other inputs. @var{lambda} must ## be a finite real number and greater or equal to 0, otherwise @qcode{NaN} is ## returned. ## ## When called with a single size argument, @code{poissrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poisscdf, poissinv, poisspdf, poissfit, poisslike, poisstat} ## @end deftypefn function r = poissrnd (lambda, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("poissrnd: function called with too few input arguments."); endif ## Check for LAMBDA being real if (iscomplex (lambda)) error ("poissrnd: LAMBDA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (lambda); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["poissrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("poissrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (lambda) && ! isequal (size (lambda), sz)) error ("poissrnd: LAMBDA must be scalar or of size SZ."); endif ## Check for class type if (isa (lambda, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Poisson distribution if (isscalar (lambda)) if (lambda >= 0 && lambda < Inf) r = randp (lambda, sz, cls); else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (lambda >= 0) & (lambda < Inf); r(k) = randp (lambda(k), cls); endif endfunction ## Test output %!assert (size (poissrnd (2)), [1, 1]) %!assert (size (poissrnd (ones (2,1))), [2, 1]) %!assert (size (poissrnd (ones (2,2))), [2, 2]) %!assert (size (poissrnd (1, 3)), [3, 3]) %!assert (size (poissrnd (1, [4 1])), [4, 1]) %!assert (size (poissrnd (1, 4, 1)), [4, 1]) %!assert (size (poissrnd (1, 4, 1)), [4, 1]) %!assert (size (poissrnd (1, 4, 1, 5)), [4, 1, 5]) %!assert (size (poissrnd (1, 0, 1)), [0, 1]) %!assert (size (poissrnd (1, 1, 0)), [1, 0]) %!assert (size (poissrnd (1, 1, 2, 0, 5)), [1, 2, 0, 5]) %!assert (poissrnd (0, 1, 1), 0) %!assert (poissrnd ([0, 0, 0], [1, 3]), [0 0 0]) ## Test class of input preserved %!assert (class (poissrnd (2)), "double") %!assert (class (poissrnd (single (2))), "single") %!assert (class (poissrnd (single ([2 2]))), "single") ## Test input validation %!error poissrnd () %!error poissrnd (i) %!error ... %! poissrnd (1, -1) %!error ... %! poissrnd (1, 1.2) %!error ... %! poissrnd (1, ones (2)) %!error ... %! poissrnd (1, [2 -1 2]) %!error ... %! poissrnd (1, [2 0 2.5]) %!error ... %! poissrnd (ones (2), ones (2)) %!error ... %! poissrnd (1, 2, -1, 5) %!error ... %! poissrnd (1, 2, 1.5, 5) %!error poissrnd (ones (2,2), 3) %!error poissrnd (ones (2,2), [3, 2]) %!error poissrnd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/raylcdf.m000066400000000000000000000114041475240274700217400ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} raylcdf (@var{x}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} raylcdf (@var{x}, @var{sigma}, @qcode{"upper"}) ## ## Rayleigh cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Rayleigh distribution with scale parameter @var{sigma}. The ## size of @var{p} is the common size of @var{x} and @var{sigma}. A scalar ## input functions as a constant matrix of the same size as the other inputs. ## ## @code{@var{p} = raylcdf (@var{x}, @var{sigma}, "upper")} computes the upper ## tail probability of the Rayleigh distribution with parameter @var{sigma}, at ## the values in @var{x}. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylinv, raylpdf, raylrnd, raylfit, rayllike, raylstat} ## @end deftypefn function p = raylcdf (x, sigma, uflag) ## Check for valid number of input arguments if (nargin < 2) error ("raylcdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin == 3 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin == 3 && ! strcmpi (uflag, "upper")) error ("raylcdf: invalid argument for upper tail."); else uflag = false; endif ## Check for common size of X and SIGMA if (! isscalar (x) || ! isscalar (sigma)) [retval, x, sigma] = common_size (x, sigma); if (retval > 0) error ("raylcdf: X and SIGMA must be of common size or scalars."); endif endif ## Check for X and SIGMA being reals if (iscomplex (x) || iscomplex (sigma)) error ("raylcdf: X and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (sigma, "single")); p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force 1 for upper flag and X <= 0 k0 = sigma > 0 & x <= 0; if (uflag && any (k0(:))) p(k0) = 1; end ## Calculate Rayleigh CDF for valid parameter and data range k = sigma > 0 & x > 0; if (any (k(:))) if (uflag) p(k) = exp (-x(k) .^ 2 ./ (2 * sigma(k) .^ 2)); else p(k) = - expm1 (-x(k) .^ 2 ./ (2 * sigma(k) .^ 2)); endif endif ## Continue argument check p(! (k0 | k)) = NaN; endfunction %!demo %! ## Plot various CDFs from the Rayleigh distribution %! x = 0:0.01:10; %! p1 = raylcdf (x, 0.5); %! p2 = raylcdf (x, 1); %! p3 = raylcdf (x, 2); %! p4 = raylcdf (x, 3); %! p5 = raylcdf (x, 4); %! plot (x, p1, "-b", x, p2, "g", x, p3, "-r", x, p4, "-m", x, p5, "-k") %! grid on %! ylim ([0, 1]) %! legend ({"σ = 0.5", "σ = 1", "σ = 2", ... %! "σ = 3", "σ = 4"}, "location", "southeast") %! title ("Rayleigh CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!test %! x = 0:0.5:2.5; %! sigma = 1:6; %! p = raylcdf (x, sigma); %! expected_p = [0.0000, 0.0308, 0.0540, 0.0679, 0.0769, 0.0831]; %! assert (p, expected_p, 0.001); %!test %! x = 0:0.5:2.5; %! p = raylcdf (x, 0.5); %! expected_p = [0.0000, 0.3935, 0.8647, 0.9889, 0.9997, 1.0000]; %! assert (p, expected_p, 0.001); %!shared x, p %! x = [-1, 0, 1, 2, Inf]; %! p = [0, 0, 0.39346934028737, 0.86466471676338, 1]; %!assert (raylcdf (x, 1), p, 1e-14) %!assert (raylcdf (x, 1, "upper"), 1 - p, 1e-14) ## Test input validation %!error raylcdf () %!error raylcdf (1) %!error raylcdf (1, 2, "uper") %!error raylcdf (1, 2, 3) %!error ... %! raylcdf (ones (3), ones (2)) %!error ... %! raylcdf (ones (2), ones (3)) %!error raylcdf (i, 2) %!error raylcdf (2, i) statistics-release-1.7.3/inst/dist_fun/raylinv.m000066400000000000000000000072231475240274700220040ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} raylinv (@var{p}, @var{sigma}) ## ## Inverse of the Rayleigh cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Rayleigh distribution with scale parameter @var{sigma}. The size of ## @var{x} is the common size of @var{p} and @var{sigma}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylcdf, raylpdf, raylrnd, raylfit, rayllike, raylstat} ## @end deftypefn function x = raylinv (p, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("raylinv: function called with too few input arguments."); endif ## Check for common size of P and SIGMA if (! isscalar (p) || ! isscalar (sigma)) [retval, p, sigma] = common_size (p, sigma); if (retval > 0) error ("raylinv: P and SIGMA must be of common size or scalars."); endif endif ## Check for X and SIGMA being reals if (iscomplex (p) || iscomplex (sigma)) error ("raylinv: P and SIGMA must not be complex."); endif ## Calculate Rayleigh iCDF x = sqrt (-2 .* log (1 - p) .* sigma .^ 2); ## Check for valid parameter and support k = find (p == 1); if (any (k)) x(k) = Inf; endif k = find (! (p >= 0) | ! (p <= 1) | ! (sigma > 0)); if (any (k)) x(k) = NaN; endif endfunction %!demo %! ## Plot various iCDFs from the Rayleigh distribution %! p = 0.001:0.001:0.999; %! x1 = raylinv (p, 0.5); %! x2 = raylinv (p, 1); %! x3 = raylinv (p, 2); %! x4 = raylinv (p, 3); %! x5 = raylinv (p, 4); %! plot (p, x1, "-b", p, x2, "g", p, x3, "-r", p, x4, "-m", p, x5, "-k") %! grid on %! ylim ([0, 10]) %! legend ({"σ = 0,5", "σ = 1", "σ = 2", ... %! "σ = 3", "σ = 4"}, "location", "northwest") %! title ("Rayleigh iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!test %! p = 0:0.1:0.5; %! sigma = 1:6; %! x = raylinv (p, sigma); %! expected_x = [0.0000, 0.9181, 2.0041, 3.3784, 5.0538, 7.0645]; %! assert (x, expected_x, 0.001); %!test %! p = 0:0.1:0.5; %! x = raylinv (p, 0.5); %! expected_x = [0.0000, 0.2295, 0.3340, 0.4223, 0.5054, 0.5887]; %! assert (x, expected_x, 0.001); ## Test input validation %!error raylinv () %!error raylinv (1) %!error ... %! raylinv (ones (3), ones (2)) %!error ... %! raylinv (ones (2), ones (3)) %!error raylinv (i, 2) %!error raylinv (2, i) statistics-release-1.7.3/inst/dist_fun/raylpdf.m000066400000000000000000000071441475240274700217630ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} raylpdf (@var{x}, @var{sigma}) ## ## Rayleigh probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Rayleigh distribution with scale parameter @var{sigma}. The size of ## @var{p} is the common size of @var{x} and @var{sigma}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylcdf, raylinv, raylrnd, raylfit, rayllike, raylstat} ## @end deftypefn function y = raylpdf (x, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("raylpdf: function called with too few input arguments."); endif ## Check for common size of X and SIGMA if (! isscalar (x) || ! isscalar (sigma)) [retval, x, sigma] = common_size (x, sigma); if (retval > 0) error ("raylpdf: X and SIGMA must be of common size or scalars."); endif endif ## Check for X and SIGMA being reals if (iscomplex (x) || iscomplex (sigma)) error ("raylpdf: X and SIGMA must not be complex."); endif ## Calculate Rayleigh PDF y = x .* exp ((-x .^ 2) ./ (2 .* sigma .^ 2)) ./ (sigma .^ 2); ## Continue argument check k = find (! isfinite (x) | ! (sigma > 0)); if (any (k)) y(k) = NaN; endif k = x < 0; if (any (k)) y(k) = 0; endif endfunction %!demo %! ## Plot various PDFs from the Rayleigh distribution %! x = 0:0.01:10; %! y1 = raylpdf (x, 0.5); %! y2 = raylpdf (x, 1); %! y3 = raylpdf (x, 2); %! y4 = raylpdf (x, 3); %! y5 = raylpdf (x, 4); %! plot (x, y1, "-b", x, y2, "g", x, y3, "-r", x, y4, "-m", x, y5, "-k") %! grid on %! ylim ([0, 1.25]) %! legend ({"σ = 0,5", "σ = 1", "σ = 2", ... %! "σ = 3", "σ = 4"}, "location", "northeast") %! title ("Rayleigh PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!test %! x = 0:0.5:2.5; %! sigma = 1:6; %! y = raylpdf (x, sigma); %! expected_y = [0.0000, 0.1212, 0.1051, 0.0874, 0.0738, 0.0637]; %! assert (y, expected_y, 0.001); %!test %! x = 0:0.5:2.5; %! y = raylpdf (x, 0.5); %! expected_y = [0.0000, 1.2131, 0.5413, 0.0667, 0.0027, 0.0000]; %! assert (y, expected_y, 0.001); ## Test input validation %!error raylpdf () %!error raylpdf (1) %!error ... %! raylpdf (ones (3), ones (2)) %!error ... %! raylpdf (ones (2), ones (3)) %!error raylpdf (i, 2) %!error raylpdf (2, i) statistics-release-1.7.3/inst/dist_fun/raylrnd.m000066400000000000000000000133321475240274700217710ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} raylrnd (@var{sigma}) ## @deftypefnx {statistics} {@var{r} =} raylrnd (@var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} raylrnd (@var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} raylrnd (@var{sigma}, [@var{sz}]) ## ## Random arrays from the Rayleigh distribution. ## ## @code{@var{r} = raylrnd (@var{sigma})} returns an array of random numbers ## chosen from the Rayleigh distribution with scale parameter @var{sigma}. The ## size of @var{r} is the size of @var{sigma}. A scalar input functions as a ## constant matrix of the same size as the other inputs. @var{sigma} must be a ## finite real number greater than 0, otherwise @qcode{NaN} is returned. ## ## When called with a single size argument, @code{raylrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylcdf, raylinv, raylpdf, raylfit, rayllike, raylstat} ## @end deftypefn function r = raylrnd (sigma, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("raylrnd: function called with too few input arguments."); endif ## Check for SIGMA being real if (iscomplex (sigma)) error ("raylrnd: SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (sigma); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["raylrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("raylrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (sigma) && ! isequal (size (sigma), sz)) error ("raylrnd: SIGMA must be scalar or of size SZ."); endif ## Generate random sample from Rayleigh distribution r = sqrt (-2 .* log (1 - rand (sz)) .* sigma .^ 2); ## Check for valid parameter k = find (! (sigma > 0)); if (any (k)) r(k) = NaN; endif ## Cast into appropriate class if (isa (sigma, "single")) r = cast (r, "single"); endif endfunction ## Test output %!assert (size (raylrnd (2)), [1, 1]) %!assert (size (raylrnd (ones (2,1))), [2, 1]) %!assert (size (raylrnd (ones (2,2))), [2, 2]) %!assert (size (raylrnd (1, 3)), [3, 3]) %!assert (size (raylrnd (1, [4 1])), [4, 1]) %!assert (size (raylrnd (1, 4, 1)), [4, 1]) %!assert (size (raylrnd (1, 4, 1)), [4, 1]) %!assert (size (raylrnd (1, 4, 1, 5)), [4, 1, 5]) %!assert (size (raylrnd (1, 0, 1)), [0, 1]) %!assert (size (raylrnd (1, 1, 0)), [1, 0]) %!assert (size (raylrnd (1, 1, 2, 0, 5)), [1, 2, 0, 5]) %!assert (raylrnd (0, 1, 1), NaN) %!assert (raylrnd ([0, 0, 0], [1, 3]), [NaN, NaN, NaN]) ## Test class of input preserved %!assert (class (raylrnd (2)), "double") %!assert (class (raylrnd (single (2))), "single") %!assert (class (raylrnd (single ([2 2]))), "single") ## Test input validation %!error raylrnd () %!error raylrnd (i) %!error ... %! raylrnd (1, -1) %!error ... %! raylrnd (1, 1.2) %!error ... %! raylrnd (1, ones (2)) %!error ... %! raylrnd (1, [2 -1 2]) %!error ... %! raylrnd (1, [2 0 2.5]) %!error ... %! raylrnd (ones (2), ones (2)) %!error ... %! raylrnd (1, 2, -1, 5) %!error ... %! raylrnd (1, 2, 1.5, 5) %!error raylrnd (ones (2,2), 3) %!error raylrnd (ones (2,2), [3, 2]) %!error raylrnd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/ricecdf.m000066400000000000000000000160561475240274700217230ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} ricecdf (@var{x}, @var{s}, @var{sigma}) ## @deftypefnx {statistics} {@var{p} =} ricecdf (@var{x}, @var{s}, @var{sigma}, @qcode{"upper"}) ## ## Rician cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Rician distribution with non-centrality (distance) parameter ## @var{s} and scale parameter @var{sigma}. The size of @var{p} is the common ## size of @var{x}, @var{s}, and @var{sigma}. A scalar input functions as a ## constant matrix of the same size as the other inputs. ## ## @code{@var{p} = ricecdf (@var{x}, @var{s}, @var{sigma}, "upper")} computes ## the upper tail probability of the Rician distribution with parameters ## @var{s} and @var{sigma}, at the values in @var{x}. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{riceinv, ricepdf, ricernd, ricefit, ricelike, ricestat} ## @end deftypefn function p = ricecdf (x, s, sigma, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("ricecdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin == 4 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin == 4 && ! strcmpi (uflag, "upper")) error ("ricecdf: invalid argument for upper tail."); else uflag = false; endif ## Check for common size of X, S, and SIGMA if (! isscalar (x) || ! isscalar (s) || ! isscalar (sigma)) [retval, x, s, sigma] = common_size (x, s, sigma); if (retval > 0) error ("ricecdf: X, S, and SIGMA must be of common size or scalars."); endif endif ## Check for X, S, and SIGMA being reals if (iscomplex (x) || iscomplex (s) || iscomplex (sigma)) error ("ricecdf: X, S, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (s, "single") || isa (sigma, "single")); p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force 1 for upper flag and X <= 0 k0 = s >= 0 & sigma > 0 & x < 0; if (uflag && any (k0(:))) p(k0) = 1; end ## Calculate Rayleigh CDF for valid parameter and data range k = s >= 0 & sigma > 0 & x >= 0; if (any (k(:))) if (uflag) p(k) = marcumQ1 (s(k) ./ sigma(k), x(k) ./ sigma(k)); else p(k) = 1 - marcumQ1 (s(k) ./ sigma(k), x(k) ./ sigma(k)); endif endif ## Continue argument check p(! (k0 | k)) = NaN; endfunction ## Marcum's "Q" function of order 1 function Q = marcumQ1 (a, b) ## Prepare output matrix if (isa (a, "single") || isa (b, "single")) Q = NaN (size (b), "single"); else Q = NaN (size (b)); endif ## Force marginal cases Q(a != Inf & b == 0) = 1; Q(a != Inf & b == Inf) = 0; Q(a == Inf & b != Inf) = 1; z = isnan (Q) & a == 0 & b != Inf; if (any(z)) Q(z) = exp ((-b(z) .^ 2) ./ 2); end ## Compute the remaining cases z = isnan (Q) & ! isnan (a) & ! isnan (b); if (any(z(:))) aa = (a(z) .^ 2) ./ 2; bb = (b(z) .^ 2) ./ 2; eA = exp (-aa); eB = bb .* exp (-bb); h = eA; d = eB .* h; s = d; j = (d > s.*eps(class(d))); k = 1; while (any (j)) eA = aa .* eA ./ k; h = h + eA; eB = bb .* eB ./ (k + 1); d = eB .* h; s(j) = s (j) + d(j); j = (d > s .* eps (class (d))); k = k + 1; endwhile Q(z) = 1 - s; endif endfunction %!demo %! ## Plot various CDFs from the Rician distribution %! x = 0:0.01:10; %! p1 = ricecdf (x, 0, 1); %! p2 = ricecdf (x, 0.5, 1); %! p3 = ricecdf (x, 1, 1); %! p4 = ricecdf (x, 2, 1); %! p5 = ricecdf (x, 4, 1); %! plot (x, p1, "-b", x, p2, "g", x, p3, "-r", x, p4, "-m", x, p5, "-k") %! grid on %! ylim ([0, 1]) %! xlim ([0, 8]) %! legend ({"s = 0, σ = 1", "s = 0.5, σ = 1", "s = 1, σ = 1", ... %! "s = 2, σ = 1", "s = 4, σ = 1"}, "location", "southeast") %! title ("Rician CDF") %! xlabel ("values in x") %! ylabel ("probability") %!demo %! ## Plot various CDFs from the Rician distribution %! x = 0:0.01:10; %! p1 = ricecdf (x, 0, 0.5); %! p2 = ricecdf (x, 0, 2); %! p3 = ricecdf (x, 0, 3); %! p4 = ricecdf (x, 2, 2); %! p5 = ricecdf (x, 4, 2); %! plot (x, p1, "-b", x, p2, "g", x, p3, "-r", x, p4, "-m", x, p5, "-k") %! grid on %! ylim ([0, 1]) %! xlim ([0, 8]) %! legend ({"ν = 0, σ = 0.5", "ν = 0, σ = 2", "ν = 0, σ = 3", ... %! "ν = 2, σ = 2", "ν = 4, σ = 2"}, "location", "southeast") %! title ("Rician CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!test %! x = 0:0.5:2.5; %! s = 1:6; %! p = ricecdf (x, s, 1); %! expected_p = [0.0000, 0.0179, 0.0108, 0.0034, 0.0008, 0.0001]; %! assert (p, expected_p, 0.001); %!test %! x = 0:0.5:2.5; %! sigma = 1:6; %! p = ricecdf (x, 1, sigma); %! expected_p = [0.0000, 0.0272, 0.0512, 0.0659, 0.0754, 0.0820]; %! assert (p, expected_p, 0.001); %!test %! x = 0:0.5:2.5; %! p = ricecdf (x, 0, 1); %! expected_p = [0.0000, 0.1175, 0.3935, 0.6753, 0.8647, 0.9561]; %! assert (p, expected_p, 0.001); %!test %! x = 0:0.5:2.5; %! p = ricecdf (x, 1, 1); %! expected_p = [0.0000, 0.0735, 0.2671, 0.5120, 0.7310, 0.8791]; %! assert (p, expected_p, 0.001); %!shared x, p %! x = [-1, 0, 1, 2, Inf]; %! p = [0, 0, 0.26712019620318, 0.73098793996409, 1]; %!assert (ricecdf (x, 1, 1), p, 1e-14) %!assert (ricecdf (x, 1, 1, "upper"), 1 - p, 1e-14) ## Test input validation %!error ricecdf () %!error ricecdf (1) %!error ricecdf (1, 2) %!error ricecdf (1, 2, 3, "uper") %!error ricecdf (1, 2, 3, 4) %!error ... %! ricecdf (ones (3), ones (2), ones (2)) %!error ... %! ricecdf (ones (2), ones (3), ones (2)) %!error ... %! ricecdf (ones (2), ones (2), ones (3)) %!error ricecdf (i, 2, 3) %!error ricecdf (2, i, 3) %!error ricecdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/riceinv.m000066400000000000000000000112571475240274700217610ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} riceinv (@var{p}, @var{s}, @var{sigma}) ## ## Inverse of the Rician distribution (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) ## of the Rician distribution with non-centrality (distance) parameter @var{s} ## and scale parameter @var{sigma}. The size of @var{x} is the common size of ## @var{x}, @var{s}, and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{ricecdf, ricepdf, ricernd, ricefit, ricelike, ricestat} ## @end deftypefn function x = riceinv (p, s, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("riceinv: function called with too few input arguments."); endif ## Check for common size of P, S, and B if (! isscalar (p) || ! isscalar (s) || ! isscalar (sigma)) [retval, p, s, sigma] = common_size (p, s, sigma); if (retval > 0) error ("riceinv: P, S, and B must be of common size or scalars."); endif endif ## Check for P, S, and B being reals if (iscomplex (p) || iscomplex (s) || iscomplex (sigma)) error ("riceinv: P, S, and B must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (s, "single") || isa (sigma, "single")) x = zeros (size (p), "single"); else x = zeros (size (p)); endif k = s < 0 | sigma <= 0 | p < 0 | p > 1 | ... isnan (p) | isnan (s) | isnan (sigma); x(k) = NaN; k = ! k; x(k) = sigma(k) .* sqrt (ncx2inv (p(k), 2, (s(k) ./ sigma(k)) .^ 2)); endfunction %!demo %! ## Plot various iCDFs from the Rician distribution %! p = 0.001:0.001:0.999; %! x1 = riceinv (p, 0, 1); %! x2 = riceinv (p, 0.5, 1); %! x3 = riceinv (p, 1, 1); %! x4 = riceinv (p, 2, 1); %! x5 = riceinv (p, 4, 1); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-m", p, x5, "-k") %! grid on %! legend ({"s = 0, σ = 1", "s = 0.5, σ = 1", "s = 1, σ = 1", ... %! "s = 2, σ = 1", "s = 4, σ = 1"}, "location", "northwest") %! title ("Rician iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.75 1 2]; %!assert (riceinv (p, ones (1,5), 2*ones (1,5)), [NaN 0 3.5354 Inf NaN], 1e-4) %!assert (riceinv (p, 1, 2*ones (1,5)), [NaN 0 3.5354 Inf NaN], 1e-4) %!assert (riceinv (p, ones (1,5), 2), [NaN 0 3.5354 Inf NaN], 1e-4) %!assert (riceinv (p, [1 0 NaN 1 1], 2), [NaN 0 NaN Inf NaN]) %!assert (riceinv (p, 1, 2*[1 0 NaN 1 1]), [NaN NaN NaN Inf NaN]) %!assert (riceinv ([p(1:2) NaN p(4:5)], 1, 2), [NaN 0 NaN Inf NaN]) ## Test class of input preserved %!assert (riceinv ([p, NaN], 1, 2), [NaN 0 3.5354 Inf NaN NaN], 1e-4) %!assert (riceinv (single ([p, NaN]), 1, 2), ... %! single ([NaN 0 3.5354 Inf NaN NaN]), 1e-4) %!assert (riceinv ([p, NaN], single (1), 2), ... %! single ([NaN 0 3.5354 Inf NaN NaN]), 1e-4) %!assert (riceinv ([p, NaN], 1, single (2)), ... %! single ([NaN 0 3.5354 Inf NaN NaN]), 1e-4) ## Test input validation %!error riceinv () %!error riceinv (1) %!error riceinv (1,2) %!error riceinv (1,2,3,4) %!error ... %! riceinv (ones (3), ones (2), ones (2)) %!error ... %! riceinv (ones (2), ones (3), ones (2)) %!error ... %! riceinv (ones (2), ones (2), ones (3)) %!error riceinv (i, 2, 2) %!error riceinv (2, i, 2) %!error riceinv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/ricepdf.m000066400000000000000000000114661475240274700217400ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} ricepdf (@var{x}, @var{s}, @var{sigma}) ## ## Rician probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Rician distribution with non-centrality (distance) parameter @var{s} ## and scale parameter @var{sigma}. The size of @var{y} is the common size of ## @var{x}, @var{s}, and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{ricecdf, riceinv, ricernd, ricefit, ricelike, ricestat} ## @end deftypefn function y = ricepdf (x, s, sigma) ## Check for valid number of input arguments if (nargin < 3) error ("ricepdf: function called with too few input arguments."); endif ## Check for common size of X, S, and SIGMA if (! isscalar (x) || ! isscalar (s) || ! isscalar (sigma)) [retval, x, s, sigma] = common_size (x, s, sigma); if (retval > 0) error ("ricepdf: X, S, and SIGMA must be of common size or scalars."); endif endif ## Check for X, S, and SIGMA being reals if (iscomplex (x) || iscomplex (s) || iscomplex (sigma)) error ("ricepdf: X, S, and SIGMA must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (s, "single") || isa (sigma, "single")); y = zeros (size (x), "single"); else y = zeros (size (x)); endif k = s < 0 | sigma <= 0 | x < 0 | isnan (x) | isnan (s) | isnan (sigma); y(k) = NaN; k = ! k; ## Do the math x_k = x(k); n_k = s(k); s_sq = sigma(k) .^ 2; x_s2 = x_k ./ s_sq; xnsq = (x_k .^ 2 + n_k .^ 2) ./ (2 .* s_sq); epxt = xnsq - x_s2 .* n_k; term = exp (-epxt); y(k) = x_s2 .* term .* besseli (0, x_s2 .* n_k, 1); ## Fix arithmetic overflow due to exponent y(epxt > (log(realmax(class(y))))) = 0; ## Fix x < 0 -> 0 y(x < 0) = 0; endfunction %!demo %! ## Plot various PDFs from the Rician distribution %! x = 0:0.01:8; %! y1 = ricepdf (x, 0, 1); %! y2 = ricepdf (x, 0.5, 1); %! y3 = ricepdf (x, 1, 1); %! y4 = ricepdf (x, 2, 1); %! y5 = ricepdf (x, 4, 1); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-m", x, y5, "-k") %! grid on %! ylim ([0, 0.65]) %! xlim ([0, 8]) %! legend ({"s = 0, σ = 1", "s = 0.5, σ = 1", "s = 1, σ = 1", ... %! "s = 2, σ = 1", "s = 4, σ = 1"}, "location", "northeast") %! title ("Rician PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2]; %! y = [0 0 0.1073 0.1978 0.2846]; %!assert (ricepdf (x, ones (1, 5), 2 * ones (1, 5)), y, 1e-4) %!assert (ricepdf (x, 1, 2 * ones (1, 5)), y, 1e-4) %!assert (ricepdf (x, ones (1, 5), 2), y, 1e-4) %!assert (ricepdf (x, [0 NaN 1 1 1], 2), [0 NaN y(3:5)], 1e-4) %!assert (ricepdf (x, 1, 2 * [0 NaN 1 1 1]), [0 NaN y(3:5)], 1e-4) %!assert (ricepdf ([x, NaN], 1, 2), [y, NaN], 1e-4) ## Test class of input preserved %!assert (ricepdf (single ([x, NaN]), 1, 2), single ([y, NaN]), 1e-4) %!assert (ricepdf ([x, NaN], single (1), 2), single ([y, NaN]), 1e-4) %!assert (ricepdf ([x, NaN], 1, single (2)), single ([y, NaN]), 1e-4) ## Test input validation %!error ricepdf () %!error ricepdf (1) %!error ricepdf (1,2) %!error ricepdf (1,2,3,4) %!error ... %! ricepdf (ones (3), ones (2), ones (2)) %!error ... %! ricepdf (ones (2), ones (3), ones (2)) %!error ... %! ricepdf (ones (2), ones (2), ones (3)) %!error ricepdf (i, 2, 2) %!error ricepdf (2, i, 2) %!error ricepdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/ricernd.m000066400000000000000000000162761475240274700217560ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} ricernd (@var{s}, @var{sigma}) ## @deftypefnx {statistics} {@var{r} =} ricernd (@var{s}, @var{sigma}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} ricernd (@var{s}, @var{sigma}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} ricernd (@var{s}, @var{sigma}, [@var{sz}]) ## ## Random arrays from the Rician distribution. ## ## @code{@var{r} = ricernd (@var{s}, @var{sigma})} returns an array of random ## numbers chosen from the Rician distribution with noncentrality parameter ## @var{s} and scale parameter @var{sigma}. The size of @var{r} is the common ## size of @var{s} and @var{sigma}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## When called with a single size argument, @code{ricernd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{ricecdf, riceinv, ricepdf, ricefit, ricelike, ricestat} ## @end deftypefn function r = ricernd (s, sigma, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("ricernd: function called with too few input arguments."); endif ## Check for common size of S and SIGMA if (! isscalar (s) || ! isscalar (sigma)) [retval, s, sigma] = common_size (s, sigma); if (retval > 0) error ("ricernd: S and SIGMA must be of common size or scalars."); endif endif ## Check for S and SIGMA being reals if (iscomplex (s) || iscomplex (sigma)) error ("ricernd: S and SIGMA must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (s); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["ricernd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("ricernd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (s) && ! isequal (size (s), sz)) error ("ricernd: S and SIGMA must be scalars or of size SZ."); endif ## Check for class type if (isa (s, "single") || isa (sigma, "single")) cls = "single"; else cls = "double"; endif ## Return NaNs for out of range values of S and SIGMA s(s < 0) = NaN; sigma(sigma <= 0) = NaN; ## Force S and SIGMA into the same size as SZ (if necessary) if (isscalar (s)) s = repmat (s, sz); endif if (isscalar (sigma)) sigma = repmat (sigma, sz); endif ## Generate random sample from the Rician distribution r = sigma .* sqrt (ncx2rnd (2, (s ./ sigma) .^ 2, sz)); ## Cast to appropriate class r = cast (r, cls); endfunction ## Test output %!assert (size (ricernd (2, 1/2)), [1, 1]) %!assert (size (ricernd (2 * ones (2, 1), 1/2)), [2, 1]) %!assert (size (ricernd (2 * ones (2, 2), 1/2)), [2, 2]) %!assert (size (ricernd (2, 1/2 * ones (2, 1))), [2, 1]) %!assert (size (ricernd (1, 1/2 * ones (2, 2))), [2, 2]) %!assert (size (ricernd (ones (2, 1), 1)), [2, 1]) %!assert (size (ricernd (ones (2, 2), 1)), [2, 2]) %!assert (size (ricernd (2, 1/2, 3)), [3, 3]) %!assert (size (ricernd (1, 1, [4, 1])), [4, 1]) %!assert (size (ricernd (1, 1, 4, 1)), [4, 1]) %!assert (size (ricernd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (ricernd (1, 1, 0, 1)), [0, 1]) %!assert (size (ricernd (1, 1, 1, 0)), [1, 0]) %!assert (size (ricernd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (ricernd (1, 1)), "double") %!assert (class (ricernd (1, single (0))), "single") %!assert (class (ricernd (1, single ([0, 0]))), "single") %!assert (class (ricernd (1, single (1), 2)), "single") %!assert (class (ricernd (1, single ([1, 1]), 1, 2)), "single") %!assert (class (ricernd (single (1), 1, 2)), "single") %!assert (class (ricernd (single ([1, 1]), 1, 1, 2)), "single") ## Test input validation %!error ricernd () %!error ricernd (1) %!error ... %! ricernd (ones (3), ones (2)) %!error ... %! ricernd (ones (2), ones (3)) %!error ricernd (i, 2) %!error ricernd (1, i) %!error ... %! ricernd (1, 1/2, -1) %!error ... %! ricernd (1, 1/2, 1.2) %!error ... %! ricernd (1, 1/2, ones (2)) %!error ... %! ricernd (1, 1/2, [2 -1 2]) %!error ... %! ricernd (1, 1/2, [2 0 2.5]) %!error ... %! ricernd (1, 1/2, 2, -1, 5) %!error ... %! ricernd (1, 1/2, 2, 1.5, 5) %!error ... %! ricernd (2, 1/2 * ones (2), 3) %!error ... %! ricernd (2, 1/2 * ones (2), [3, 2]) %!error ... %! ricernd (2, 1/2 * ones (2), 3, 2) %!error ... %! ricernd (2 * ones (2), 1/2, 3) %!error ... %! ricernd (2 * ones (2), 1/2, [3, 2]) %!error ... %! ricernd (2 * ones (2), 1/2, 3, 2) statistics-release-1.7.3/inst/dist_fun/tcdf.m000066400000000000000000000206101475240274700212330ustar00rootroot00000000000000## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2013-2017 Julien Bect ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} tcdf (@var{x}, @var{df}) ## @deftypefnx {statistics} {@var{p} =} tcdf (@var{x}, @var{df}, @qcode{"upper"}) ## ## Student's T cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Student's T distribution with @var{df} degrees of freedom. The ## size of @var{p} is the common size of @var{x} and @var{df}. A scalar input ## functions as a constant matrix of the same size as the other input. ## ## @code{@var{p} = tcdf (@var{x}, @var{df}, "upper")} computes the upper tail ## probability of the Student's T distribution with @var{df} degrees of freedom, ## at the values in @var{x}. ## ## Further information about the Student's T distribution can be found at ## @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution} ## ## @seealso{tinv, tpdf, trnd, tstat} ## @end deftypefn function p = tcdf (x, df, uflag) ## Check for valid number of input arguments if (nargin < 2) error ("tcdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) x = -x; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("tcdf: invalid argument for upper tail."); endif ## Check for common size of X and DF if (! isscalar (x) || ! isscalar (df)) [err, x, df] = common_size (x, df); if (err > 0) error ("tcdf: X and DF must be of common size or scalars."); endif endif ## Check for X and DF being reals if (iscomplex (x) || iscomplex (df)) error ("tcdf: X and DF must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Check for NaNs or DF <= 0 is_nan = isnan (x) | ! (df > 0); p(is_nan) = NaN; ## Check for Inf where DF > 0 (for -Inf is already 0) k = (x == Inf) & (df > 0); p(k) = 1; ## Find finite values in X where 0 < DF < Inf k = isfinite (x) & (df > 0) & (df < Inf); ## Process more efficiently small positive integer DF up to 1e4 ks = k & (fix (df) == df) & (df <= 1e4); if any (ks(:)) if (isscalar (df)) p(ks) = tcdf_integer_df (x(ks), df); return else vu = unique (df(ks)); for i = 1:numel (vu) ki = k & (df == vu(i)); p(ki) = tcdf_integer_df (x(ki), vu(i)); endfor endif endif ## Proccess remaining values for DF (non-integers and > 1e4) except DF == Inf k &= ! ks; ## Distinguish between small and big abs(x) xx = x .^ 2; x_big_abs = (xx > df); ## Deal with the case "abs(x) big" kk = k & x_big_abs; if (isscalar (df)) p(kk) = betainc (df ./ (df + xx(kk)), df/2, 1/2) / 2; else p(kk) = betainc (df(kk) ./ (df(kk) + xx(kk)), df(kk)/2, 1/2) / 2; endif ## Deal with the case "abs(x) small" kk = k & ! x_big_abs; if (isscalar (df)) p(kk) = 0.5 * (1 - betainc (xx(kk) ./ (df + xx(kk)), 1/2, df/2)); else p(kk) = 0.5 * (1 - betainc (xx(kk) ./ (df(kk) + xx(kk)), 1/2, df(kk)/2)); endif ## For x > 0, F(x) = 1 - F(-|x|). k &= (x > 0); if (any (k(:))) p(k) = 1 - p(k); endif ## Special case for Cauchy distribution ## Use acot(-x) instead of the usual (atan x)/pi + 0.5 to avoid roundoff error xpos = (x > 0); c = (df == 1); p(c) = xpos(c) + acot (-x(c)) / pi; ## Special case for DF == Inf k = isfinite (x) & (df == Inf); p(k) = normcdf (x(k)); ## Make the result exact for the median p(x == 0 & ! is_nan) = 0.5; endfunction ## Compute the t distribution CDF efficiently (without calling betainc) ## for small positive integer DF up to 1e4 function p = tcdf_integer_df (x, df) if (df == 1) p = 0.5 + atan(x)/pi; elseif (df == 2) p = 0.5 + x ./ (2 * sqrt(2 + x .^ 2)); else xs = x ./ sqrt(df); xxf = 1 ./ (1 + xs .^ 2); u = s = 1; if mod (df, 2) ## odd DF m = (df - 1) / 2; for i = 2:m u .*= (1 - 1/(2*i - 1)) .* xxf; s += u; endfor p = 0.5 + (xs .* xxf .* s + atan(xs)) / pi; else ## even DF m = df / 2; for i = 1:(m - 1) u .*= (1 - 1/(2*i)) .* xxf; s += u; endfor p = 0.5 + (xs .* sqrt(xxf) .* s) / 2; endif endif endfunction %!demo %! ## Plot various CDFs from the Student's T distribution %! x = -5:0.01:5; %! p1 = tcdf (x, 1); %! p2 = tcdf (x, 2); %! p3 = tcdf (x, 5); %! p4 = tcdf (x, Inf); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-m") %! grid on %! xlim ([-5, 5]) %! ylim ([0, 1]) %! legend ({"df = 1", "df = 2", ... %! "df = 5", 'df = \infty'}, "location", "southeast") %! title ("Student's T CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x,y %! x = [-Inf 0 1 Inf]; %! y = [0 1/2 3/4 1]; %!assert (tcdf (x, ones (1,4)), y, eps) %!assert (tcdf (x, 1), y, eps) %!assert (tcdf (x, [0 1 NaN 1]), [NaN 1/2 NaN 1], eps) %!assert (tcdf ([x(1:2) NaN x(4)], 1), [y(1:2) NaN y(4)], eps) %!assert (tcdf (2, 3, "upper"), 0.0697, 1e-4) %!assert (tcdf (205, 5, "upper"), 2.6206e-11, 1e-14) ## Test class of input preserved %!assert (tcdf ([x, NaN], 1), [y, NaN], eps) %!assert (tcdf (single ([x, NaN]), 1), single ([y, NaN]), eps ("single")) %!assert (tcdf ([x, NaN], single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error tcdf () %!error tcdf (1) %!error tcdf (1, 2, "uper") %!error tcdf (1, 2, 3) %!error ... %! tcdf (ones (3), ones (2)) %!error ... %! tcdf (ones (3), ones (2)) %!error ... %! tcdf (ones (3), ones (2), "upper") %!error tcdf (i, 2) %!error tcdf (2, i) ## Check some reference values %!shared tol_rel %! tol_rel = 10 * eps; ## check accuracy for small positive values %!assert (tcdf (10^(-10), 2.5), 0.50000000003618087, -tol_rel) %!assert (tcdf (10^(-11), 2.5), 0.50000000000361809, -tol_rel) %!assert (tcdf (10^(-12), 2.5), 0.50000000000036181, -tol_rel) %!assert (tcdf (10^(-13), 2.5), 0.50000000000003618, -tol_rel) %!assert (tcdf (10^(-14), 2.5), 0.50000000000000362, -tol_rel) %!assert (tcdf (10^(-15), 2.5), 0.50000000000000036, -tol_rel) %!assert (tcdf (10^(-16), 2.5), 0.50000000000000004, -tol_rel) ## check accuracy for large negative values %!assert (tcdf (-10^1, 2.5), 2.2207478836537124e-03, -tol_rel) %!assert (tcdf (-10^2, 2.5), 7.1916492116661878e-06, -tol_rel) %!assert (tcdf (-10^3, 2.5), 2.2747463948307452e-08, -tol_rel) %!assert (tcdf (-10^4, 2.5), 7.1933970159922115e-11, -tol_rel) %!assert (tcdf (-10^5, 2.5), 2.2747519231756221e-13, -tol_rel) ## # Reference values obtained using Python 2.7.4 and mpmath 0.17 ## ## from mpmath import * ## ## mp.dps = 100 ## ## def F(x_in, nu_in): ## x = mpf(x_in); ## nu = mpf(nu_in); ## t = nu / (nu + x*x) ## a = nu / 2 ## b = mpf(0.5) ## F = betainc(a, b, 0, t, regularized=True) / 2 ## if (x > 0): ## F = 1 - F ## return F ## ## nu = 2.5 ## ## for i in range(1, 6): ## x = - power(mpf(10), mpf(i)) ## print "%%!assert (tcdf (-10^%d, 2.5), %s, -eps)" \ ## % (i, nstr(F(x, nu), 17)) ## ## for i in range(10, 17): ## x = power(mpf(10), -mpf(i)) ## print "%%!assert (tcdf (10^(-%d), 2.5), %s, -eps)" \ ## % (i, nstr(F(x, nu), 17)) statistics-release-1.7.3/inst/dist_fun/tinv.m000066400000000000000000000112331475240274700212740ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} tinv (@var{p}, @var{df}) ## ## Inverse of the Student's T cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the Student's T distribution with @var{df} degrees of freedom. The size of ## @var{x} is the common size of @var{x} and @var{df}. A scalar input functions ## as a constant matrix of the same size as the other input. ## ## This function is analogous to looking in a table for the t-value of a ## single-tailed distribution. For very large @var{df} (>10000), the inverse of ## the standard normal distribution is used. ## ## Further information about the Student's T distribution can be found at ## @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution} ## ## @seealso{tcdf, tpdf, trnd, tstat} ## @end deftypefn function x = tinv (p, df) ## Check for valid number of input arguments if (nargin < 2) error ("tinv: function called with too few input arguments."); endif ## Check for common size of P and DF if (! isscalar (p) || ! isscalar (df)) [retval, p, df] = common_size (p, df); if (retval > 0) error ("tinv: P and DF must be of common size or scalars."); endif endif ## Check for P and DF being reals if (iscomplex (p) || iscomplex (df)) error ("tinv: P and DF must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (df, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif k = (p == 0) & (df > 0); x(k) = -Inf; k = (p == 1) & (df > 0); x(k) = Inf; if (isscalar (df)) k = (p > 0) & (p < 1); if ((df > 0) && (df < 10000)) x(k) = (sign (p(k) - 1/2) .* sqrt (df * (1 ./ betainv (2*min (p(k), 1 - p(k)), df/2, 1/2) - 1))); elseif (df >= 10000) ## For large df, use the quantiles of the standard normal x(k) = -sqrt (2) * erfcinv (2 * p(k)); endif else k = (p > 0) & (p < 1) & (df > 0) & (df < 10000); x(k) = (sign (p(k) - 1/2) .* sqrt (df(k) .* (1 ./ betainv (2*min (p(k), 1 - p(k)), df(k)/2, 1/2) - 1))); ## For large df, use the quantiles of the standard normal k = (p > 0) & (p < 1) & (df >= 10000); x(k) = -sqrt (2) * erfcinv (2 * p(k)); endif endfunction %!demo %! ## Plot various iCDFs from the Student's T distribution %! p = 0.001:0.001:0.999; %! x1 = tinv (p, 1); %! x2 = tinv (p, 2); %! x3 = tinv (p, 5); %! x4 = tinv (p, Inf); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-m") %! grid on %! xlim ([0, 1]) %! ylim ([-5, 5]) %! legend ({"df = 1", "df = 2", ... %! "df = 5", 'df = \infty'}, "location", "northwest") %! title ("Student's T iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (tinv (p, ones (1,5)), [NaN -Inf 0 Inf NaN]) %!assert (tinv (p, 1), [NaN -Inf 0 Inf NaN], eps) %!assert (tinv (p, [1 0 NaN 1 1]), [NaN NaN NaN Inf NaN], eps) %!assert (tinv ([p(1:2) NaN p(4:5)], 1), [NaN -Inf NaN Inf NaN]) ## Test class of input preserved %!assert (tinv ([p, NaN], 1), [NaN -Inf 0 Inf NaN NaN], eps) %!assert (tinv (single ([p, NaN]), 1), single ([NaN -Inf 0 Inf NaN NaN]), eps ("single")) %!assert (tinv ([p, NaN], single (1)), single ([NaN -Inf 0 Inf NaN NaN]), eps ("single")) ## Test input validation %!error tinv () %!error tinv (1) %!error ... %! tinv (ones (3), ones (2)) %!error ... %! tinv (ones (2), ones (3)) %!error tinv (i, 2) %!error tinv (2, i) statistics-release-1.7.3/inst/dist_fun/tlscdf.m000066400000000000000000000143001475240274700215710ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} tlscdf (@var{x}, @var{mu}, @var{sigma}, @var{nu}) ## @deftypefnx {statistics} {@var{p} =} tlscdf (@var{x}, @var{mu}, @var{sigma}, @var{nu}, @qcode{"upper"}) ## ## Location-scale Student's T cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the location-scale Student's T distribution with location parameter ## @var{mu}, scale parameter @var{sigma}, and @var{nu} degrees of freedom. The ## size of @var{p} is the common size of @var{x}, @var{mu}, @var{sigma}, and ## @var{nu}. A scalar input functions as a constant matrix of the same size as ## the other inputs. ## ## @code{@var{p} = tlscdf (@var{x}, @var{mu}, @var{sigma}, @var{nu}, "upper")} ## computes the upper tail probability of the location-scale Student's T ## distribution with parameters @var{mu}, @var{sigma}, and @var{nu}, at the ## values in @var{x}. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlsinv, tlspdf, tlsrnd, tlsfit, tlslike, tlsstat} ## @end deftypefn function p = tlscdf (x, mu, sigma, nu, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("tlscdf: function called with too few input arguments."); endif ## Check for "upper" flag upper = false; if (nargin > 4 && strcmpi (uflag, "upper")) upper = true; elseif (nargin > 4 && ! strcmpi (uflag, "upper")) error ("tlscdf: invalid argument for upper tail."); endif ## Check for common size of X, MU, SIGMA, and NU if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma) || ! isscalar (nu)) [err, x, mu, sigma, nu] = common_size (x, mu, sigma, nu); if (err > 0) error ("tlscdf: X, MU, SIGMA, and NU must be of common size or scalars."); endif endif ## Check for X, MU, SIGMA, and NU being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma) || iscomplex (nu)) error ("tlscdf: X, MU, SIGMA, and NU must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single") || isa (nu, "single")) cls = "single"; else cls = "double"; endif ## Force invalid SIGMA parameter to NaN sigma(sigma <= 0) = NaN; ## Call tcdf to do the work if (upper) p = tcdf ((x - mu) ./ sigma, nu, "upper"); else p = tcdf ((x - mu) ./ sigma, nu); endif ## Force class type p = cast (p, cls); endfunction %!demo %! ## Plot various CDFs from the location-scale Student's T distribution %! x = -8:0.01:8; %! p1 = tlscdf (x, 0, 1, 1); %! p2 = tlscdf (x, 0, 2, 2); %! p3 = tlscdf (x, 3, 2, 5); %! p4 = tlscdf (x, -1, 3, Inf); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-m") %! grid on %! xlim ([-8, 8]) %! ylim ([0, 1]) %! legend ({"mu = 0, sigma = 1, nu = 1", "mu = 0, sigma = 2, nu = 2", ... %! "mu = 3, sigma = 2, nu = 5", 'mu = -1, sigma = 3, nu = \infty'}, ... %! "location", "northwest") %! title ("Location-scale Student's T CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x,y %! x = [-Inf 0 1 Inf]; %! y = [0 1/2 3/4 1]; %!assert (tlscdf (x, 0, 1, ones (1,4)), y, eps) %!assert (tlscdf (x, 0, 1, 1), y, eps) %!assert (tlscdf (x, 0, 1, [0 1 NaN 1]), [NaN 1/2 NaN 1], eps) %!assert (tlscdf ([x(1:2) NaN x(4)], 0, 1, 1), [y(1:2) NaN y(4)], eps) %!assert (tlscdf (2, 0, 1, 3, "upper"), 0.0697, 1e-4) %!assert (tlscdf (205, 0, 1, 5, "upper"), 2.6206e-11, 1e-14) ## Test class of input preserved %!assert (tlscdf ([x, NaN], 0, 1, 1), [y, NaN], eps) %!assert (tlscdf (single ([x, NaN]), 0, 1, 1), single ([y, NaN]), eps ("single")) %!assert (tlscdf ([x, NaN], single (0), 1, 1), single ([y, NaN]), eps ("single")) %!assert (tlscdf ([x, NaN], 0, single (1), 1), single ([y, NaN]), eps ("single")) %!assert (tlscdf ([x, NaN], 0, 1, single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error tlscdf () %!error tlscdf (1) %!error tlscdf (1, 2) %!error tlscdf (1, 2, 3) %!error tlscdf (1, 2, 3, 4, "uper") %!error tlscdf (1, 2, 3, 4, 5) %!error ... %! tlscdf (ones (3), ones (2), 1, 1) %!error ... %! tlscdf (ones (3), 1, ones (2), 1) %!error ... %! tlscdf (ones (3), 1, 1, ones (2)) %!error ... %! tlscdf (ones (3), ones (2), 1, 1, "upper") %!error ... %! tlscdf (ones (3), 1, ones (2), 1, "upper") %!error ... %! tlscdf (ones (3), 1, 1, ones (2), "upper") %!error tlscdf (i, 2, 1, 1) %!error tlscdf (2, i, 1, 1) %!error tlscdf (2, 1, i, 1) %!error tlscdf (2, 1, 1, i) statistics-release-1.7.3/inst/dist_fun/tlsinv.m000066400000000000000000000116201475240274700216330ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} tlsinv (@var{p}, @var{mu}, @var{sigma}, @var{nu}) ## ## Inverse of the location-scale Student's T cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the location-scale Student's T distribution with location parameter @var{mu}, ## scale parameter @var{sigma}, and @var{nu} degrees of freedom. The size of ## @var{x} is the common size of @var{p}, @var{mu}, @var{sigma}, and @var{nu}. ## A scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlscdf, tlspdf, tlsrnd, tlsfit, tlslike, tlsstat} ## @end deftypefn function x = tlsinv (p, mu, sigma, nu) ## Check for valid number of input arguments if (nargin < 4) error ("tlsinv: function called with too few input arguments."); endif ## Check for common size of P, MU, SIGMA, and NU if (! isscalar (p) || ! isscalar (mu) || ! isscalar (sigma) || ! isscalar (nu)) [retval, p, mu, sigma, nu] = common_size (p, mu, sigma, nu); if (retval > 0) error ("tlsinv: P, MU, SIGMA, and NU must be of common size or scalars."); endif endif ## Check for P, MU, SIGMA, and NU being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (sigma) || iscomplex (nu)) error ("tlsinv: P, MU, SIGMA, and NU must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (sigma, "single") || isa (nu, "single")) cls = "single"; else cls = "double"; endif ## Force invalid SIGMA parameter to NaN sigma(sigma <= 0) = NaN; ## Call tinv to do the work x = tinv (p, nu) .* sigma + mu; ## Force class type x = cast (x, cls); endfunction %!demo %! ## Plot various iCDFs from the location-scale Student's T distribution %! p = 0.001:0.001:0.999; %! x1 = tlsinv (p, 0, 1, 1); %! x2 = tlsinv (p, 0, 2, 2); %! x3 = tlsinv (p, 3, 2, 5); %! x4 = tlsinv (p, -1, 3, Inf); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-m") %! grid on %! xlim ([0, 1]) %! ylim ([-8, 8]) %! legend ({"mu = 0, sigma = 1, nu = 1", "mu = 0, sigma = 2, nu = 2", ... %! "mu = 3, sigma = 2, nu = 5", 'mu = -1, sigma = 3, nu = \infty'}, ... %! "location", "southeast") %! title ("Location-scale Student's T iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (tlsinv (p, 0, 1, ones (1,5)), [NaN -Inf 0 Inf NaN]) %!assert (tlsinv (p, 0, 1, 1), [NaN -Inf 0 Inf NaN], eps) %!assert (tlsinv (p, 0, 1, [1 0 NaN 1 1]), [NaN NaN NaN Inf NaN], eps) %!assert (tlsinv ([p(1:2) NaN p(4:5)], 0, 1, 1), [NaN -Inf NaN Inf NaN]) ## Test class of input preserved %!assert (class (tlsinv ([p, NaN], 0, 1, 1)), "double") %!assert (class (tlsinv (single ([p, NaN]), 0, 1, 1)), "single") %!assert (class (tlsinv ([p, NaN], single (0), 1, 1)), "single") %!assert (class (tlsinv ([p, NaN], 0, single (1), 1)), "single") %!assert (class (tlsinv ([p, NaN], 0, 1, single (1))), "single") ## Test input validation %!error tlsinv () %!error tlsinv (1) %!error tlsinv (1, 2) %!error tlsinv (1, 2, 3) %!error ... %! tlsinv (ones (3), ones (2), 1, 1) %!error ... %! tlsinv (ones (2), 1, ones (3), 1) %!error ... %! tlsinv (ones (2), 1, 1, ones (3)) %!error tlsinv (i, 2, 3, 4) %!error tlsinv (2, i, 3, 4) %!error tlsinv (2, 2, i, 4) %!error tlsinv (2, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/tlspdf.m000066400000000000000000000120141475240274700216060ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} tlspdf (@var{x}, @var{mu}, @var{sigma}, @var{nu}) ## ## Location-scale Student's T probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the location-scale Student's T distribution with location parameter ## @var{mu}, scale parameter @var{sigma}, and @var{nu} degrees of freedom. The ## size of @var{y} is the common size of @var{x}, @var{mu}, @var{sigma}, and ## @var{nu}. A scalar input functions as a constant matrix of the same size as ## the other inputs. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlscdf, tlsinv, tlsrnd, tlsfit, tlslike, tlsstat} ## @end deftypefn function y = tlspdf (x, mu, sigma, nu) ## Check for valid number of input arguments if (nargin < 4) error ("tlspdf: function called with too few input arguments."); endif ## Check for common size of X, MU, SIGMA, and NU if (! isscalar (x) || ! isscalar (mu) || ! isscalar (sigma) || ! isscalar (nu)) [err, x, mu, sigma, nu] = common_size (x, mu, sigma, nu); if (err > 0) error ("tlspdf: X, MU, SIGMA, and NU must be of common size or scalars."); endif endif ## Check for X, MU, SIGMA, and NU being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (sigma) || iscomplex (nu)) error ("tlspdf: X, MU, SIGMA, and NU must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (sigma, "single") || isa (nu, "single")) cls = "single"; else cls = "double"; endif ## Force invalid SIGMA parameter to NaN sigma(sigma <= 0) = NaN; ## Call tpdf to do the work y = tpdf ((x - mu) ./ sigma, nu) ./ sigma; ## Force class type y = cast (y, cls); endfunction %!demo %! ## Plot various PDFs from the Student's T distribution %! x = -8:0.01:8; %! y1 = tlspdf (x, 0, 1, 1); %! y2 = tlspdf (x, 0, 2, 2); %! y3 = tlspdf (x, 3, 2, 5); %! y4 = tlspdf (x, -1, 3, Inf); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-m") %! grid on %! xlim ([-8, 8]) %! ylim ([0, 0.41]) %! legend ({"mu = 0, sigma = 1, nu = 1", "mu = 0, sigma = 2, nu = 2", ... %! "mu = 3, sigma = 2, nu = 5", 'mu = -1, sigma = 3, nu = \infty'}, ... %! "location", "northwest") %! title ("Location-scale Student's T PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!test %! x = rand (10,1); %! y = 1./(pi * (1 + x.^2)); %! assert (tlspdf (x, 0, 1, 1), y, 5*eps); %! assert (tlspdf (x+5, 5, 1, 1), y, 5*eps); %! assert (tlspdf (x.*2, 0, 2, 1), y./2, 5*eps); %!shared x, y %! x = [-Inf 0 0.5 1 Inf]; %! y = 1./(pi * (1 + x.^2)); %!assert (tlspdf (x, 0, 1, ones (1,5)), y, eps) %!assert (tlspdf (x, 0, 1, 1), y, eps) %!assert (tlspdf (x, 0, 1, [0 NaN 1 1 1]), [NaN NaN y(3:5)], eps) %!assert (tlspdf (x, 0, 1, Inf), normpdf (x)) ## Test class of input preserved %!assert (class (tlspdf ([x, NaN], 1, 1, 1)), "double") %!assert (class (tlspdf (single ([x, NaN]), 1, 1, 1)), "single") %!assert (class (tlspdf ([x, NaN], single (1), 1, 1)), "single") %!assert (class (tlspdf ([x, NaN], 1, single (1), 1)), "single") %!assert (class (tlspdf ([x, NaN], 1, 1, single (1))), "single") ## Test input validation %!error tlspdf () %!error tlspdf (1) %!error tlspdf (1, 2) %!error tlspdf (1, 2, 3) %!error ... %! tlspdf (ones (3), ones (2), 1, 1) %!error ... %! tlspdf (ones (2), 1, ones (3), 1) %!error ... %! tlspdf (ones (2), 1, 1, ones (3)) %!error tlspdf (i, 2, 1, 1) %!error tlspdf (2, i, 1, 1) %!error tlspdf (2, 1, i, 1) %!error tlspdf (2, 1, 1, i) statistics-release-1.7.3/inst/dist_fun/tlsrnd.m000066400000000000000000000171221475240274700216250ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} tlsrnd (@var{mu}, @var{sigma}, @var{nu}) ## @deftypefnx {statistics} {@var{r} =} tlsrnd (@var{mu}, @var{sigma}, @var{nu}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} tlsrnd (@var{mu}, @var{sigma}, @var{nu}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} tlsrnd (@var{mu}, @var{sigma}, @var{nu}, [@var{sz}]) ## ## Random arrays from the location-scale Student's T distribution. ## ## Return a matrix of random samples from the location-scale Student's T ## distribution with location parameter @var{mu}, scale parameter @var{sigma}, ## and @var{nu} degrees of freedom. ## ## @code{@var{r} = tlsrnd (@var{nu})} returns an array of random numbers chosen ## from the location-scale Student's T distribution with location parameter ## @var{mu}, scale parameter @var{sigma}, and @var{nu} degrees of freedom. The ## size of @var{r} is the common size of @var{mu}, @var{sigma}, and @var{nu}. A ## scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## When called with a single size argument, @code{tlsrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlscdf, tlsinv, tlspdf, tlsfit, tlslike, tlsstat} ## @end deftypefn function r = tlsrnd (mu, sigma, nu, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("tlsrnd: function called with too few input arguments."); endif ## Check for common size of MU, SIGMA, and NU if (! isscalar (mu) || ! isscalar (sigma) || ! isscalar (nu)) [retval, mu, sigma, nu] = common_size (mu, sigma, nu); if (retval > 0) error ("tlsrnd: MU, SIGMA, and NU must be of common size or scalars."); endif endif ## Check for NU being real if (iscomplex (mu) || iscomplex (sigma) || iscomplex (nu)) error ("tlsrnd: MU, SIGMA, and NU must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (nu); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["tlsrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("tlsrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (nu) && ! isequal (size (nu), sz)) error ("tlsrnd: MU, SIGMA, and NU must be scalar or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (sigma, "single") || isa (nu, "single")) cls = "single"; else cls = "double"; endif ## Call trnd to do the work r = mu + sigma .* trnd (nu, sz); ## Force class type r = cast (r, cls); endfunction ## Test output %!assert (size (tlsrnd (1, 2, 3)), [1, 1]) %!assert (size (tlsrnd (ones (2,1), 2, 3)), [2, 1]) %!assert (size (tlsrnd (ones (2,2), 2, 3)), [2, 2]) %!assert (size (tlsrnd (1, 2, 3, 3)), [3, 3]) %!assert (size (tlsrnd (1, 2, 3, [4 1])), [4, 1]) %!assert (size (tlsrnd (1, 2, 3, 4, 1)), [4, 1]) %!assert (size (tlsrnd (1, 2, 3, 4, 1)), [4, 1]) %!assert (size (tlsrnd (1, 2, 3, 4, 1, 5)), [4, 1, 5]) %!assert (size (tlsrnd (1, 2, 3, 0, 1)), [0, 1]) %!assert (size (tlsrnd (1, 2, 3, 1, 0)), [1, 0]) %!assert (size (tlsrnd (1, 2, 3, 1, 2, 0, 5)), [1, 2, 0, 5]) %!assert (tlsrnd (1, 2, 0, 1, 1), NaN) %!assert (tlsrnd (1, 2, [0, 0, 0], [1, 3]), [NaN, NaN, NaN]) ## Test class of input preserved %!assert (class (tlsrnd (1, 2, 3)), "double") %!assert (class (tlsrnd (single (1), 2, 3)), "single") %!assert (class (tlsrnd (single ([1, 1]), 2, 3)), "single") %!assert (class (tlsrnd (1, single (2), 3)), "single") %!assert (class (tlsrnd (1, single ([2, 2]), 3)), "single") %!assert (class (tlsrnd (1, 2, single (3))), "single") %!assert (class (tlsrnd (1, 2, single ([3, 3]))), "single") ## Test input validation %!error tlsrnd () %!error tlsrnd (1) %!error tlsrnd (1, 2) %!error ... %! tlsrnd (ones (3), ones (2), 1) %!error ... %! tlsrnd (ones (2), 1, ones (3)) %!error ... %! tlsrnd (1, ones (2), ones (3)) %!error tlsrnd (i, 2, 3) %!error tlsrnd (1, i, 3) %!error tlsrnd (1, 2, i) %!error ... %! tlsrnd (1, 2, 3, -1) %!error ... %! tlsrnd (1, 2, 3, 1.2) %!error ... %! tlsrnd (1, 2, 3, ones (2)) %!error ... %! tlsrnd (1, 2, 3, [2 -1 2]) %!error ... %! tlsrnd (1, 2, 3, [2 0 2.5]) %!error ... %! tlsrnd (ones (2), 2, 3, ones (2)) %!error ... %! tlsrnd (1, 2, 3, 2, -1, 5) %!error ... %! tlsrnd (1, 2, 3, 2, 1.5, 5) %!error ... %! tlsrnd (ones (2,2), 2, 3, 3) %!error ... %! tlsrnd (1, ones (2,2), 3, 3) %!error ... %! tlsrnd (1, 2, ones (2,2), 3) %!error ... %! tlsrnd (1, 2, ones (2,2), [3, 3]) %!error ... %! tlsrnd (1, 2, ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/tpdf.m000066400000000000000000000076641475240274700212660ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} tpdf (@var{x}, @var{df}) ## ## Student's T probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Student's T distribution with @var{df} degrees of freedom. The size ## of @var{y} is the common size of @var{x} and @var{df}. A scalar input ## functions as a constant matrix of the same size as the other input. ## ## Further information about the Student's T distribution can be found at ## @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution} ## ## @seealso{tcdf, tpdf, trnd, tstat} ## @end deftypefn function y = tpdf (x, df) ## Check for valid number of input arguments if (nargin < 2) error ("tpdf: function called with too few input arguments."); endif ## Check for common size of X and DF if (! isscalar (x) || ! isscalar (df)) [retval, x, df] = common_size (x, df); if (retval > 0) error ("tpdf: X and DF must be of common size or scalars."); endif endif ## Check for X and DF being reals if (iscomplex (x) || iscomplex (df)) error ("tpdf: X and DF must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (df, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif k = isnan (x) | ! (df > 0); y(k) = NaN; k = isfinite (x) & (df > 0) & (df < Inf); kinf = isfinite (x) & isinf (df); if (any (k)) y(k) = exp (- (df(k) + 1) .* log (1 + x(k) .^ 2 ./ df(k)) / 2) ./ ... (sqrt (df(k)) .* beta (df(k)/2, 1/2)); endif if (any (kinf)) y(kinf) = normpdf (x(kinf)); endif endfunction %!demo %! ## Plot various PDFs from the Student's T distribution %! x = -5:0.01:5; %! y1 = tpdf (x, 1); %! y2 = tpdf (x, 2); %! y3 = tpdf (x, 5); %! y4 = tpdf (x, Inf); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-m") %! grid on %! xlim ([-5, 5]) %! ylim ([0, 0.41]) %! legend ({"df = 1", "df = 2", ... %! "df = 5", 'df = \infty'}, "location", "northeast") %! title ("Student's T PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!test %! x = rand (10,1); %! y = 1./(pi * (1 + x.^2)); %! assert (tpdf (x, 1), y, 5*eps); %!shared x, y %! x = [-Inf 0 0.5 1 Inf]; %! y = 1./(pi * (1 + x.^2)); %!assert (tpdf (x, ones (1,5)), y, eps) %!assert (tpdf (x, 1), y, eps) %!assert (tpdf (x, [0 NaN 1 1 1]), [NaN NaN y(3:5)], eps) %!assert (tpdf (x, Inf), normpdf (x)) ## Test class of input preserved %!assert (tpdf ([x, NaN], 1), [y, NaN], eps) %!assert (tpdf (single ([x, NaN]), 1), single ([y, NaN]), eps ("single")) %!assert (tpdf ([x, NaN], single (1)), single ([y, NaN]), eps ("single")) ## Test input validation %!error tpdf () %!error tpdf (1) %!error ... %! tpdf (ones (3), ones (2)) %!error ... %! tpdf (ones (2), ones (3)) %!error tpdf (i, 2) %!error tpdf (2, i) statistics-release-1.7.3/inst/dist_fun/tricdf.m000066400000000000000000000162221475240274700215720ustar00rootroot00000000000000## Copyright (C) 1997-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} tricdf (@var{x}, @var{a}, @var{b}, @var{c}) ## @deftypefnx {statistics} {@var{p} =} tricdf (@var{x}, @var{a}, @var{b}, @var{c}, @qcode{"upper"}) ## ## Triangular cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the triangular distribution with lower limit parameter @var{a}, peak ## location (mode) parameter @var{b}, and upper limit parameter @var{c}. The ## size of @var{p} is the common size of the input arguments. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## @code{@var{p} = tricdf (@var{x}, @var{a}, @var{b}, @var{c}, "upper")} ## computes the upper tail probability of the triangular distribution with ## parameters @var{a}, @var{b}, and @var{c}, at the values in @var{x}. ## ## Note that the order of the parameter input arguments has been changed after ## statistics version 1.6.3 in order to be MATLAB compatible with the parameters ## used in the TriangularDistribution probability distribution object. More ## specifically, the positions of the parameters @var{b} and @var{c} have been ## swapped. As a result, the naming conventions no longer coinside with those ## used in Wikipedia, in which @math{b} denotes the upper limit and @math{c} ## denotes the mode or peak parameter. ## ## Further information about the triangular distribution can be found at ## @url{https://en.wikipedia.org/wiki/Triangular_distribution} ## ## @seealso{triinv, tripdf, trirnd, tristat} ## @end deftypefn function p = tricdf (x, a, b, c, uflag) ## Check for valid number of input arguments if (nargin < 4) error ("tricdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 4) if (! strcmpi (uflag, "upper")) error ("tricdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of A, B, and C if (! isscalar (x) || ! isscalar (a) || ! isscalar (b) || ! isscalar (c)) [retval, x, a, b, c] = common_size (x, a, b, c); if (retval > 0) error ("tricdf: X, A, B, and C must be of common size or scalars."); endif endif ## Check for X, BETA, and GAMMA being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b) || iscomplex (c)) error ("tricdf: X, A, B, and C must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single") ... || isa (c, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Force NaNs for out of range parameters. k = isnan (x) | ! (a < c) | ! (b >= a) | ! (b <= c); p(k) = NaN; ## Find valid values in parameters and data k = (a < c) & (a <= b) & (b <= c); k1 = (x <= a) & k; k2 = (x > a) & (x <= b) & k; k3 = (x > b) & (x < c) & k; k4 = (x >= c) & k; ## Compute triangular CDF if (uflag) p(k1) = 1; p(k2) = 1 - ((x(k2) - a(k2)) .^ 2) ./ ((c(k2) - a(k2)) .* (b(k2) - a(k2))); p(k3) = ((c(k3) - x(k3)) .^ 2) ./ ((c(k3) - a(k3)) .* (c(k3) - b(k3))); else p(k2) = ((x(k2) - a(k2)) .^ 2) ./ ((c(k2) - a(k2)) .* (b(k2) - a(k2))); p(k3) = 1 - ((c(k3) - x(k3)) .^ 2) ./ ((c(k3) - a(k3)) .* (c(k3) - b(k3))); p(k4) = 1; endif endfunction %!demo %! ## Plot various CDFs from the triangular distribution %! x = 0.001:0.001:10; %! p1 = tricdf (x, 3, 4, 6); %! p2 = tricdf (x, 1, 2, 5); %! p3 = tricdf (x, 2, 3, 9); %! p4 = tricdf (x, 2, 5, 9); %! plot (x, p1, "-b", x, p2, "-g", x, p3, "-r", x, p4, "-c") %! grid on %! xlim ([0, 10]) %! legend ({"a = 3, b = 4, c = 6", "a = 1, b = 2, c = 5", ... %! "a = 2, b = 3, c = 9", "a = 2, b = 5, c = 9"}, ... %! "location", "southeast") %! title ("Triangular CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1, 0, 0.1, 0.5, 0.9, 1, 2] + 1; %! y = [0, 0, 0.02, 0.5, 0.98, 1 1]; %!assert (tricdf (x, ones (1,7), 1.5 * ones (1, 7), 2 * ones (1, 7)), y, eps) %!assert (tricdf (x, 1 * ones (1, 7), 1.5, 2), y, eps) %!assert (tricdf (x, 1 * ones (1, 7), 1.5, 2, "upper"), 1 - y, eps) %!assert (tricdf (x, 1, 1.5, 2 * ones (1, 7)), y, eps) %!assert (tricdf (x, 1, 1.5 * ones (1, 7), 2), y, eps) %!assert (tricdf (x, 1, 1.5, 2), y, eps) %!assert (tricdf (x, [1, 1, NaN, 1, 1, 1, 1], 1.5, 2), ... %! [y(1:2), NaN, y(4:7)], eps) %!assert (tricdf (x, 1, 1.5, 2*[1, 1, NaN, 1, 1, 1, 1]), ... %! [y(1:2), NaN, y(4:7)], eps) %!assert (tricdf (x, 1, 1.5, 2*[1, 1, NaN, 1, 1, 1, 1]), ... %! [y(1:2), NaN, y(4:7)], eps) %!assert (tricdf ([x, NaN], 1, 1.5, 2), [y, NaN], eps) ## Test class of input preserved %!assert (tricdf (single ([x, NaN]), 1, 1.5, 2), ... %! single ([y, NaN]), eps("single")) %!assert (tricdf ([x, NaN], single (1), 1.5, 2), ... %! single ([y, NaN]), eps("single")) %!assert (tricdf ([x, NaN], 1, single (1.5), 2), ... %! single ([y, NaN]), eps("single")) %!assert (tricdf ([x, NaN], 1, 1.5, single (2)), ... %! single ([y, NaN]), eps("single")) ## Test input validation %!error tricdf () %!error tricdf (1) %!error tricdf (1, 2) %!error tricdf (1, 2, 3) %!error ... %! tricdf (1, 2, 3, 4, 5, 6) %!error tricdf (1, 2, 3, 4, "tail") %!error tricdf (1, 2, 3, 4, 5) %!error ... %! tricdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! tricdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! tricdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! tricdf (ones (2), ones (2), ones(2), ones(3)) %!error tricdf (i, 2, 3, 4) %!error tricdf (1, i, 3, 4) %!error tricdf (1, 2, i, 4) %!error tricdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/triinv.m000066400000000000000000000141271475240274700216340ustar00rootroot00000000000000## Copyright (B) 1995-2015 Kurt Hornik ## Copyright (B) 2016 Dag Lyberg ## Copyright (B) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} triinv (@var{p}, @var{a}, @var{b}, @var{c}) ## ## Inverse of the triangular cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the triangular distribution with lower limit parameter @var{a}, peak ## location (mode) parameter @var{b}, and upper limit parameter @var{c}. The ## size of @var{x} is the common size of the input arguments. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Note that the order of the parameter input arguments has been changed after ## statistics version 1.6.3 in order to be MATLAB compatible with the parameters ## used in the TriangularDistribution probability distribution object. More ## specifically, the positions of the parameters @var{b} and @var{c} have been ## swapped. As a result, the naming conventions no longer coinside with those ## used in Wikipedia, in which @math{b} denotes the upper limit and @math{c} ## denotes the mode or peak parameter. ## ## Further information about the triangular distribution can be found at ## @url{https://en.wikipedia.org/wiki/Triangular_distribution} ## ## @seealso{tricdf, tripdf, trirnd, tristat} ## @end deftypefn function x = triinv (p, a, b, c) ## Check for valid number of input arguments if (nargin < 4) error ("triinv: function called with too few input arguments."); endif ## Check for common size of P, A, B, and C if (! isscalar (p) || ! isscalar (a) || ! isscalar (b) || ! isscalar (c)) [retval, p, a, b, c] = common_size (p, a, b, c); if (retval > 0) error ("triinv: P, A, B, and C must be of common size or scalars."); endif endif ## Check for P, A, B, and C being reals if (iscomplex (p) || iscomplex (a) || iscomplex (b) || iscomplex (c)) error ("triinv: P, A, B, and C must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (a, "single") || isa (b, "single") ... || isa (c, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Force zeros for within range parameters. k = (p >= 0) & (p <= 1) & (a < c) & (a <= b) & (b <= c); x(k) = 0; ## Compute triangular iCDF h = 2 ./ (c-a); w = b - a; area1 = h .* w / 2; j = k & (p <= area1); x(j) += (2 * p(j) .* (w(j) ./ h(j))) .^ 0.5 + a(j); w = c - b; j = k & (area1 < p) & (p < 1); x(j) += c(j) - (2 * (1 - p(j)) .* (w(j) ./ h(j))) .^ 0.5; j = k & (p == 1); x(j) = c(j); endfunction %!demo %! ## Plot various iCDFs from the triangular distribution %! p = 0.001:0.001:0.999; %! x1 = triinv (p, 3, 6, 4); %! x2 = triinv (p, 1, 5, 2); %! x3 = triinv (p, 2, 9, 3); %! x4 = triinv (p, 2, 9, 5); %! plot (p, x1, "-b", p, x2, "-g", p, x3, "-r", p, x4, "-c") %! grid on %! ylim ([0, 10]) %! legend ({"a = 3, b = 6, c = 4", "a = 1, b = 5, c = 2", ... %! "a = 2, b = 9, c = 3", "a = 2, b = 9, c = 5"}, ... %! "location", "northwest") %! title ("Triangular CDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p, y %! p = [-1, 0, 0.02, 0.5, 0.98, 1, 2]; %! y = [NaN, 0, 0.1, 0.5, 0.9, 1, NaN] + 1; %!assert (triinv (p, ones (1, 7), 1.5 * ones (1, 7), 2 * ones (1, 7)), y, eps) %!assert (triinv (p, 1 * ones (1, 7), 1.5, 2), y, eps) %!assert (triinv (p, 1, 1.5, 2 * ones (1, 7)), y, eps) %!assert (triinv (p, 1, 1.5*ones (1,7), 2), y, eps) %!assert (triinv (p, 1, 1.5, 2), y, eps) %!assert (triinv (p, [1, 1, NaN, 1, 1, 1, 1], 1.5, 2), [y(1:2), NaN, y(4:7)], eps) %!assert (triinv (p, 1, 1.5 * [1, 1, NaN, 1, 1, 1, 1], 2), [y(1:2), NaN, y(4:7)], eps) %!assert (triinv (p, 1, 1.5, 2 * [1, 1, NaN, 1, 1, 1, 1]), [y(1:2), NaN, y(4:7)], eps) %!assert (triinv ([p, NaN], 1, 1.5, 2), [y, NaN], eps) ## Test class of input preserved %!assert (triinv (single ([p, NaN]), 1, 1.5, 2), single ([y, NaN]), eps('single')) %!assert (triinv ([p, NaN], single (1), 1.5, 2), single ([y, NaN]), eps('single')) %!assert (triinv ([p, NaN], 1, single (1.5), 2), single ([y, NaN]), eps('single')) %!assert (triinv ([p, NaN], 1, 1.5, single (2)), single ([y, NaN]), eps('single')) ## Test input validation %!error triinv () %!error triinv (1) %!error triinv (1, 2) %!error triinv (1, 2, 3) %!error ... %! triinv (1, 2, 3, 4, 5) %!error ... %! triinv (ones (3), ones (2), ones(2), ones(2)) %!error ... %! triinv (ones (2), ones (3), ones(2), ones(2)) %!error ... %! triinv (ones (2), ones (2), ones(3), ones(2)) %!error ... %! triinv (ones (2), ones (2), ones(2), ones(3)) %!error triinv (i, 2, 3, 4) %!error triinv (1, i, 3, 4) %!error triinv (1, 2, i, 4) %!error triinv (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/tripdf.m000066400000000000000000000140521475240274700216060ustar00rootroot00000000000000## Copyright (C) 1997-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} tripdf (@var{x}, @var{a}, @var{b}, @var{c}) ## ## Triangular probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the triangular distribution with lower limit parameter @var{a}, peak ## location (mode) parameter @var{b}, and upper limit parameter @var{c}. The ## size of @var{y} is the common size of the input arguments. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Note that the order of the parameter input arguments has been changed after ## statistics version 1.6.3 in order to be MATLAB compatible with the parameters ## used in the TriangularDistribution probability distribution object. More ## specifically, the positions of the parameters @var{b} and @var{c} have been ## swapped. As a result, the naming conventions no longer coinside with those ## used in Wikipedia, in which @math{b} denotes the upper limit and @math{c} ## denotes the mode or peak parameter. ## ## Further information about the triangular distribution can be found at ## @url{https://en.wikipedia.org/wiki/Triangular_distribution} ## ## @seealso{tricdf, triinv, trirnd, tristat} ## @end deftypefn function y = tripdf (x, a, b, c) ## Check for valid number of input arguments if (nargin < 4) error ("tripdf: function called with too few input arguments."); endif ## Check for common size of X, A, B, and C if (! isscalar (x) || ! isscalar (a) || ! isscalar (b) || ! isscalar (c)) [retval, x, a, b, c] = common_size (x, a, b, c); if (retval > 0) error ("tripdf: X, A, B, and C must be of common size or scalars."); endif endif ## Check for X, A, B, and C being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b) || iscomplex (c)) error ("tripdf: X, A, B, and C must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single") ... || isa (c, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Force NaNs for out of range parameters. k = isnan (x) | ! (a < c) | ! (b >= a) | ! (b <= c); y(k) = NaN; k = (x >= a) & (x <= c) & (a < c) & (a <= b) & (b <= c); h = 2 ./ (c - a); j = k & (a <= x) & (x < b); y(j) = h(j) .* (x(j) - a(j)) ./ (b(j) - a(j)); j = k & (x == b); y(j) = h(j); j = k & (b < x) & (x <= c); y(j) = h(j) .* (c(j) - x(j)) ./ (c(j) - b(j)); endfunction %!demo %! ## Plot various CDFs from the triangular distribution %! x = 0.001:0.001:10; %! y1 = tripdf (x, 3, 4, 6); %! y2 = tripdf (x, 1, 2, 5); %! y3 = tripdf (x, 2, 3, 9); %! y4 = tripdf (x, 2, 5, 9); %! plot (x, y1, "-b", x, y2, "-g", x, y3, "-r", x, y4, "-c") %! grid on %! xlim ([0, 10]) %! legend ({"a = 3, b = 4, c = 6", "a = 1, b = 2, c = 5", ... %! "a = 2, b = 3, c = 9", "a = 2, b = 5, c = 9"}, ... %! "location", "northeast") %! title ("Triangular CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y, deps %! x = [-1, 0, 0.1, 0.5, 0.9, 1, 2] + 1; %! y = [0, 0, 0.4, 2, 0.4, 0, 0]; %! deps = 2*eps; %!assert (tripdf (x, ones (1,7), 1.5*ones (1,7), 2*ones (1,7)), y, deps) %!assert (tripdf (x, 1*ones (1,7), 1.5, 2), y, deps) %!assert (tripdf (x, 1, 1.5, 2*ones (1,7)), y, deps) %!assert (tripdf (x, 1, 1.5*ones (1,7), 2), y, deps) %!assert (tripdf (x, 1, 1.5, 2), y, deps) %!assert (tripdf (x, [1, 1, NaN, 1, 1, 1, 1], 1.5, 2), [y(1:2), NaN, y(4:7)], deps) %!assert (tripdf (x, 1, 1.5, 2*[1, 1, NaN, 1, 1, 1, 1]), [y(1:2), NaN, y(4:7)], deps) %!assert (tripdf (x, 1, 1.5*[1, 1, NaN, 1, 1, 1, 1], 2), [y(1:2), NaN, y(4:7)], deps) %!assert (tripdf ([x, NaN], 1, 1.5, 2), [y, NaN], deps) ## Test class of input preserved %!assert (tripdf (single ([x, NaN]), 1, 1.5, 2), single ([y, NaN]), eps("single")) %!assert (tripdf ([x, NaN], single (1), 1.5, 2), single ([y, NaN]), eps("single")) %!assert (tripdf ([x, NaN], 1, 1.5, single (2)), single ([y, NaN]), eps("single")) %!assert (tripdf ([x, NaN], 1, single (1.5), 2), single ([y, NaN]), eps("single")) ## Test input validation %!error tripdf () %!error tripdf (1) %!error tripdf (1, 2) %!error tripdf (1, 2, 3) %!error ... %! tripdf (1, 2, 3, 4, 5) %!error ... %! tripdf (ones (3), ones (2), ones(2), ones(2)) %!error ... %! tripdf (ones (2), ones (3), ones(2), ones(2)) %!error ... %! tripdf (ones (2), ones (2), ones(3), ones(2)) %!error ... %! tripdf (ones (2), ones (2), ones(2), ones(3)) %!error tripdf (i, 2, 3, 4) %!error tripdf (1, i, 3, 4) %!error tripdf (1, 2, i, 4) %!error tripdf (1, 2, 3, i) statistics-release-1.7.3/inst/dist_fun/trirnd.m000066400000000000000000000202051475240274700216150ustar00rootroot00000000000000## Copyright (C) 1997-2015 Kurt Hornik ## Copyright (C) 2016 Dag Lyberg ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} trirnd (@var{a}, @var{b}, @var{c}) ## @deftypefnx {statistics} {@var{r} =} trirnd (@var{a}, @var{b}, @var{c}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} trirnd (@var{a}, @var{b}, @var{c}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} trirnd (@var{a}, @var{b}, @var{c}, [@var{sz}]) ## ## Random arrays from the triangular distribution. ## ## @code{@var{r} = trirnd (@var{sigma})} returns an array of random numbers ## chosen from the triangular distribution with lower limit parameter @var{a}, ## peak location (mode) parameter @var{b}, and upper limit parameter @var{c}. ## The size of @var{r} is the common size of @var{a}, @var{b}, and @var{c}. A ## scalar input functions as a constant matrix of the same size as the other ## inputs. ## ## When called with a single size argument, @code{trirnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Note that the order of the parameter input arguments has been changed after ## statistics version 1.6.3 in order to be MATLAB compatible with the parameters ## used in the TriangularDistribution probability distribution object. More ## specifically, the positions of the parameters @var{b} and @var{c} have been ## swapped. As a result, the naming conventions no longer coinside with those ## used in Wikipedia, in which @math{b} denotes the upper limit and @math{c} ## denotes the mode or peak parameter. ## ## Further information about the triangular distribution can be found at ## @url{https://en.wikipedia.org/wiki/Triangular_distribution} ## ## @seealso{tricdf, triinv, tripdf, tristat} ## @end deftypefn function rnd = trirnd (a, b, c, varargin) ## Check for valid number of input arguments if (nargin < 3) error ("trirnd: function called with too few input arguments."); endif ## Check for common size of A, B, and C if (! isscalar (a) || ! isscalar (b) || ! isscalar (c)) [retval, a, b, c] = common_size (a, b, c); scalarABC = false; if (retval > 0) error ("trirnd: A, B, and C must be of common size or scalars."); endif else scalarABC = true; endif ## Check for A, B, and C being reals if (iscomplex (a) || iscomplex (b) || iscomplex (c)) error ("trirnd: A, B, and C must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 3) sz = size (a); elseif (nargin == 4) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["trirnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 4) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("trirnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (a) && ! isequal (size (a), sz)) error ("trirnd: A, B, and C must be scalar or of size SZ."); endif ## Check for class type if (isa (a, "single") || isa (b, "single") || isa (c, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from triangular distribution if (scalarABC) if ((-Inf < a) && (a < c) && (a <= b) && (b <= c) && (c < Inf)) w = c-a; left_width = b-a; right_width = c-b; h = 2 / w; left_area = h * left_width / 2; rnd = rand (sz, cls); idx = rnd < left_area; rnd(idx) = a + (rnd(idx) * w * left_width).^0.5; rnd(~idx) = c - ((1-rnd(~idx)) * w * right_width).^0.5; else rnd = NaN (sz, cls); endif else w = c-a; left_width = b-a; right_width = c-b; h = 2 ./ w; left_area = h .* left_width / 2; rnd = rand (sz, cls); k = rnd < left_area; rnd(k) = a(k) + (rnd(k) .* w(k) .* left_width(k)).^0.5; rnd(~k) = c(~k) - ((1-rnd(~k)) .* w(~k) .* right_width(~k)).^0.5; k = ! (-Inf < a) | ! (a < c) | ! (a <= b) | ! (b <= c) | ! (c < Inf); rnd(k) = NaN; endif endfunction ## Test results %!assert (size (trirnd (1, 1.5, 2)), [1, 1]) %!assert (size (trirnd (1 * ones (2, 1), 1.5, 2)), [2, 1]) %!assert (size (trirnd (1 * ones (2, 2), 1.5, 2)), [2, 2]) %!assert (size (trirnd (1, 1.5 * ones (2, 1), 2)), [2, 1]) %!assert (size (trirnd (1, 1.5 * ones (2, 2), 2)), [2, 2]) %!assert (size (trirnd (1, 1.5, 2 * ones (2, 1))), [2, 1]) %!assert (size (trirnd (1, 1.5, 2 * ones (2, 2))), [2, 2]) %!assert (size (trirnd (1, 1.5, 2, 3)), [3, 3]) %!assert (size (trirnd (1, 1.5, 2, [4, 1])), [4, 1]) %!assert (size (trirnd (1, 1.5, 2, 4, 1)), [4, 1]) ## Test class of input preserved %!assert (class (trirnd (1, 1.5, 2)), "double") %!assert (class (trirnd (single (1), 1.5, 2)), "single") %!assert (class (trirnd (single ([1, 1]), 1.5, 2)), "single") %!assert (class (trirnd (1, single (1.5), 2)), "single") %!assert (class (trirnd (1, single ([1.5, 1.5]), 2)), "single") %!assert (class (trirnd (1, 1.5, single (1.5))), "single") %!assert (class (trirnd (1, 1.5, single ([2, 2]))), "single") ## Test input validation %!error trirnd () %!error trirnd (1) %!error trirnd (1, 2) %!error ... %! trirnd (ones (3), 5 * ones (2), ones (2)) %!error ... %! trirnd (ones (2), 5 * ones (3), ones (2)) %!error ... %! trirnd (ones (2), 5 * ones (2), ones (3)) %!error trirnd (i, 5, 3) %!error trirnd (1, 5+i, 3) %!error trirnd (1, 5, i) %!error ... %! trirnd (1, 5, 3, -1) %!error ... %! trirnd (1, 5, 3, 1.2) %!error ... %! trirnd (1, 5, 3, ones (2)) %!error ... %! trirnd (1, 5, 3, [2 -1 2]) %!error ... %! trirnd (1, 5, 3, [2 0 2.5]) %!error ... %! trirnd (1, 5, 3, 2, -1, 5) %!error ... %! trirnd (1, 5, 3, 2, 1.5, 5) %!error ... %! trirnd (2, 5 * ones (2), 2, 3) %!error ... %! trirnd (2, 5 * ones (2), 2, [3, 2]) %!error ... %! trirnd (2, 5 * ones (2), 2, 3, 2) statistics-release-1.7.3/inst/dist_fun/trnd.m000066400000000000000000000135461475240274700212740ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} trnd (@var{df}) ## @deftypefnx {statistics} {@var{r} =} trnd (@var{df}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} trnd (@var{df}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} trnd (@var{df}, [@var{sz}]) ## ## Random arrays from the Student's T distribution. ## ## Return a matrix of random samples from the Students's T distribution with ## @var{df} degrees of freedom. ## ## @code{@var{r} = trnd (@var{df})} returns an array of random numbers chosen ## from the Student's T distribution with @var{df} degrees of freedom. The size ## of @var{r} is the size of @var{df}. A scalar input functions as a constant ## matrix of the same size as the other inputs. @var{df} must be a finite real ## number greater than 0, otherwise NaN is returned. ## ## When called with a single size argument, @code{trnd} returns a square matrix ## with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Student's T distribution can be found at ## @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution} ## ## @seealso{tcdf, tpdf, tpdf, tstat} ## @end deftypefn function r = trnd (df, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("trnd: function called with too few input arguments."); endif ## Check for DF being real if (iscomplex (df)) error ("trnd: DF must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (df); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["trnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("trnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (df) && ! isequal (size (df), sz)) error ("trnd: DF must be scalar or of size SZ."); endif ## Check for class type if (isa (df, "single")) cls = "single"; else cls = "double"; endif if (isscalar (df)) if ((df > 0) && (df < Inf)) r = randn (sz, cls) ./ sqrt (2*randg (df/2, sz, cls) / df); elseif (isinf (df)) r = randn (sz, cls); else r = NaN (sz, cls); endif else r = NaN (sz, cls); k = (df > 0) & (df < Inf); kinf = isinf (df); r(k) = randn (sum (k(:)), 1, cls) ./ ... sqrt (2*randg (df(k)/2, cls) ./ df(k))(:); r(kinf) = randn (sum (kinf(:)), 1, cls); endif endfunction ## Test output %!assert (size (trnd (2)), [1, 1]) %!assert (size (trnd (ones (2,1))), [2, 1]) %!assert (size (trnd (ones (2,2))), [2, 2]) %!assert (size (trnd (1, 3)), [3, 3]) %!assert (size (trnd (1, [4 1])), [4, 1]) %!assert (size (trnd (1, 4, 1)), [4, 1]) %!assert (size (trnd (1, 4, 1)), [4, 1]) %!assert (size (trnd (1, 4, 1, 5)), [4, 1, 5]) %!assert (size (trnd (1, 0, 1)), [0, 1]) %!assert (size (trnd (1, 1, 0)), [1, 0]) %!assert (size (trnd (1, 1, 2, 0, 5)), [1, 2, 0, 5]) %!assert (trnd (0, 1, 1), NaN) %!assert (trnd ([0, 0, 0], [1, 3]), [NaN, NaN, NaN]) ## Test class of input preserved %!assert (class (trnd (2)), "double") %!assert (class (trnd (single (2))), "single") %!assert (class (trnd (single ([2 2]))), "single") ## Test input validation %!error trnd () %!error trnd (i) %!error ... %! trnd (1, -1) %!error ... %! trnd (1, 1.2) %!error ... %! trnd (1, ones (2)) %!error ... %! trnd (1, [2 -1 2]) %!error ... %! trnd (1, [2 0 2.5]) %!error ... %! trnd (ones (2), ones (2)) %!error ... %! trnd (1, 2, -1, 5) %!error ... %! trnd (1, 2, 1.5, 5) %!error trnd (ones (2,2), 3) %!error trnd (ones (2,2), [3, 2]) %!error trnd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/unidcdf.m000066400000000000000000000121471475240274700217350ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2007-2016 David Bateman ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} unidcdf (@var{x}, @var{N}) ## @deftypefnx {statistics} {@var{p} =} unidcdf (@var{x}, @var{N}, @qcode{"upper"}) ## ## Discrete uniform cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of a discrete uniform distribution with parameter @var{N}, which ## corresponds to the maximum observable value. @code{unidcdf} assumes the ## integer values in the range @math{[1,N]} with equal probability. The size of ## @var{p} is the common size of @var{x} and @var{N}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## The maximum observable values in @var{N} must be positive integers, otherwise ## @qcode{NaN} is returned. ## ## @code{[@dots{}] = unidcdf (@var{x}, @var{N}, "upper")} computes the upper ## tail probability of the discrete uniform distribution with maximum observable ## value @var{N}, at the values in @var{x}. ## ## Warning: The underlying implementation uses the double class and will only ## be accurate for @var{N} < @code{flintmax} (@w{@math{2^{53}}} on ## IEEE 754 compatible systems). ## ## Further information about the discrete uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Discrete_uniform_distribution} ## ## @seealso{unidinv, unidpdf, unidrnd, unidfit, unidstat} ## @end deftypefn function p = unidcdf (x, N, uflag) ## Check for valid number of input arguments if (nargin < 2) error ("unidcdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("unidcdf: invalid argument for upper tail."); else uflag = false; endif ## Check for common size of X and N if (! isscalar (x) || ! isscalar (N)) [retval, x, N] = common_size (x, N); if (retval > 0) error ("unidcdf: X and N must be of common size or scalars."); endif endif ## Check for X and N being reals if (iscomplex (x) || iscomplex (N)) error ("unidcdf: X and N must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (N, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Return 1 for X >= N p(x >= N) = 1; ## Floor X xf = floor (x); ## Compute uniform discrete CDF k = find (xf >= 1 & xf <= N); if any(k) p(k) = xf(k) ./ N(k); endif ## Check for NaNs or floored N <= 0 is_nan = isnan (x) | ! (N > 0 & N == fix (N)); if (any (is_nan(:))) p(is_nan) = NaN; endif p(N < 1 | round(N) != N) = NaN; if (uflag) # Compute upper tail p = 1 - unidcdf (x, N); endif endfunction %!demo %! ## Plot various CDFs from the discrete uniform distribution %! x = 0:10; %! p1 = unidcdf (x, 5); %! p2 = unidcdf (x, 9); %! plot (x, p1, "*b", x, p2, "*g") %! grid on %! xlim ([0, 10]) %! ylim ([0, 1]) %! legend ({"N = 5", "N = 9"}, "location", "southeast") %! title ("Discrete uniform CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [0 1 2.5 10 11]; %! y = [0, 0.1 0.2 1.0 1.0]; %!assert (unidcdf (x, 10*ones (1,5)), y) %!assert (unidcdf (x, 10*ones (1,5), "upper"), 1 - y) %!assert (unidcdf (x, 10), y) %!assert (unidcdf (x, 10, "upper"), 1 - y) %!assert (unidcdf (x, 10*[0 1 NaN 1 1]), [NaN 0.1 NaN y(4:5)]) %!assert (unidcdf ([x(1:2) NaN Inf x(5)], 10), [y(1:2) NaN 1 y(5)]) ## Test class of input preserved %!assert (unidcdf ([x, NaN], 10), [y, NaN]) %!assert (unidcdf (single ([x, NaN]), 10), single ([y, NaN])) %!assert (unidcdf ([x, NaN], single (10)), single ([y, NaN])) ## Test input validation %!error unidcdf () %!error unidcdf (1) %!error unidcdf (1, 2, 3) %!error unidcdf (1, 2, "tail") %!error ... %! unidcdf (ones (3), ones (2)) %!error ... %! unidcdf (ones (2), ones (3)) %!error unidcdf (i, 2) %!error unidcdf (2, i) statistics-release-1.7.3/inst/dist_fun/unidinv.m000066400000000000000000000102271475240274700217720ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2007-2016 David Bateman ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} unidinv (@var{p}, @var{N}) ## ## Inverse of the discrete uniform cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the discrete uniform distribution with parameter @var{N}, which corresponds ## to the maximum observable value. @code{unidinv} assumes the integer values ## in the range @math{[1,N]} with equal probability. The size of @var{x} is the ## common size of @var{p} and @var{N}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## The maximum observable values in @var{N} must be positive integers, otherwise ## @qcode{NaN} is returned. ## ## Warning: The underlying implementation uses the double class and will only ## be accurate for @var{N} < @code{flintmax} (@w{@math{2^{53}}} on ## IEEE 754 compatible systems). ## ## Further information about the discrete uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Discrete_uniform_distribution} ## ## @seealso{unidcdf, unidpdf, unidrnd, unidfit, unidstat} ## @end deftypefn function x = unidinv (p, N) ## Check for valid number of input arguments if (nargin < 2) error ("unidinv: function called with too few input arguments."); endif ## Check for common size of P and N if (! isscalar (p) || ! isscalar (N)) [retval, p, N] = common_size (p, N); if (retval > 0) error ("unidinv: P and N must be of common size or scalars."); endif endif ## Check for P and N being reals if (iscomplex (p) || iscomplex (N)) error ("unidinv: P and N must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (N, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## For Matlab compatibility, unidinv(0) = NaN k = (p > 0) & (p <= 1) & (N > 0 & N == fix (N)); x(k) = floor (p(k) .* N(k)); endfunction %!demo %! ## Plot various iCDFs from the discrete uniform distribution %! p = 0.001:0.001:0.999; %! x1 = unidinv (p, 5); %! x2 = unidinv (p, 9); %! plot (p, x1, "-b", p, x2, "-g") %! grid on %! xlim ([0, 1]) %! ylim ([0, 10]) %! legend ({"N = 5", "N = 9"}, "location", "northwest") %! title ("Discrete uniform iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (unidinv (p, 10*ones (1,5)), [NaN NaN 5 10 NaN], eps) %!assert (unidinv (p, 10), [NaN NaN 5 10 NaN], eps) %!assert (unidinv (p, 10*[0 1 NaN 1 1]), [NaN NaN NaN 10 NaN], eps) %!assert (unidinv ([p(1:2) NaN p(4:5)], 10), [NaN NaN NaN 10 NaN], eps) ## Test class of input preserved %!assert (unidinv ([p, NaN], 10), [NaN NaN 5 10 NaN NaN], eps) %!assert (unidinv (single ([p, NaN]), 10), single ([NaN NaN 5 10 NaN NaN]), eps) %!assert (unidinv ([p, NaN], single (10)), single ([NaN NaN 5 10 NaN NaN]), eps) ## Test input validation %!error unidinv () %!error unidinv (1) %!error ... %! unidinv (ones (3), ones (2)) %!error ... %! unidinv (ones (2), ones (3)) %!error unidinv (i, 2) %!error unidinv (2, i) statistics-release-1.7.3/inst/dist_fun/unidpdf.m000066400000000000000000000077441475240274700217610ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2007-2016 David Bateman ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} unidpdf (@var{x}, @var{N}) ## ## Discrete uniform probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the discrete uniform distribution with parameter @var{N}, which ## corresponds to the maximum observable value. @code{unidpdf} assumes the ## integer values in the range @math{[1,N]} with equal probability. The size of ## @var{x} is the common size of @var{p} and @var{N}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## The maximum observable values in @var{N} must be positive integers, otherwise ## @qcode{NaN} is returned. ## ## Warning: The underlying implementation uses the double class and will only ## be accurate for @var{N} < @code{flintmax} (@w{@math{2^{53}}} on ## IEEE 754 compatible systems). ## ## Further information about the discrete uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Discrete_uniform_distribution} ## ## @seealso{unidcdf, unidinv, unidrnd, unidfit, unidstat} ## @end deftypefn function y = unidpdf (x, N) ## Check for valid number of input arguments if (nargin < 2) error ("unidpdf: function called with too few input arguments."); endif ## Check for common size of X and N if (! isscalar (x) || ! isscalar (N)) [retval, x, N] = common_size (x, N); if (retval > 0) error ("unidpdf: X and N must be of common size or scalars."); endif endif ## Check for X and N being reals if (iscomplex (x) || iscomplex (N)) error ("unidpdf: X and N must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (N, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif k = isnan (x) | ! (N > 0 & N == fix (N)); y(k) = NaN; k = ! k & (x >= 1) & (x <= N) & (x == fix (x)); y(k) = 1 ./ N(k); endfunction %!demo %! ## Plot various PDFs from the discrete uniform distribution %! x = 0:10; %! y1 = unidpdf (x, 5); %! y2 = unidpdf (x, 9); %! plot (x, y1, "*b", x, y2, "*g") %! grid on %! xlim ([0, 10]) %! ylim ([0, 0.25]) %! legend ({"N = 5", "N = 9"}, "location", "northeast") %! title ("Descrete uniform PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 1 2 10 11]; %! y = [0 0 0.1 0.1 0.1 0]; %!assert (unidpdf (x, 10*ones (1,6)), y) %!assert (unidpdf (x, 10), y) %!assert (unidpdf (x, 10*[0 NaN 1 1 1 1]), [NaN NaN y(3:6)]) %!assert (unidpdf ([x, NaN], 10), [y, NaN]) ## Test class of input preserved %!assert (unidpdf (single ([x, NaN]), 10), single ([y, NaN])) %!assert (unidpdf ([x, NaN], single (10)), single ([y, NaN])) ## Test input validation %!error unidpdf () %!error unidpdf (1) %!error ... %! unidpdf (ones (3), ones (2)) %!error ... %! unidpdf (ones (2), ones (3)) %!error unidpdf (i, 2) %!error unidpdf (2, i) statistics-release-1.7.3/inst/dist_fun/unidrnd.m000066400000000000000000000140541475240274700217630ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 2005-2016 John W. Eaton ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} unidrnd (@var{N}) ## @deftypefnx {statistics} {@var{r} =} unidrnd (@var{N}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} unidrnd (@var{N}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} unidrnd (@var{N}, [@var{sz}]) ## ## Random arrays from the discrete uniform distribution. ## ## @code{@var{r} = unidrnd (@var{N})} returns an array of random numbers chosen ## from the discrete uniform distribution with parameter @var{N}, which ## corresponds to the maximum observable value. @code{unidrnd} assumes the ## integer values in the range @math{[1,N]} with equal probability. The size of ## @var{r} is the size of @var{N}. A scalar input functions as a constant ## matrix of the same size as the other inputs. ## ## The maximum observable values in @var{N} must be positive integers, otherwise ## @qcode{NaN} is returned. ## ## When called with a single size argument, @code{unidrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Warning: The underlying implementation uses the double class and will only ## be accurate for @var{N} < @code{flintmax} (@w{@math{2^{53}}} on ## IEEE 754 compatible systems). ## ## Further information about the discrete uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Discrete_uniform_distribution} ## ## @seealso{unidcdf, unidinv, unidrnd, unidfit, unidstat} ## @end deftypefn function r = unidrnd (N, varargin) ## Check for valid number of input arguments if (nargin < 1) error ("unidrnd: function called with too few input arguments."); endif ## Check for N being real if (iscomplex (N)) error ("unidrnd: N must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 1) sz = size (N); elseif (nargin == 2) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["unidrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 2) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("unidrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (N) && ! isequal (size (N), sz)) error ("unidrnd: N must be scalar or of size SZ."); endif ## Check for class type if (isa (N, "single")) cls = "single"; else cls = "double"; endif if (isscalar (N)) if (N > 0 && N == fix (N)) r = ceil (rand (sz, cls) * N); else r = NaN (sz, cls); endif else r = ceil (rand (sz, cls) .* N); k = ! (N > 0 & N == fix (N)); r(k) = NaN; endif endfunction ## Test output %!assert (size (unidrnd (2)), [1, 1]) %!assert (size (unidrnd (ones (2,1))), [2, 1]) %!assert (size (unidrnd (ones (2,2))), [2, 2]) %!assert (size (unidrnd (1, 3)), [3, 3]) %!assert (size (unidrnd (1, [4 1])), [4, 1]) %!assert (size (unidrnd (1, 4, 1)), [4, 1]) %!assert (size (unidrnd (1, 4, 1)), [4, 1]) %!assert (size (unidrnd (1, 4, 1, 5)), [4, 1, 5]) %!assert (size (unidrnd (1, 0, 1)), [0, 1]) %!assert (size (unidrnd (1, 1, 0)), [1, 0]) %!assert (size (unidrnd (1, 1, 2, 0, 5)), [1, 2, 0, 5]) %!assert (unidrnd (0, 1, 1), NaN) %!assert (unidrnd ([0, 0, 0], [1, 3]), [NaN, NaN, NaN]) ## Test class of input preserved %!assert (class (unidrnd (2)), "double") %!assert (class (unidrnd (single (2))), "single") %!assert (class (unidrnd (single ([2 2]))), "single") ## Test input validation %!error unidrnd () %!error unidrnd (i) %!error ... %! unidrnd (1, -1) %!error ... %! unidrnd (1, 1.2) %!error ... %! unidrnd (1, ones (2)) %!error ... %! unidrnd (1, [2 -1 2]) %!error ... %! unidrnd (1, [2 0 2.5]) %!error ... %! unidrnd (ones (2), ones (2)) %!error ... %! unidrnd (1, 2, -1, 5) %!error ... %! unidrnd (1, 2, 1.5, 5) %!error unidrnd (ones (2,2), 3) %!error unidrnd (ones (2,2), [3, 2]) %!error unidrnd (ones (2,2), 2, 3) statistics-release-1.7.3/inst/dist_fun/unifcdf.m000066400000000000000000000133601475240274700217350ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} unifcdf (@var{x}, @var{a}, @var{b}) ## @deftypefnx {statistics} {@var{p} =} unifcdf (@var{x}, @var{a}, @var{b}, @qcode{"upper"}) ## ## Continuous uniform cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the continuous uniform distribution with parameters @var{a} and ## @var{b}, which define the lower and upper bounds of the interval ## @qcode{[@var{a}, @var{b}]}. The size of @var{p} is the common size of ## @var{x}, @var{a}, and @var{b}. A scalar input functions as a constant matrix ## of the same size as the other inputs. ## ## @code{[@dots{}] = unifcdf (@var{x}, @var{a}, @var{b}, "upper")} computes the ## upper tail probability of the continuous uniform distribution with parameters ## @var{a}, and @var{b}, at the values in @var{x}. ## ## Further information about the continuous uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{unifinv, unifpdf, unifrnd, unifit, unifstat} ## @end deftypefn function p = unifcdf (x, a, b, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("unifcdf: function called with too few input arguments."); endif ## Check for "upper" flag if (nargin > 3 && strcmpi (uflag, "upper")) uflag = true; elseif (nargin > 3 && ! strcmpi (uflag, "upper")) error ("unifcdf: invalid argument for upper tail."); else uflag = false; endif ## Check for common size of X, A, and B if (! isscalar (x) || ! isscalar (a) || ! isscalar (b)) [retval, x, a, b] = common_size (x, a, b); if (retval > 0) error ("unifcdf: X, A, and B must be of common size or scalars."); endif endif ## Check for X, A, and B being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b)) error ("unifcdf: X, A, and B must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Calculate continuous uniform CDF for valid parameter and data range k = find(x > a & x < b & a < b); if (uflag) p(x <= a & a < b) = 1; p(x >= b & a < b) = 0; if any(k) p(k) = (b(k)- x(k)) ./ (b(k) - a(k)); endif else p(x <= a & a < b) = 0; p(x >= b & a < b) = 1; if any(k) p(k) = (x(k) - a(k)) ./ (b(k) - a(k)); endif endif ## Continue argument check p(a >= b) = NaN; p(isnan(x) | isnan(a) | isnan(b)) = NaN; endfunction %!demo %! ## Plot various CDFs from the continuous uniform distribution %! x = 0:0.1:10; %! p1 = unifcdf (x, 2, 5); %! p2 = unifcdf (x, 3, 9); %! plot (x, p1, "-b", x, p2, "-g") %! grid on %! xlim ([0, 10]) %! ylim ([0, 1]) %! legend ({"a = 2, b = 5", "a = 3, b = 9"}, "location", "southeast") %! title ("Continuous uniform CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2] + 1; %! y = [0 0 0.5 1 1]; %!assert (unifcdf (x, ones (1,5), 2*ones (1,5)), y) %!assert (unifcdf (x, ones (1,5), 2*ones (1,5), "upper"), 1 - y) %!assert (unifcdf (x, 1, 2*ones (1,5)), y) %!assert (unifcdf (x, 1, 2*ones (1,5), "upper"), 1 - y) %!assert (unifcdf (x, ones (1,5), 2), y) %!assert (unifcdf (x, ones (1,5), 2, "upper"), 1 - y) %!assert (unifcdf (x, [2 1 NaN 1 1], 2), [NaN 0 NaN 1 1]) %!assert (unifcdf (x, [2 1 NaN 1 1], 2, "upper"), 1 - [NaN 0 NaN 1 1]) %!assert (unifcdf (x, 1, 2*[0 1 NaN 1 1]), [NaN 0 NaN 1 1]) %!assert (unifcdf (x, 1, 2*[0 1 NaN 1 1], "upper"), 1 - [NaN 0 NaN 1 1]) %!assert (unifcdf ([x(1:2) NaN x(4:5)], 1, 2), [y(1:2) NaN y(4:5)]) %!assert (unifcdf ([x(1:2) NaN x(4:5)], 1, 2, "upper"), 1 - [y(1:2) NaN y(4:5)]) ## Test class of input preserved %!assert (unifcdf ([x, NaN], 1, 2), [y, NaN]) %!assert (unifcdf (single ([x, NaN]), 1, 2), single ([y, NaN])) %!assert (unifcdf ([x, NaN], single (1), 2), single ([y, NaN])) %!assert (unifcdf ([x, NaN], 1, single (2)), single ([y, NaN])) ## Test input validation %!error unifcdf () %!error unifcdf (1) %!error unifcdf (1, 2) %!error unifcdf (1, 2, 3, 4) %!error unifcdf (1, 2, 3, "tail") %!error ... %! unifcdf (ones (3), ones (2), ones (2)) %!error ... %! unifcdf (ones (2), ones (3), ones (2)) %!error ... %! unifcdf (ones (2), ones (2), ones (3)) %!error unifcdf (i, 2, 2) %!error unifcdf (2, i, 2) %!error unifcdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/unifinv.m000066400000000000000000000106471475240274700220020ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} unifinv (@var{p}, @var{a}, @var{b}) ## ## Inverse of the continuous uniform cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the continuous uniform distribution with parameters @var{a} and @var{b}, ## which define the lower and upper bounds of the interval ## @qcode{[@var{a}, @var{b}]}. The size of @var{x} is the common size of ## @var{p}, @var{a}, and @var{b}. A scalar input functions as a constant matrix ## of the same size as the other inputs. ## ## Further information about the continuous uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{unifcdf, unifpdf, unifrnd, unifit, unifstat} ## @end deftypefn function x = unifinv (p, a, b) ## Check for valid number of input arguments if (nargin < 3) error ("unifinv: function called with too few input arguments."); endif ## Check for common size of P, A, and B if (! isscalar (p) || ! isscalar (a) || ! isscalar (b)) [retval, p, a, b] = common_size (p, a, b); if (retval > 0) error ("unifinv: P, A, and B must be of common size or scalars."); endif endif ## Check for P, A, and B being reals if (iscomplex (p) || iscomplex (a) || iscomplex (b)) error ("unifinv: P, A, and B must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (a, "single") || isa (b, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ## Calculate continuous uniform iCDF for valid parameter and data range k = (p >= 0) & (p <= 1) & (a < b); x(k) = a(k) + p(k) .* (b(k) - a(k)); endfunction %!demo %! ## Plot various iCDFs from the continuous uniform distribution %! p = 0.001:0.001:0.999; %! x1 = unifinv (p, 2, 5); %! x2 = unifinv (p, 3, 9); %! plot (p, x1, "-b", p, x2, "-g") %! grid on %! xlim ([0, 1]) %! ylim ([0, 10]) %! legend ({"a = 2, b = 5", "a = 3, b = 9"}, "location", "northwest") %! title ("Continuous uniform iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared p %! p = [-1 0 0.5 1 2]; %!assert (unifinv (p, ones (1,5), 2*ones (1,5)), [NaN 1 1.5 2 NaN]) %!assert (unifinv (p, 0, 1), [NaN 1 1.5 2 NaN] - 1) %!assert (unifinv (p, 1, 2*ones (1,5)), [NaN 1 1.5 2 NaN]) %!assert (unifinv (p, ones (1,5), 2), [NaN 1 1.5 2 NaN]) %!assert (unifinv (p, [1 2 NaN 1 1], 2), [NaN NaN NaN 2 NaN]) %!assert (unifinv (p, 1, 2*[1 0 NaN 1 1]), [NaN NaN NaN 2 NaN]) %!assert (unifinv ([p(1:2) NaN p(4:5)], 1, 2), [NaN 1 NaN 2 NaN]) ## Test class of input preserved %!assert (unifinv ([p, NaN], 1, 2), [NaN 1 1.5 2 NaN NaN]) %!assert (unifinv (single ([p, NaN]), 1, 2), single ([NaN 1 1.5 2 NaN NaN])) %!assert (unifinv ([p, NaN], single (1), 2), single ([NaN 1 1.5 2 NaN NaN])) %!assert (unifinv ([p, NaN], 1, single (2)), single ([NaN 1 1.5 2 NaN NaN])) ## Test input validation %!error unifinv () %!error unifinv (1, 2) %!error ... %! unifinv (ones (3), ones (2), ones (2)) %!error ... %! unifinv (ones (2), ones (3), ones (2)) %!error ... %! unifinv (ones (2), ones (2), ones (3)) %!error unifinv (i, 2, 2) %!error unifinv (2, i, 2) %!error unifinv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/unifpdf.m000066400000000000000000000105501475240274700217500ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} unifpdf (@var{x}, @var{a}, @var{b}) ## ## Continuous uniform probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the continuous uniform distribution with parameters @var{a} and @var{b}, ## which define the lower and upper bounds of the interval ## @qcode{[@var{a}, @var{b}]}. The size of @var{y} is the common size of ## @var{x}, @var{a}, and @var{b}. A scalar input functions as a constant matrix ## of the same size as the other inputs. ## ## Further information about the continuous uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{unifcdf, unifinv, unifrnd, unifit, unifstat} ## @end deftypefn function y = unifpdf (x, a, b) ## Check for valid number of input arguments if (nargin < 3) error ("unifpdf: function called with too few input arguments."); endif ## Check for common size of X, A, and B if (! isscalar (x) || ! isscalar (a) || ! isscalar (b)) [retval, x, a, b] = common_size (x, a, b); if (retval > 0) error ("unifpdf: X, A, and B must be of common size or scalars."); endif endif ## Check for X, A, and B being reals if (iscomplex (x) || iscomplex (a) || iscomplex (b)) error ("unifpdf: X, A, and B must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (a, "single") || isa (b, "single")) y = zeros (size (x), "single"); else y = zeros (size (x)); endif ## Calculate continuous uniform PDF for valid parameter and data range k = isnan (x) | ! (a < b); y(k) = NaN; k = (x >= a) & (x <= b) & (a < b); y(k) = 1 ./ (b(k) - a(k)); endfunction %!demo %! ## Plot various PDFs from the continuous uniform distribution %! x = 0:0.001:10; %! y1 = unifpdf (x, 2, 5); %! y2 = unifpdf (x, 3, 9); %! plot (x, y1, "-b", x, y2, "-g") %! grid on %! xlim ([0, 10]) %! ylim ([0, 0.4]) %! legend ({"a = 2, b = 5", "a = 3, b = 9"}, "location", "northeast") %! title ("Continuous uniform PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y %! x = [-1 0 0.5 1 2] + 1; %! y = [0 1 1 1 0]; %!assert (unifpdf (x, ones (1,5), 2*ones (1,5)), y) %!assert (unifpdf (x, 1, 2*ones (1,5)), y) %!assert (unifpdf (x, ones (1,5), 2), y) %!assert (unifpdf (x, [2 NaN 1 1 1], 2), [NaN NaN y(3:5)]) %!assert (unifpdf (x, 1, 2*[0 NaN 1 1 1]), [NaN NaN y(3:5)]) %!assert (unifpdf ([x, NaN], 1, 2), [y, NaN]) %!assert (unifpdf (x, 0, 1), [1 1 0 0 0]) ## Test class of input preserved %!assert (unifpdf (single ([x, NaN]), 1, 2), single ([y, NaN])) %!assert (unifpdf (single ([x, NaN]), single (1), 2), single ([y, NaN])) %!assert (unifpdf ([x, NaN], 1, single (2)), single ([y, NaN])) ## Test input validation %!error unifpdf () %!error unifpdf (1) %!error unifpdf (1, 2) %!error ... %! unifpdf (ones (3), ones (2), ones (2)) %!error ... %! unifpdf (ones (2), ones (3), ones (2)) %!error ... %! unifpdf (ones (2), ones (2), ones (3)) %!error unifpdf (i, 2, 2) %!error unifpdf (2, i, 2) %!error unifpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/unifrnd.m000066400000000000000000000150601475240274700217630ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2019 Anthony Morast ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} unifrnd (@var{a}, @var{b}) ## @deftypefnx {statistics} {@var{r} =} unifrnd (@var{a}, @var{b}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} unifrnd (@var{a}, @var{b}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} unifrnd (@var{a}, @var{b}, [@var{sz}]) ## ## Random arrays from the continuous uniform distribution. ## ## @code{@var{r} = unifrnd (@var{a}, @var{b})} returns an array of random ## numbers chosen from the continuous uniform distribution with parameters ## @var{a} and @var{b}, which define the lower and upper bounds of the interval ## @qcode{[@var{a}, @var{b}]}. The size of @var{r} is the common size of ## @var{a} and @var{b}. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## When called with a single size argument, @code{unifrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the continuous uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{unifcdf, unifinv, unifpdf, unifit, unifstat} ## @end deftypefn function r = unifrnd (a, b, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("unifrnd: function called with too few input arguments."); endif ## Check for common size of A and B if (! isscalar (a) || ! isscalar (b)) [retval, a, b] = common_size (a, b); if (retval > 0) error ("unifrnd: A and B must be of common size or scalars."); endif endif ## Check for A and B being reals if (iscomplex (a) || iscomplex (b)) error ("unifrnd: A and B must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (a); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["unifrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("unifrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (a) && ! isequal (size (a), sz)) error ("unifrnd: A and B must be scalars or of size SZ."); endif ## Check for class type if (isa (a, "single") || isa (b, "single")) cls = "single"; else cls = "double"; endif if (isscalar (a) && isscalar (b)) if ((-Inf < a) && (a <= b) && (b < Inf)) r = a + (b - a) * rand (sz, cls); else r = NaN (sz, cls); endif else r = a + (b - a) .* rand (sz, cls); k = !(-Inf < a) | !(a <= b) | !(b < Inf); r(k) = NaN; endif endfunction ## Test output %!assert (size (unifrnd (1, 1)), [1 1]) %!assert (size (unifrnd (1, ones (2,1))), [2, 1]) %!assert (size (unifrnd (1, ones (2,2))), [2, 2]) %!assert (size (unifrnd (ones (2,1), 1)), [2, 1]) %!assert (size (unifrnd (ones (2,2), 1)), [2, 2]) %!assert (size (unifrnd (1, 1, 3)), [3, 3]) %!assert (size (unifrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (unifrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (unifrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (unifrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (unifrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (unifrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (unifrnd (1, 1)), "double") %!assert (class (unifrnd (1, single (1))), "single") %!assert (class (unifrnd (1, single ([1, 1]))), "single") %!assert (class (unifrnd (single (1), 1)), "single") %!assert (class (unifrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error unifrnd () %!error unifrnd (1) %!error ... %! unifrnd (ones (3), ones (2)) %!error ... %! unifrnd (ones (2), ones (3)) %!error unifrnd (i, 2, 3) %!error unifrnd (1, i, 3) %!error ... %! unifrnd (1, 2, -1) %!error ... %! unifrnd (1, 2, 1.2) %!error ... %! unifrnd (1, 2, ones (2)) %!error ... %! unifrnd (1, 2, [2 -1 2]) %!error ... %! unifrnd (1, 2, [2 0 2.5]) %!error ... %! unifrnd (1, 2, 2, -1, 5) %!error ... %! unifrnd (1, 2, 2, 1.5, 5) %!error ... %! unifrnd (2, ones (2), 3) %!error ... %! unifrnd (2, ones (2), [3, 2]) %!error ... %! unifrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/vmcdf.m000066400000000000000000000130351475240274700214150ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} vmcdf (@var{x}, @var{mu}, @var{k}) ## @deftypefnx {statistics} {@var{p} =} vmcdf (@var{x}, @var{mu}, @var{k}, @qcode{"upper"}) ## ## Von Mises probability density function (PDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the von Mises distribution with location parameter @var{mu} and ## concentration parameter @var{k} on the interval @math{[-pi,pi]}. The size of ## @var{p} is the common size of @var{x}, @var{mu}, and @var{k}. A scalar input ## functions as a constant matrix of the same same size as the other inputs. ## ## @code{@var{p} = vmcdf (@var{x}, @var{mu}, @var{k}, "upper")} computes the ## upper tail probability of the von Mises distribution with parameters @var{mu} ## and @var{k}, at the values in @var{x}. ## ## Note: the CDF of the von Mises distribution is not analytic. Hence, it is ## calculated by integrating its probability density which is expressed as a ## series of Bessel functions. Balancing between performance and accuracy, the ## integration uses a step of @qcode{1e-5} on the interval @math{[-pi,pi]}, ## which results to an accuracy of about 10 significant digits. ## ## Further information about the von Mises distribution can be found at ## @url{https://en.wikipedia.org/wiki/Von_Mises_distribution} ## ## @seealso{vminv, vmpdf, vmrnd} ## @end deftypefn function p = vmcdf (x, mu, k, uflag) ## Check for valid number of input arguments if (nargin < 3) error ("vmcdf: function called with too few input arguments."); endif ## Check for valid "upper" flag if (nargin > 3) if (! strcmpi (uflag, "upper")) error ("vmcdf: invalid argument for upper tail."); else uflag = true; endif else uflag = false; endif ## Check for common size of X, MU, and K if (! isscalar (x) || ! isscalar (mu) || ! isscalar (k)) [retval, x, mu, k] = common_size (x, mu, k); if (retval > 0) error ("vmcdf: X, MU, and K must be of common size or scalars."); endif endif ## Check for X, MU, and K being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (k)) error ("vmcdf: X, MU, and K must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (k, "single")) p = zeros (size (x), "single"); else p = zeros (size (x)); endif ## Evaluate Von Mises CDF by integrating from -PI to PI interval = linspace (-pi, pi, 1e5)'; # accurate to >10 significant digits f = exp (k .* cos (interval)) ./ (2 .* pi .* besseli (0, k)); c = cumtrapz (interval, f); p = diag (interp1 (interval, c, x - mu, "spline"))'; ## Force Nan for negative K p(k < 0) = NaN; ## Apply upper flag (if required) if (uflag) p = 1 - p; endif endfunction %!demo %! ## Plot various CDFs from the von Mises distribution %! x1 = [-pi:0.1:pi]; %! p1 = vmcdf (x1, 0, 0.5); %! p2 = vmcdf (x1, 0, 1); %! p3 = vmcdf (x1, 0, 2); %! p4 = vmcdf (x1, 0, 4); %! plot (x1, p1, "-r", x1, p2, "-g", x1, p3, "-b", x1, p4, "-c") %! grid on %! xlim ([-pi, pi]) %! legend ({"μ = 0, k = 0.5", "μ = 0, k = 1", ... %! "μ = 0, k = 2", "μ = 0, k = 4"}, "location", "northwest") %! title ("Von Mises CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, p0, p1 %! x = [-pi:pi/2:pi]; %! p0 = [0, 0.10975, 0.5, 0.89025, 1]; %! p1 = [0, 0.03752, 0.5, 0.99622, 1]; %!assert (vmcdf (x, 0, 1), p0, 1e-5) %!assert (vmcdf (x, 0, 1, "upper"), 1 - p0, 1e-5) %!assert (vmcdf (x, zeros (1,5), ones (1,5)), p0, 1e-5) %!assert (vmcdf (x, zeros (1,5), ones (1,5), "upper"), 1 - p0, 1e-5) %!assert (vmcdf (x, 0, [1 2 3 4 5]), p1, 1e-5) %!assert (vmcdf (x, 0, [1 2 3 4 5], "upper"), 1 - p1, 1e-5) ## Test class of input preserved %!assert (isa (vmcdf (single (pi), 0, 1), "single"), true) %!assert (isa (vmcdf (pi, single (0), 1), "single"), true) %!assert (isa (vmcdf (pi, 0, single (1)), "single"), true) ## Test input validation %!error vmcdf () %!error vmcdf (1) %!error vmcdf (1, 2) %!error vmcdf (1, 2, 3, "tail") %!error vmcdf (1, 2, 3, 4) %!error ... %! vmcdf (ones (3), ones (2), ones (2)) %!error ... %! vmcdf (ones (2), ones (3), ones (2)) %!error ... %! vmcdf (ones (2), ones (2), ones (3)) %!error vmcdf (i, 2, 2) %!error vmcdf (2, i, 2) %!error vmcdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/vminv.m000066400000000000000000000131571475240274700214620ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} vminv (@var{p}, @var{mu}, @var{k}) ## ## Inverse of the von Mises cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) of ## the von Mises distribution with location parameter @var{mu} and concentration ## parameter @var{k} on the interval @math{[-pi,pi]}. The size of @var{x} is ## the common size of @var{p}, @var{mu}, and @var{k}. A scalar input functions ## as a constant matrix of the same size as the other inputs. ## ## Note: the quantile of the von Mises distribution is not analytic. Hence, it ## is approximated by a custom searching algorithm using its CDF until it ## converges up to a tolerance of @qcode{1e-5} or 100 iterations. As a result, ## balancing between performance and accuracy, the accuracy is about ## @qcode{5e-5} for @qcode{@var{k} = 1} and it drops to @qcode{5e-5} as @var{k} ## increases. ## ## Further information about the von Mises distribution can be found at ## @url{https://en.wikipedia.org/wiki/Von_Mises_distribution} ## ## @seealso{vmcdf, vmpdf, vmrnd} ## @end deftypefn function x = vminv (p, mu, k) ## Check for valid number of input arguments if (nargin < 3) error ("vminv: function called with too few input arguments."); endif ## Check for common size of P, MU, and K [err, p, mu, k] = common_size (p, mu, k); if (err > 0) error ("vminv: P, MU, and K must be of common size or scalars."); endif ## Check for P, MU, and K being reals if (iscomplex (p) || iscomplex (mu) || iscomplex (k)) error ("vminv: P, MU, and K must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (mu, "single") || isa (k, "single")) x = NaN (size (p), "single"); else x = NaN (size (p), "double"); endif ## Process edge cases p=0, p=0.5, p=1 p_0 = p < eps (class (x)) & k > 0 & isfinite (mu); x(p_0) = -pi + mu(p_0); p_5 = abs (p - 0.5) < eps (class (x)) & k > 0 & isfinite (mu); x(p_5) = mu(p_5); p_1 = 1 - p < eps (class (x)) & k > 0 & isfinite (mu); x(p_1) = pi + mu(p_1); ## Get remaining valid cases valc = p > 0 & p < 1 & ! p_5 & k > 0 & isfinite (mu); if (! any (valc)) return endif p = p(valc); mu = mu(valc); k = k(valc); ## Complement cases of p<0.5 to 1-p and keep track to invert them at the end comp = p < 0.5; p(comp) = 1 - p(comp); ## Initialize counter and threshold crit = 1e-5; count_limit = 100; count = 0; ## Supply a starting guess for the iteration by linear interpolation with k=0 x0 = 2 * pi .* p - pi; xz = zeros (size (p)); xa = xz; ## Compute p0 and compare to target p p0 = vmcdf (x0, 0, k); ## Solution is always 0 < x < x0. Search for x until p == p0 within threshold while (any (abs (p - p0) > crit) && count < count_limit) count = count + 1; xnew = xz + (abs (x0) - abs (xz)) .* 0.5; p0 = vmcdf (xnew, 0, k); ## Prepare for next step xdec = (p0 - p) > crit; xinc = (p - p0) > crit; if (any (xdec)) x0(xdec) = xnew(xdec); endif if (any (xinc)) xz(xinc) = xnew(xinc); endif endwhile ## Return the converged value(s). xnew(comp) = -xnew(comp); x(valc) = xnew + mu; if (count == count_limit) warning ("vminv: did not converge."); endif endfunction %!demo %! ## Plot various iCDFs from the von Mises distribution %! p1 = [0,0.005,0.01:0.01:0.1,0.15,0.2:0.1:0.8,0.85,0.9:0.01:0.99,0.995,1]; %! x1 = vminv (p1, 0, 0.5); %! x2 = vminv (p1, 0, 1); %! x3 = vminv (p1, 0, 2); %! x4 = vminv (p1, 0, 4); %! plot (p1, x1, "-r", p1, x2, "-g", p1, x3, "-b", p1, x4, "-c") %! grid on %! ylim ([-pi, pi]) %! legend ({"μ = 0, k = 0.5", "μ = 0, k = 1", ... %! "μ = 0, k = 2", "μ = 0, k = 4"}, "location", "northwest") %! title ("Von Mises iCDF") %! xlabel ("probability") %! ylabel ("values in x") ## Test output %!shared x, p0, p1 %! x = [-pi:pi/2:pi]; %! p0 = [0, 0.10975, 0.5, 0.89025, 1]; %! p1 = [0, 0.03752, 0.5, 0.99622, 1]; %!assert (vminv (p0, 0, 1), x, 5e-5) %!assert (vminv (p0, zeros (1,5), ones (1,5)), x, 5e-5) %!assert (vminv (p1, 0, [1 2 3 4 5]), x, [5e-5, 5e-4, 5e-5, 5e-4, 5e-5]) ## Test input validation %!error vminv () %!error vminv (1) %!error vminv (1, 2) %!error ... %! vminv (ones (3), ones (2), ones (2)) %!error ... %! vminv (ones (2), ones (3), ones (2)) %!error ... %! vminv (ones (2), ones (2), ones (3)) %!error vminv (i, 2, 2) %!error vminv (2, i, 2) %!error vminv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/vmpdf.m000066400000000000000000000102621475240274700214310ustar00rootroot00000000000000## Copyright (C) 2009 Soren Hauberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} vmpdf (@var{x}, @var{mu}, @var{k}) ## ## Von Mises probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the von Mises distribution with location parameter @var{mu} and ## concentration parameter @var{k} on the interval [-pi, pi]. The size of ## @var{y} is the common size of @var{x}, @var{mu}, and @var{k}. A scalar input ## functions as a constant matrix of the same size as the other inputs. ## ## Further information about the von Mises distribution can be found at ## @url{https://en.wikipedia.org/wiki/Von_Mises_distribution} ## ## @seealso{vmcdf, vminv, vmrnd} ## @end deftypefn function y = vmpdf (x, mu, k) ## Check for valid number of input arguments if (nargin < 3) error ("vmpdf: function called with too few input arguments."); endif ## Check for common size of X, MU, and K if (! isscalar (x) || ! isscalar (mu) || ! isscalar (k)) [retval, x, mu, k] = common_size (x, mu, k); if (retval > 0) error ("vmpdf: X, MU, and K must be of common size or scalars."); endif endif ## Check for X, MU, and K being reals if (iscomplex (x) || iscomplex (mu) || iscomplex (k)) error ("vmpdf: X, MU, and K must not be complex."); endif ## Evaluate Von Mises PDF Z = 2 .* pi .* besseli (0, k); y = exp (k .* cos (x - mu)) ./ Z; ## Force Nan for negative K y(k < 0) = NaN; ## Check for class type if (isa (x, "single") || isa (mu, "single") || isa (k, "single")) y = cast (y, "single"); else y = cast (y, "double"); endif endfunction %!demo %! ## Plot various PDFs from the von Mises distribution %! x1 = [-pi:0.1:pi]; %! y1 = vmpdf (x1, 0, 0.5); %! y2 = vmpdf (x1, 0, 1); %! y3 = vmpdf (x1, 0, 2); %! y4 = vmpdf (x1, 0, 4); %! plot (x1, y1, "-r", x1, y2, "-g", x1, y3, "-b", x1, y4, "-c") %! grid on %! xlim ([-pi, pi]) %! ylim ([0, 0.8]) %! legend ({"μ = 0, k = 0.5", "μ = 0, k = 1", ... %! "μ = 0, k = 2", "μ = 0, k = 4"}, "location", "northwest") %! title ("Von Mises PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x, y0, y1 %! x = [-pi:pi/2:pi]; %! y0 = [0.046245, 0.125708, 0.341710, 0.125708, 0.046245]; %! y1 = [0.046245, 0.069817, 0.654958, 0.014082, 0.000039]; %!assert (vmpdf (x, 0, 1), y0, 1e-5) %!assert (vmpdf (x, zeros (1,5), ones (1,5)), y0, 1e-6) %!assert (vmpdf (x, 0, [1 2 3 4 5]), y1, 1e-6) ## Test class of input preserved %!assert (isa (vmpdf (single (pi), 0, 1), "single"), true) %!assert (isa (vmpdf (pi, single (0), 1), "single"), true) %!assert (isa (vmpdf (pi, 0, single (1)), "single"), true) ## Test input validation %!error vmpdf () %!error vmpdf (1) %!error vmpdf (1, 2) %!error ... %! vmpdf (ones (3), ones (2), ones (2)) %!error ... %! vmpdf (ones (2), ones (3), ones (2)) %!error ... %! vmpdf (ones (2), ones (2), ones (3)) %!error vmpdf (i, 2, 2) %!error vmpdf (2, i, 2) %!error vmpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/vmrnd.m000066400000000000000000000164461475240274700214550ustar00rootroot00000000000000## Copyright (C) 2009 Soren Hauberg ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} vmrnd (@var{mu}, @var{k}) ## @deftypefnx {statistics} {@var{r} =} vmrnd (@var{mu}, @var{k}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} vmrnd (@var{mu}, @var{k}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} vmrnd (@var{mu}, @var{k}, [@var{sz}]) ## ## Random arrays from the von Mises distribution. ## ## @code{@var{r} = vmrnd (@var{mu}, @var{k})} returns an array of random angles ## chosen from a von Mises distribution with location parameter @var{mu} and ## concentration parameter @var{k} on the interval [-pi, pi]. The size of ## @var{r} is the common size of @var{mu} and @var{k}. A scalar input functions ## as a constant matrix of the same size as the other inputs. Both parameters ## must be finite real numbers and @var{k} > 0, otherwise NaN is returned. ## ## When called with a single size argument, @code{vmrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the von Mises distribution can be found at ## @url{https://en.wikipedia.org/wiki/Von_Mises_distribution} ## ## @seealso{vmcdf, vminv, vmpdf} ## @end deftypefn function r = vmrnd (mu, k, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("vmrnd: function called with too few input arguments."); endif ## Check for common size of MU and Κ if (! isscalar (mu) || ! isscalar (k)) [retval, mu, k] = common_size (mu, k); if (retval > 0) error ("vmrnd: MU and K must be of common size or scalars."); endif endif ## Check for MU and Κ being reals if (iscomplex (mu) || iscomplex (k)) error ("vmrnd: MU and K must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (mu); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["vmrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("vmrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (mu) && ! isequal (size (k), sz)) error ("vmrnd: MU and K must be scalars or of size SZ."); endif ## Check for class type if (isa (mu, "single") || isa (k, "single")) cls = "single"; else cls = "double"; endif ## Handle zero size dimensions if (any (sz == 0)) r = nan (sz, cls); return endif ## Simulate! if (all (k < 1e-6)) ## k is small: sample uniformly on circle r = mu + (2 * pi * rand (sz) - pi); else a = 1 + sqrt (1 + 4 .* k .^ 2); b = (a - sqrt (2 .* a)) ./ (2 .* k); r_tmp = (1 + b .^ 2) ./ (2 .* b); N = prod (sz); if (isscalar (k)) r_tmp = repmat (r_tmp, 1, N); k_tmp = repmat (k, 1, N); mu_rs = repmat (mu, 1, N); else r_tmp = reshape (r_tmp, 1, N); k_tmp = reshape (k, 1, N); mu_rs = reshape (mu, 1, N); endif notdone = true (N, 1); while (any (notdone)) u (:, notdone) = (rand (3, N))(:,notdone); z (notdone) = (cos (pi .* u (1, :)))(notdone); f (notdone) = ((1 + r_tmp(notdone) .* z(notdone)) ./ (r_tmp(notdone) + z(notdone))); c (notdone) = (k_tmp(notdone) .* (r_tmp(notdone) - f(notdone))); notdone = (u (2, :) >= c .* (2 - c)) & (log (c) - log (u (2, :)) + 1 - c < 0); #N = sum (notdone); endwhile r = mu_rs + sign (u (3, :) - 0.5) .* acos (f); r = reshape (r, sz); endif ## Cast to appropriate class r = cast (r, cls); endfunction ## Test output %!assert (size (vmrnd (1, 1)), [1 1]) %!assert (size (vmrnd (1, ones (2,1))), [2, 1]) %!assert (size (vmrnd (1, ones (2,2))), [2, 2]) %!assert (size (vmrnd (ones (2,1), 1)), [2, 1]) %!assert (size (vmrnd (ones (2,2), 1)), [2, 2]) %!assert (size (vmrnd (1, 1, 3)), [3, 3]) %!assert (size (vmrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (vmrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (vmrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (vmrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (vmrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (vmrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (vmrnd (1, 1)), "double") %!assert (class (vmrnd (1, single (1))), "single") %!assert (class (vmrnd (1, single ([1, 1]))), "single") %!assert (class (vmrnd (single (1), 1)), "single") %!assert (class (vmrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error vmrnd () %!error vmrnd (1) %!error ... %! vmrnd (ones (3), ones (2)) %!error ... %! vmrnd (ones (2), ones (3)) %!error vmrnd (i, 2, 3) %!error vmrnd (1, i, 3) %!error ... %! vmrnd (1, 2, -1) %!error ... %! vmrnd (1, 2, 1.2) %!error ... %! vmrnd (1, 2, ones (2)) %!error ... %! vmrnd (1, 2, [2 -1 2]) %!error ... %! vmrnd (1, 2, [2 0 2.5]) %!error ... %! vmrnd (1, 2, 2, -1, 5) %!error ... %! vmrnd (1, 2, 2, 1.5, 5) %!error ... %! vmrnd (2, ones (2), 3) %!error ... %! vmrnd (2, ones (2), [3, 2]) %!error ... %! vmrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/wblcdf.m000066400000000000000000000211761475240274700215640ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} wblcdf (@var{x}) ## @deftypefnx {statistics} {@var{p} =} wblcdf (@var{x}, @var{lambda}) ## @deftypefnx {statistics} {@var{p} =} wblcdf (@var{x}, @var{lambda}, @var{k}) ## @deftypefnx {statistics} {@var{p} =} wblcdf (@dots{}, @qcode{"upper"}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} wblcdf (@var{x}, @var{lambda}, @var{k}, @var{pcov}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} wblcdf (@var{x}, @var{lambda}, @var{k}, @var{pcov}, @var{alpha}) ## @deftypefnx {statistics} {[@var{p}, @var{plo}, @var{pup}] =} wblcdf (@dots{}, @qcode{"upper"}) ## ## Weibull cumulative distribution function (CDF). ## ## For each element of @var{x}, compute the cumulative distribution function ## (CDF) of the Weibull distribution with scale parameter @var{lambda} and shape ## parameter @var{k}. The size of @var{p} is the common size of @var{x}, ## @var{lambda} and @var{k}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Default values are @var{lambda} = 1, @var{k} = 1. ## ## When called with three output arguments, @code{[@var{p}, @var{plo}, ## @var{pup}]} it computes the confidence bounds for @var{p} when the input ## parameters @var{lambda} and @var{k} are estimates. In such case, @var{pcov}, ## a 2-by-2 matrix containing the covariance matrix of the estimated parameters, ## is necessary. Optionally, @var{alpha} has a default value of 0.05, and ## specifies 100 * (1 - @var{alpha})% confidence bounds. @var{plo} and @var{pup} ## are arrays of the same size as @var{p} containing the lower and upper ## confidence bounds. ## ## @code{[@dots{}] = wblcdf (@dots{}, "upper")} computes the upper tail ## probability of the lognormal distribution. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblinv, wblpdf, wblrnd, wblstat, wblplot} ## @end deftypefn function [varargout] = wblcdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 6) error ("wblcdf: invalid number of input arguments."); endif ## Check for "upper" flag if (nargin > 1 && strcmpi (varargin{end}, "upper")) uflag = true; varargin(end) = []; elseif (nargin > 1 && ischar (varargin{end}) && ... ! strcmpi (varargin{end}, "upper")) error ("wblcdf: invalid argument for upper tail."); elseif (nargin > 1 && isempty (varargin{end})) uflag = false; varargin(end) = []; else uflag = false; endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) lambda = varargin{1}; else lambda = 1; endif if (numel (varargin) > 1) k = varargin{2}; else k = 1; endif if (numel (varargin) > 2) pcov = varargin{3}; ## Check for valid covariance matrix 2x2 if (! isequal (size (pcov), [2, 2])) error ("wblcdf: invalid size of covariance matrix."); endif else ## Check that cov matrix is provided if 3 output arguments are requested if (nargout > 1) error ("wblcdf: covariance matrix is required for confidence bounds."); endif pcov = []; endif if (numel (varargin) > 3) alpha = varargin{4}; ## Check for valid alpha value if (! isnumeric (alpha) || numel (alpha) !=1 || alpha <= 0 || alpha >= 1) error ("wblcdf: invalid value for alpha."); endif else alpha = 0.05; endif ## Check for common size of X, LAMBDA, and K if (! isscalar (x) || ! isscalar (lambda) || ! isscalar (k)) [err, x, lambda, k] = common_size (x, lambda, k); if (err > 0) error ("wblcdf: X, LAMBDA, and K must be of common size or scalars."); endif endif ## Check for X, LAMBDA, and K being reals if (iscomplex (x) || iscomplex (lambda) || iscomplex (k)) error ("wblcdf: X, LAMBDA, and K must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (lambda, "single") || isa (k, "single")); is_class = "single"; else is_class = "double"; endif ## Return NaN for out of range parameters. lambda(lambda <= 0) = NaN; k(k <= 0) = NaN; ## Force 0 for negative data x(x < 0) = 0; ## Compute z z = (x ./ lambda) .^ k; if (uflag) p = exp(-z); else p = -expm1(-z); endif ## Compute confidence bounds (if requested) if (nargout >= 2) ## Work on log scale log_z = log (z); d_lambda = 1 ./ lambda; d_k = -1 ./ (k .^ 2); log_zvar = (pcov(1,1) .* d_lambda .^ 2 + ... 2 * pcov(1,2) .* d_lambda .* d_k .* log_z + ... pcov(2,2) .* (d_k .* log_z) .^ 2) .* (k .^ 2); if (any(log_zvar < 0)) error ("wblcdf: bad covariance matrix."); endif normz = -norminv (alpha / 2); halfwidth = normz * sqrt (log_zvar); zlo = log_z - halfwidth; zup = log_z + halfwidth; ## Convert back from log scale if uflag == true plo = exp (-exp (zup)); pup = exp (-exp (zlo)); else plo = -expm1 (-exp (zlo)); pup = -expm1 (-exp (zup)); endif endif ## Prepare output varargout{1} = cast (p, is_class); if (nargout > 1) varargout{2} = cast (plo, is_class); varargout{3} = cast (pup, is_class); endif endfunction %!demo %! ## Plot various CDFs from the Weibull distribution %! x = 0:0.001:2.5; %! p1 = wblcdf (x, 1, 0.5); %! p2 = wblcdf (x, 1, 1); %! p3 = wblcdf (x, 1, 1.5); %! p4 = wblcdf (x, 1, 5); %! plot (x, p1, "-b", x, p2, "-r", x, p3, "-m", x, p4, "-g") %! grid on %! legend ({"λ = 1, k = 0.5", "λ = 1, k = 1", ... %! "λ = 1, k = 1.5", "λ = 1, k = 5"}, "location", "southeast") %! title ("Weibull CDF") %! xlabel ("values in x") %! ylabel ("probability") ## Test output %!shared x, y %! x = [-1 0 0.5 1 Inf]; %! y = [0, 1-exp(-x(2:4)), 1]; %!assert (wblcdf (x, ones (1,5), ones (1,5)), y, 1e-16) %!assert (wblcdf (x, ones (1,5), ones (1,5), "upper"), 1 - y) %!assert (wblcdf (x, "upper"), 1 - y) %!assert (wblcdf (x, 1, ones (1,5)), y, 1e-16) %!assert (wblcdf (x, ones (1,5), 1), y, 1e-16) %!assert (wblcdf (x, [0 1 NaN Inf 1], 1), [NaN 0 NaN 0 1]) %!assert (wblcdf (x, [0 1 NaN Inf 1], 1, "upper"), 1 - [NaN 0 NaN 0 1]) %!assert (wblcdf (x, 1, [0 1 NaN Inf 1]), [NaN 0 NaN y(4:5)]) %!assert (wblcdf (x, 1, [0 1 NaN Inf 1], "upper"), 1 - [NaN 0 NaN y(4:5)]) %!assert (wblcdf ([x(1:2) NaN x(4:5)], 1, 1), [y(1:2) NaN y(4:5)]) %!assert (wblcdf ([x(1:2) NaN x(4:5)], 1, 1, "upper"), 1 - [y(1:2) NaN y(4:5)]) ## Test class of input preserved %!assert (wblcdf ([x, NaN], 1, 1), [y, NaN], 1e-16) %!assert (wblcdf (single ([x, NaN]), 1, 1), single ([y, NaN])) %!assert (wblcdf ([x, NaN], single (1), 1), single ([y, NaN])) %!assert (wblcdf ([x, NaN], 1, single (1)), single ([y, NaN])) ## Test input validation %!error wblcdf () %!error wblcdf (1,2,3,4,5,6,7) %!error wblcdf (1, 2, 3, 4, "uper") %!error ... %! wblcdf (ones (3), ones (2), ones (2)) %!error wblcdf (2, 3, 4, [1, 2]) %!error ... %! [p, plo, pup] = wblcdf (1, 2, 3) %!error [p, plo, pup] = ... %! wblcdf (1, 2, 3, [1, 0; 0, 1], 0) %!error [p, plo, pup] = ... %! wblcdf (1, 2, 3, [1, 0; 0, 1], 1.22) %!error [p, plo, pup] = ... %! wblcdf (1, 2, 3, [1, 0; 0, 1], "alpha", "upper") %!error wblcdf (i, 2, 2) %!error wblcdf (2, i, 2) %!error wblcdf (2, 2, i) %!error ... %! [p, plo, pup] =wblcdf (1, 2, 3, [1, 0; 0, -inf], 0.04) statistics-release-1.7.3/inst/dist_fun/wblinv.m000066400000000000000000000120621475240274700216160ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} wblinv (@var{p}) ## @deftypefnx {statistics} {@var{x} =} wblinv (@var{p}, @var{lambda}) ## @deftypefnx {statistics} {@var{x} =} wblinv (@var{p}, @var{lambda}, @var{k}) ## ## Inverse of the Weibull cumulative distribution function (iCDF). ## ## For each element of @var{p}, compute the quantile (the inverse of the CDF) ## of the Weibull distribution with scale parameter @var{lambda} and shape ## parameter @var{k}. The size of @var{x} is the common size of @var{p}, ## @var{lambda}, and @var{k}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Default values are @var{lambda} = 1, @var{k} = 1. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblcdf, wblpdf, wblrnd, wblstat, wblplot} ## @end deftypefn function x = wblinv (p, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 3) error ("wblinv: invalid number of input arguments."); endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) lambda = varargin{1}; else lambda = 1; endif if (numel (varargin) > 1) k = varargin{2}; else k = 1; endif ## Check for common size of P, LAMBDA, and K if (! isscalar (p) || ! isscalar (lambda) || ! isscalar (k)) [retval, p, lambda, k] = common_size (p, lambda, k); if (retval > 0) error ("wblinv: P, LAMBDA, and K must be of common size or scalars."); endif endif ## Check for P, LAMBDA, and K being reals if (iscomplex (p) || iscomplex (lambda) || iscomplex (k)) error ("wblinv: P, LAMBDA, and K must not be complex."); endif ## Check for class type if (isa (p, "single") || isa (lambda, "single") || isa (k, "single")) x = NaN (size (p), "single"); else x = NaN (size (p)); endif ok = (lambda > 0) & (lambda < Inf) & (k > 0) & (k < Inf); pk = (p == 0) & ok; x(pk) = 0; pk = (p == 1) & ok; x(pk) = Inf; pk = (p > 0) & (p < 1) & ok; if (isscalar (lambda) && isscalar (k)) x(pk) = lambda * (- log (1 - p(pk))) .^ (1 / k); else x(pk) = lambda(pk) .* (- log (1 - p(pk))) .^ (1 ./ k(pk)); endif endfunction %!demo %! ## Plot various iCDFs from the Weibull distribution %! p = 0.001:0.001:0.999; %! x1 = wblinv (p, 1, 0.5); %! x2 = wblinv (p, 1, 1); %! x3 = wblinv (p, 1, 1.5); %! x4 = wblinv (p, 1, 5); %! plot (p, x1, "-b", p, x2, "-r", p, x3, "-m", p, x4, "-g") %! ylim ([0, 2.5]) %! grid on %! legend ({"λ = 1, k = 0.5", "λ = 1, k = 1", ... %! "λ = 1, k = 1.5", "λ = 1, k = 5"}, "location", "northwest") %! title ("Weibull iCDF") %! xlabel ("probability") %! ylabel ("x") ## Test output %!shared p %! p = [-1 0 0.63212055882855778 1 2]; %!assert (wblinv (p, ones (1,5), ones (1,5)), [NaN 0 1 Inf NaN], eps) %!assert (wblinv (p, 1, ones (1,5)), [NaN 0 1 Inf NaN], eps) %!assert (wblinv (p, ones (1,5), 1), [NaN 0 1 Inf NaN], eps) %!assert (wblinv (p, [1 -1 NaN Inf 1], 1), [NaN NaN NaN NaN NaN]) %!assert (wblinv (p, 1, [1 -1 NaN Inf 1]), [NaN NaN NaN NaN NaN]) %!assert (wblinv ([p(1:2) NaN p(4:5)], 1, 1), [NaN 0 NaN Inf NaN]) ## Test class of input preserved %!assert (wblinv ([p, NaN], 1, 1), [NaN 0 1 Inf NaN NaN], eps) %!assert (wblinv (single ([p, NaN]), 1, 1), single ([NaN 0 1 Inf NaN NaN]), eps ("single")) %!assert (wblinv ([p, NaN], single (1), 1), single ([NaN 0 1 Inf NaN NaN]), eps ("single")) %!assert (wblinv ([p, NaN], 1, single (1)), single ([NaN 0 1 Inf NaN NaN]), eps ("single")) ## Test input validation %!error wblinv () %!error wblinv (1,2,3,4) %!error ... %! wblinv (ones (3), ones (2), ones (2)) %!error ... %! wblinv (ones (2), ones (3), ones (2)) %!error ... %! wblinv (ones (2), ones (2), ones (3)) %!error wblinv (i, 2, 2) %!error wblinv (2, i, 2) %!error wblinv (2, 2, i) statistics-release-1.7.3/inst/dist_fun/wblpdf.m000066400000000000000000000110111475240274700215640ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} wblinv (@var{x}) ## @deftypefnx {statistics} {@var{y} =} wblinv (@var{x}, @var{lambda}) ## @deftypefnx {statistics} {@var{y} =} wblinv (@var{x}, @var{lambda}, @var{k}) ## ## Weibull probability density function (PDF). ## ## For each element of @var{x}, compute the probability density function (PDF) ## of the Weibull distribution with scale parameter @var{lambda} and shpe ## parameter @var{k}. The size of @var{y} is the common size of @var{x}, ## @var{lambda}, and @var{k}. A scalar input functions as a constant matrix of ## the same size as the other inputs. ## ## Default values are @var{lambda} = 1, @var{k} = 1. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblcdf, wblinv, wblrnd, wblfit, wbllike, wblstat, wblplot} ## @end deftypefn function y = wblpdf (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 3) error ("wblpdf: invalid number of input arguments."); endif ## Get extra arguments (if they exist) or add defaults if (numel (varargin) > 0) lambda = varargin{1}; else lambda = 1; endif if (numel (varargin) > 1) k = varargin{2}; else k = 1; endif ## Check for common size of X, LAMBDA, and K if (! isscalar (lambda) || ! isscalar (k)) [retval, x, lambda, k] = common_size (x, lambda, k); if (retval > 0) error ("wblpdf: X, LAMBDA, and K must be of common size or scalars."); endif endif ## Check for X, LAMBDA, and K being reals if (iscomplex (x) || iscomplex (lambda) || iscomplex (k)) error ("wblpdf: X, LAMBDA, and K must not be complex."); endif ## Check for class type if (isa (x, "single") || isa (lambda, "single") || isa (k, "single")) y = NaN (size (x), "single"); else y = NaN (size (x)); endif ok = ((lambda > 0) & (lambda < Inf) & (k > 0) & (k < Inf)); xk = (x < 0) & ok; y(xk) = 0; xk = (x >= 0) & (x < Inf) & ok; if (isscalar (lambda) && isscalar (k)) y(xk) = (k * (lambda .^ -k) ... .* (x(xk) .^ (k - 1)) ... .* exp (- (x(xk) / lambda) .^ k)); else y(xk) = (k(xk) .* (lambda(xk) .^ -k(xk)) ... .* (x(xk) .^ (k(xk) - 1)) ... .* exp (- (x(xk) ./ lambda(xk)) .^ k(xk))); endif endfunction %!demo %! ## Plot various PDFs from the Weibul distribution %! x = 0:0.001:2.5; %! y1 = wblpdf (x, 1, 0.5); %! y2 = wblpdf (x, 1, 1); %! y3 = wblpdf (x, 1, 1.5); %! y4 = wblpdf (x, 1, 5); %! plot (x, y1, "-b", x, y2, "-r", x, y3, "-m", x, y4, "-g") %! grid on %! ylim ([0, 2.5]) %! legend ({"λ = 5, k = 0.5", "λ = 9, k = 1", ... %! "λ = 6, k = 1.5", "λ = 2, k = 5"}, "location", "northeast") %! title ("Weibul PDF") %! xlabel ("values in x") %! ylabel ("density") ## Test output %!shared x,y %! x = [-1 0 0.5 1 Inf]; %! y = [0, exp(-x(2:4)), NaN]; %!assert (wblpdf (x, ones (1,5), ones (1,5)), y) %!assert (wblpdf (x, 1, ones (1,5)), y) %!assert (wblpdf (x, ones (1,5), 1), y) %!assert (wblpdf (x, [0 NaN Inf 1 1], 1), [NaN NaN NaN y(4:5)]) %!assert (wblpdf (x, 1, [0 NaN Inf 1 1]), [NaN NaN NaN y(4:5)]) %!assert (wblpdf ([x, NaN], 1, 1), [y, NaN]) ## Test class of input preserved %!assert (wblpdf (single ([x, NaN]), 1, 1), single ([y, NaN])) %!assert (wblpdf ([x, NaN], single (1), 1), single ([y, NaN])) %!assert (wblpdf ([x, NaN], 1, single (1)), single ([y, NaN])) ## Test input validation %!error wblpdf () %!error wblpdf (1,2,3,4) %!error wblpdf (ones (3), ones (2), ones (2)) %!error wblpdf (ones (2), ones (3), ones (2)) %!error wblpdf (ones (2), ones (2), ones (3)) %!error wblpdf (i, 2, 2) %!error wblpdf (2, i, 2) %!error wblpdf (2, 2, i) statistics-release-1.7.3/inst/dist_fun/wblrnd.m000066400000000000000000000152271475240274700216130ustar00rootroot00000000000000## Copyright (C) 2012 Rik Wehbring ## Copyright (C) 1995-2016 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} wblrnd (@var{lambda}, @var{k}) ## @deftypefnx {statistics} {@var{r} =} wblrnd (@var{lambda}, @var{k}, @var{rows}) ## @deftypefnx {statistics} {@var{r} =} wblrnd (@var{lambda}, @var{k}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} wblrnd (@var{lambda}, @var{k}, [@var{sz}]) ## ## Random arrays from the Weibull distribution. ## ## @code{@var{r} = wblrnd (@var{lambda}, @var{k})} returns an array of random ## numbers chosen from the Weibull distribution with scale parameter ## @var{lambda} and shape parameter @var{k}. The size of @var{r} is the common ## size of @var{lambda} and @var{k}. A scalar input functions as a constant ## matrix of the same size as the other inputs. Both parameters must be ## positive reals. ## ## When called with a single size argument, @code{wblrnd} returns a square ## matrix with the dimension specified. When called with more than one scalar ## argument, the first two arguments are taken as the number of rows and columns ## and any further arguments specify additional matrix dimensions. The size may ## also be specified with a row vector of dimensions, @var{sz}. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblcdf, wblinv, wblpdf, wblfit, wbllike, wblstat, wblplot} ## @end deftypefn function r = wblrnd (lambda, k, varargin) ## Check for valid number of input arguments if (nargin < 2) error ("wblrnd: function called with too few input arguments."); endif ## Check for common size of LAMBDA and K if (! isscalar (lambda) || ! isscalar (k)) [retval, lambda, k] = common_size (lambda, k); if (retval > 0) error ("wblrnd: LAMBDA and K must be of common size or scalars."); endif endif ## Check for LAMBDA and K being reals if (iscomplex (lambda) || iscomplex (k)) error ("wblrnd: LAMBDA and K must not be complex."); endif ## Parse and check SIZE arguments if (nargin == 2) sz = size (lambda); elseif (nargin == 3) if (isscalar (varargin{1}) && varargin{1} >= 0 ... && varargin{1} == fix (varargin{1})) sz = [varargin{1}, varargin{1}]; elseif (isrow (varargin{1}) && all (varargin{1} >= 0) ... && all (varargin{1} == fix (varargin{1}))) sz = varargin{1}; elseif error (strcat (["wblrnd: SZ must be a scalar or a row vector"], ... [" of non-negative integers."])); endif elseif (nargin > 3) posint = cellfun (@(x) (! isscalar (x) || x < 0 || x != fix (x)), varargin); if (any (posint)) error ("wblrnd: dimensions must be non-negative integers."); endif sz = [varargin{:}]; endif ## Check that parameters match requested dimensions in size if (! isscalar (lambda) && ! isequal (size (lambda), sz)) error ("wblrnd: LAMBDA and K must be scalar or of size SZ."); endif ## Check for class type if (isa (lambda, "single") || isa (k, "single")) cls = "single"; else cls = "double"; endif ## Generate random sample from Weibull distribution if (isscalar (lambda) && isscalar (k)) if ((lambda > 0) && (lambda < Inf) && (k > 0) && (k < Inf)) r = lambda * rande (sz, cls) .^ (1/k); else r = NaN (sz, cls); endif else r = lambda .* rande (sz, cls) .^ (1./k); is_nan = (lambda <= 0) | (lambda == Inf) | (k <= 0) | (k == Inf); r(is_nan) = NaN; endif endfunction ## Test output %!assert (size (wblrnd (1, 1)), [1 1]) %!assert (size (wblrnd (1, ones (2,1))), [2, 1]) %!assert (size (wblrnd (1, ones (2,2))), [2, 2]) %!assert (size (wblrnd (ones (2,1), 1)), [2, 1]) %!assert (size (wblrnd (ones (2,2), 1)), [2, 2]) %!assert (size (wblrnd (1, 1, 3)), [3, 3]) %!assert (size (wblrnd (1, 1, [4, 1])), [4, 1]) %!assert (size (wblrnd (1, 1, 4, 1)), [4, 1]) %!assert (size (wblrnd (1, 1, 4, 1, 5)), [4, 1, 5]) %!assert (size (wblrnd (1, 1, 0, 1)), [0, 1]) %!assert (size (wblrnd (1, 1, 1, 0)), [1, 0]) %!assert (size (wblrnd (1, 1, 1, 2, 0, 5)), [1, 2, 0, 5]) ## Test class of input preserved %!assert (class (wblrnd (1, 1)), "double") %!assert (class (wblrnd (1, single (1))), "single") %!assert (class (wblrnd (1, single ([1, 1]))), "single") %!assert (class (wblrnd (single (1), 1)), "single") %!assert (class (wblrnd (single ([1, 1]), 1)), "single") ## Test input validation %!error wblrnd () %!error wblrnd (1) %!error ... %! wblrnd (ones (3), ones (2)) %!error ... %! wblrnd (ones (2), ones (3)) %!error wblrnd (i, 2, 3) %!error wblrnd (1, i, 3) %!error ... %! wblrnd (1, 2, -1) %!error ... %! wblrnd (1, 2, 1.2) %!error ... %! wblrnd (1, 2, ones (2)) %!error ... %! wblrnd (1, 2, [2 -1 2]) %!error ... %! wblrnd (1, 2, [2 0 2.5]) %!error ... %! wblrnd (1, 2, 2, -1, 5) %!error ... %! wblrnd (1, 2, 2, 1.5, 5) %!error ... %! wblrnd (2, ones (2), 3) %!error ... %! wblrnd (2, ones (2), [3, 2]) %!error ... %! wblrnd (2, ones (2), 3, 2) statistics-release-1.7.3/inst/dist_fun/wienrnd.m000066400000000000000000000040631475240274700217650ustar00rootroot00000000000000## Copyright (C) 1995-2017 Friedrich Leisch ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} wienrnd (@var{t}, @var{d}, @var{n}) ## ## Return a simulated realization of the @var{d}-dimensional Wiener Process ## on the interval [0, @var{t}]. ## ## If @var{d} is omitted, @var{d} = 1 is used. The first column of the ## return matrix contains time, the remaining columns contain the Wiener ## process. ## ## The optional parameter @var{n} defines the number of summands used for ## simulating the process over an interval of length 1. If @var{n} is ## omitted, @var{n} = 1000 is used. ## @end deftypefn function r = wienrnd (t, d, n) if (nargin == 1) d = 1; n = 1000; elseif (nargin == 2) n = 1000; elseif (nargin > 3) print_usage (); endif if (! isscalar (t) || ! isscalar (d) || ! isscalar (n)) error ("wienrnd: T, D, and N must all be scalars."); endif if (! (fix (t) == t) || ! (fix (d) == d) || ! (fix (n) == n) || t <= 0 || d <= 0 || n <= 0) error ("wienrnd: T, D, and N must all be positive integers."); endif r = randn (n * t, d); r = cumsum (r) / sqrt (n); r = [((1: n*t)' / n), r]; endfunction %!error wienrnd (0) %!error wienrnd (1, 3, -50) %!error wienrnd (5, 0) %!error wienrnd (0.4, 3, 5) %!error wienrnd ([1 4], 3, 5) statistics-release-1.7.3/inst/dist_fun/wishpdf.m000066400000000000000000000053101475240274700217570ustar00rootroot00000000000000## Copyright (C) 2013 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} wishpdf (@var{W}, @var{Sigma}, @var{df}, @var{log_y}=false) ## ## Compute the probability density function of the Wishart distribution ## ## Inputs: A @var{p} x @var{p} matrix @var{W} where to find the PDF. The @var{p} ## x @var{p} positive definite matrix @var{Sigma} and scalar degrees of freedom ## parameter @var{df} characterizing the Wishart distribution. (For the density ## to be finite, need @var{df} > (@var{p} - 1).) ## ## If the flag @var{log_y} is set, return the log probability density -- this ## helps avoid underflow when the numerical value of the density is very small ## ## Output: @var{y} is the probability density of Wishart(@var{Sigma}, @var{df}) ## at @var{W}. ## ## @seealso{wishrnd, iwishpdf, iwishrnd} ## @end deftypefn function y = wishpdf (W, Sigma, df, log_y=false) if (nargin < 3) print_usage (); endif p = size(Sigma, 1); if (df <= (p - 1)) error ("wishpdf: DF too small, no finite densities exist."); endif ## calculate the logarithm of G_d(df/2), the multivariate gamma function g = (p * (p-1) / 4) * log(pi); for i = 1:p g = g + log(gamma((df + (1 - i))/2)); endfor C = chol(Sigma); ## use formulas for determinant of positive definite matrix for better ## efficiency and numerical accuracy logdet_W = 2*sum(log(diag(chol(W)))); logdet_Sigma = 2*sum(log(diag(C))); y = -(df*p)/2 * log(2) - (df/2)*logdet_Sigma - g + ... ((df - p - 1)/2)*logdet_W - trace(chol2inv(C)*W)/2; if ~log_y y = exp(y); endif endfunction ##test results cross-checked against dwish function in R MCMCpack library %!assert(wishpdf(4, 3, 3.1), 0.07702496, 1E-7); %!assert(wishpdf([2 -0.3;-0.3 4], [1 0.3;0.3 1], 4), 0.004529741, 1E-7); %!assert(wishpdf([6 2 5; 2 10 -5; 5 -5 25], [9 5 5; 5 10 -8; 5 -8 22], 5.1), 4.474865e-10, 1E-15); %% Test input validation %!error wishpdf () %!error wishpdf (1, 2) %!error wishpdf (1, 2, 0) %!error wishpdf (1, 2) statistics-release-1.7.3/inst/dist_fun/wishrnd.m000066400000000000000000000067071475240274700220040ustar00rootroot00000000000000## Copyright (C) 2013-2019 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{W}, @var{D}] =} wishrnd (@var{Sigma}, @var{df}, @var{D}, @var{n}=1) ## ## Return a random matrix sampled from the Wishart distribution with given ## parameters ## ## Inputs: the @math{p x p} positive definite matrix @var{Sigma} (or the ## lower-triangular Cholesky factor @var{D} of @var{Sigma}) and scalar degrees ## of freedom parameter @var{df}. ## ## @var{df} can be non-integer as long as @math{@var{df} > p} ## ## Output: a random @math{p x p} matrix @var{W} from the ## Wishart(@var{Sigma}, @var{df}) distribution. If @var{n} > 1, then @var{W} is ## @var{p} x @var{p} x @var{n} and holds @var{n} such random matrices. ## (Optionally, the lower-triangular Cholesky factor @var{D} of @var{Sigma} is ## also returned.) ## ## Averaged across many samples, the mean of @var{W} should approach ## @var{df}*@var{Sigma}, and the variance of each element @var{W}_ij should ## approach @var{df}*(@var{Sigma}_ij^2 + @var{Sigma}_ii*@var{Sigma}_jj) ## ## @subheading References ## ## @enumerate ## @item ## Yu-Cheng Ku and Peter Bloomfield (2010), Generating Random Wishart Matrices ## with Fractional Degrees of Freedom in OX, ## http://www.gwu.edu/~forcpgm/YuChengKu-030510final-WishartYu-ChengKu.pdf ## @end enumerate ## ## @seealso{wishpdf, iwishpdf, iwishrnd} ## @end deftypefn function [W, D] = wishrnd (Sigma, df, D, n=1) if (nargin < 2) print_usage (); endif if nargin < 3 || isempty(D) try D = chol(Sigma, 'lower'); catch error (strcat (["iwishrnd: Cholesky decomposition failed;"], ... [" SIGMA probably not positive definite."])); end_try_catch endif p = size(D, 1); if df < p df = floor(df); #distribution not defined for small noninteger df df_isint = 1; else #check for integer degrees of freedom df_isint = (df == floor(df)); endif if ~df_isint [ii, jj] = ind2sub([p, p], 1:(p*p)); endif if n > 1 W = nan(p, p, n); endif for i = 1:n if df_isint Z = D * randn(p, df); else Z = diag(sqrt(chi2rnd(df - (0:(p-1))))); #fill diagonal ##note: chi2rnd(x) is equivalent to 2*randg(x/2), but the latter seems to ## offer no performance advantage Z(ii > jj) = randn(p*(p-1)/2, 1); #fill lower triangle Z = D * Z; endif W(:, :, i) = Z*Z'; endfor endfunction %!assert(size (wishrnd (1,2)), [1, 1]); %!assert(size (wishrnd (1,2,[])), [1, 1]); %!assert(size (wishrnd (1,2,1)), [1, 1]); %!assert(size (wishrnd ([],2,1)), [1, 1]); %!assert(size (wishrnd ([3 1; 1 3], 2.00001, [], 1)), [2, 2]); %!assert(size (wishrnd (eye(2), 2, [], 3)), [2, 2, 3]); %% Test input validation %!error wishrnd () %!error wishrnd (1) %!error wishrnd ([1; 1], 2) statistics-release-1.7.3/inst/dist_obj/000077500000000000000000000000001475240274700201205ustar00rootroot00000000000000statistics-release-1.7.3/inst/dist_obj/BetaDistribution.m000066400000000000000000001054401475240274700235550ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef BetaDistribution ## -*- texinfo -*- ## @deftypefn {statistics} BetaDistribution ## ## Beta probability distribution object. ## ## A @code{BetaDistribution} object consists of parameters, a model ## description, and sample data for a beta probability distribution. ## ## The beta distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{a} @tab 1st Shape parameter @tab @math{α > 0} ## @item @qcode{b} @tab 2nd Shape parameter @tab @math{β > 0} ## @end multitable ## ## There are several ways to create a @code{BetaDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{BetaDistribution (@var{a}, @var{b})} ## to create a beta distribution with specified parameter values. ## @item Use the static method @qcode{BetaDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{BetaDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{BetaDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{fitdist, makedist, betacdf, betainv, betapdf, betarnd, lognfit, ## betalike, betastat} ## @end deftypefn properties (Dependent = true) a b endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "BetaDistribution"; DistributionCode = "beta"; NumParameters = 2; ParameterNames = {"a", "b"}; ParameterDescription = {"1st Shape", "2nd Shape"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = BetaDistribution (a, b) if (nargin == 0) a = 1; b = 1; endif checkparams (a, b); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [a, b]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "beta distribution"); endfunction function disp (this) __disp__ (this, "beta distribution"); endfunction function this = set.a (this, a) checkparams (a, this.b); this.InputData = []; this.ParameterValues(1) = a; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function a = get.a (this) a = this.ParameterValues(1); endfunction function this = set.b (this, b) checkparams (this.a, b); this.InputData = []; this.ParameterValues(2) = b; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function b = get.b (this) b = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {BetaDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = betacdf (x, this.a, this.b); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= betacdf (lx, this.a, this.b); p(! (lb | ub)) /= diff (betacdf ([lx, ux], this.a, this.b)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = betacdf (this.Truncation(1), this.a, this.b); up = betacdf (this.Truncation(2), this.a, this.b); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = betainv (np, this.a, this.b); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = betainv (p, this.a, this.b); endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = betastat (this.a, this.b); endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = betacdf ([lx, ux], this.a, this.b); m = betainv (sum (Fa_b) / 2, this.a, this.b); else m = betainv (0.5, this.a, this.b); endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - betalike ([this.a, this.b], this.InputData.data, ... this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {BetaDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = betapdf (x, this.a, this.b); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (betacdf ([lx, ux], this.a, this.b)); endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {} plot (@var{pd}) ## @deftypefnx {BetaDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {BetaDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {BetaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {BetaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {BetaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the beta distribution, @qcode{@var{pnum} = 1} selects the parameter ## @qcode{a} and @qcode{@var{pnum} = 2} selects the parameter @var{b}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {BetaDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {BetaDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {BetaDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (betacdf ([lx, ux], this.a, this.b)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = betarnd (this.a, this.b, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = betarnd (this.a, this.b, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {BetaDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = betastat (this.a, this.b); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) freq = []; else freq = varargin{2}; endif if (nargin < 4) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{3}; endif ## Fit data [phat, pci] = betafit (x, alpha, freq, options); [~, acov] = betalike (phat, x, freq); ## Create fitted distribution object pd = BetaDistribution.makeFitted (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) a = phat(1); b = phat(2); pd = BetaDistribution (a, b); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (a, b) if (! (isscalar (a) && isnumeric (a) && isreal (a) && isfinite (a) && a > 0)) error ("BetaDistribution: A must be a positive real scalar.") endif if (! (isscalar (b) && isnumeric (b) && isreal (b) && isfinite (b) && b > 0)) error ("BetaDistribution: B must be a positive real scalar.") endif endfunction %!demo %! ## Generate a data set of 5000 random samples from a Beta distribution with %! ## parameters a = 2 and b = 4. Fit a Beta distribution to this data and plot %! ## a PDF of the fitted distribution superimposed on a histogram of the data %! %! pd = makedist ("Beta", "a", 2, "b", 4) %! randg ("seed", 21); %! data = random (pd, 5000, 1); %! pd = fitdist (data, "Beta") %! plot (pd) %! title (sprintf ("Fitted Beta distribution with a = %0.2f and b = %0.2f", ... %! pd.a, pd.b)) %!demo %! ## Plot the PDF of a Beta distribution, with parameters a = 2 and b = 4, %! ## truncated at [0.1, 0.8] intervals. Generate 10000 random samples from %! ## this truncated distribution and superimpose a histogram with 100 bins %! ## scaled accordingly %! %! pd = makedist ("Beta", "a", 2, "b", 4) %! t = truncate (pd, 0.1, 0.8) %! randg ("seed", 21); %! data = random (t, 10000, 1); %! plot (t) %! title ("Beta distribution (a = 2, b = 4) truncated at [0.1, 0.8]") %! hold on %! hist (data, 100, 140) %! hold off %!demo %! ## Generate a data set of 100 random samples from a Beta distribution with %! ## parameters a = 2 and b = 4. Fit a Beta distribution to this data and plot %! ## its CDF superimposed over an empirical CDF of the data %! %! pd = makedist ("Beta", "a", 2, "b", 4) %! randg ("seed", 21); %! data = random (pd, 100, 1); %! pd = fitdist (data, "Beta") %! plot (pd, "plottype", "cdf") %! title (sprintf ("Fitted Beta distribution with a = %0.2f and b = %0.2f", ... %! pd.a, pd.b)) %! legend ({"empirical CDF", "fitted CDF"}, "location", "east") %!demo %! ## Generate a data set of 200 random samples from a Beta distribution with %! ## parameters a = 2 and b = 4. Display a probability plot for the Beta %! ## distribution fit to the data. %! %! pd = makedist ("Beta", "a", 2, "b", 4) %! randg ("seed", 21); %! data = random (pd, 200, 1); %! pd = fitdist (data, "Beta") %! plot (pd, "plottype", "probability") %! title (sprintf ("Probability plot of a fitted Beta distribution with a = %0.2f and b = %0.2f", ... %! pd.a, pd.b)) %! legend ({"empirical CDF", "fitted CDF"}, "location", "southeast") ## Test output %!shared pd, t %! pd = BetaDistribution; %! t = truncate (pd, 0.2, 0.8); %!assert (cdf (pd, [0:0.2:1]), [0, 0.2, 0.4, 0.6, 0.8, 1], 1e-4); %!assert (cdf (t, [0:0.2:1]), [0, 0, 0.3333, 0.6667, 1, 1], 1e-4); %!assert (cdf (pd, [-1, 1, NaN]), [0, 1, NaN], 1e-4); %!assert (cdf (t, [-1, 1, NaN]), [0, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.2, 0.4, 0.6, 0.8, 1], 1e-4); %!assert (icdf (t, [0:0.2:1]), [0.2, 0.32, 0.44, 0.56, 0.68, 0.8], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.4, 0.6, 0.8, 1, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 0.44, 0.56, 0.68, 0.8, NaN], 1e-4); %!assert (iqr (pd), 0.5, 1e-4); %!assert (iqr (t), 0.3, 1e-4); %!assert (mean (pd), 0.5); %!assert (mean (t), 0.5, 1e-6); %!assert (median (pd), 0.5); %!assert (median (t), 0.5, 1e-6); %!assert (pdf (pd, [0:0.2:1]), [1, 1, 1, 1, 1, 1], 1e-4); %!assert (pdf (t, [0:0.2:1]), [0, 1.6667, 1.6667, 1.6667, 1.6667, 0], 1e-4); %!assert (pdf (pd, [-1, 1, NaN]), [0, 1, NaN], 1e-4); %!assert (pdf (t, [-1, 1, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 0.2), false); %!assert (any (random (t, 1000, 1) > 0.8), false); %!assert (std (pd), 0.2887, 1e-4); %!assert (std (t), 0.1732, 1e-4); %!assert (var (pd), 0.0833, 1e-4); %!assert (var (t), 0.0300, 1e-4); ## Test input validation ## 'BetaDistribution' constructor %!error ... %! BetaDistribution(0, 1) %!error ... %! BetaDistribution(Inf, 1) %!error ... %! BetaDistribution(i, 1) %!error ... %! BetaDistribution("a", 1) %!error ... %! BetaDistribution([1, 2], 1) %!error ... %! BetaDistribution(NaN, 1) %!error ... %! BetaDistribution(1, 0) %!error ... %! BetaDistribution(1, -1) %!error ... %! BetaDistribution(1, Inf) %!error ... %! BetaDistribution(1, i) %!error ... %! BetaDistribution(1, "a") %!error ... %! BetaDistribution(1, [1, 2]) %!error ... %! BetaDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (BetaDistribution, 2, "uper") %!error ... %! cdf (BetaDistribution, 2, 3) ## 'paramci' method %!shared x %! randg ("seed", 1); %! x = betarnd (1, 1, [100, 1]); %!error ... %! paramci (BetaDistribution.fit (x), "alpha") %!error ... %! paramci (BetaDistribution.fit (x), "alpha", 0) %!error ... %! paramci (BetaDistribution.fit (x), "alpha", 1) %!error ... %! paramci (BetaDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (BetaDistribution.fit (x), "alpha", "") %!error ... %! paramci (BetaDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (BetaDistribution.fit (x), "parameter", "a", "alpha", {0.05}) %!error ... %! paramci (BetaDistribution.fit (x), "parameter", {"a", "b", "param"}) %!error ... %! paramci (BetaDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"a", "b", "param"}) %!error ... %! paramci (BetaDistribution.fit (x), "parameter", "param") %!error ... %! paramci (BetaDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (BetaDistribution.fit (x), "NAME", "value") %!error ... %! paramci (BetaDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (BetaDistribution.fit (x), "alpha", 0.01, "parameter", "a", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (BetaDistribution, "Parent") %!error ... %! plot (BetaDistribution, "PlotType", 12) %!error ... %! plot (BetaDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (BetaDistribution, "PlotType", "pdfcdf") %!error ... %! plot (BetaDistribution, "Discrete", "pdfcdf") %!error ... %! plot (BetaDistribution, "Discrete", [1, 0]) %!error ... %! plot (BetaDistribution, "Discrete", {true}) %!error ... %! plot (BetaDistribution, "Parent", 12) %!error ... %! plot (BetaDistribution, "Parent", "hax") %!error ... %! plot (BetaDistribution, "invalidNAME", "pdf") %!error ... %! plot (BetaDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (BetaDistribution, 2) %!error ... %! proflik (BetaDistribution.fit (x), 3) %!error ... %! proflik (BetaDistribution.fit (x), [1, 2]) %!error ... %! proflik (BetaDistribution.fit (x), {1}) %!error ... %! proflik (BetaDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (BetaDistribution.fit (x), 1, "Display") %!error ... %! proflik (BetaDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (BetaDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (BetaDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (BetaDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (BetaDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (BetaDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (BetaDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (BetaDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (BetaDistribution) %!error ... %! truncate (BetaDistribution, 2) %!error ... %! truncate (BetaDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = BetaDistribution(1, 1); %! pd(2) = BetaDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/BinomialDistribution.m000066400000000000000000001055471475240274700244440ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef BinomialDistribution ## -*- texinfo -*- ## @deftypefn {statistics} BinomialDistribution ## ## Binomial probability distribution object. ## ## A @code{BinomialDistribution} object consists of parameters, a model ## description, and sample data for a binomial probability distribution. ## ## The binomial distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{N} @tab Number of trials @tab positive integer ## @item @qcode{p} @tab Probability of success @tab @math{0 <= p <= 1} ## @end multitable ## ## There are several ways to create a @code{BinomialDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{BinomialDistribution (@var{N}, @var{p})} ## to create a binomial distribution with specified parameter values. ## @item Use the static method @qcode{BinomialDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{BinomialDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{BinomialDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{fitdist, makedist, binocdf, binoinv, binopdf, binornd, binofit, ## binolike, binostat} ## @end deftypefn properties (Dependent = true) N p endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "BinomialDistribution"; DistributionCode = "bino"; NumParameters = 2; ParameterNames = {"N", "p"}; ParameterDescription = {"Number of trials", "Probability of success"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, 1]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = BinomialDistribution (N, p) if (nargin == 0) N = 1; p = 0.5; endif checkparams (N, p); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [N, p]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "binomial distribution"); endfunction function disp (this) __disp__ (this, "binomial distribution"); endfunction function this = set.N (this, N) checkparams (N, this.p); this.InputData = []; this.ParameterValues(1) = N; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function N = get.N (this) N = this.ParameterValues(1); endfunction function this = set.p (this, p) checkparams (this.N, p); this.InputData = []; this.ParameterValues(2) = p; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function p = get.p (this) p = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {BinomialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = binocdf (x, this.N, this.p); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= binocdf (lx - 1, this.N, this.p); p(! (lb | ub)) /= diff (binocdf ([lx-1, ux], this.N, this.p)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif umax = binoinv (1, this.N, this.p); if (this.IsTruncated && this.Truncation(2) >= umax) ## Find out of range p values is_nan = p < 0 | p > 1; ## Get lower and upper boundaries lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, umax); lp = binocdf (lx - 1, this.N, this.p); up = binocdf (ux, this.N, this.p); p = lp + p * (up - lp); p(is_nan) = NaN; endif x = binoinv (p, this.N, this.p); if (this.IsTruncated) x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif m = binostat (this.N, this.p); if (this.IsTruncated) lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, binoinv (1, this.N, this.p)); x = [lx:ux]; m = sum (x .* pdf (this, x)); endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = binocdf ([lx, ux], this.N, this.p); m = binoinv (sum (Fa_b) / 2, this.N, this.p); else if (! __traditional__() & this.p == 0.5 & rem (this.N, 2) == 1) m = this.mean (); else m = binoinv (0.5, this.N, this.p); endif endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - binolike ([this.N, this.p], this.InputData.data); endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {BinomialDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = binopdf (x, this.N, this.p); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (binocdf ([lx-1, ux], this.N, this.p)); endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {} plot (@var{pd}) ## @deftypefnx {BinomialDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {BinomialDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {BinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {BinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {BinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the negative binomial distribution, @qcode{@var{pnum} = 1} selects ## the parameter @qcode{N} and @qcode{@var{pnum} = 2} selects the parameter ## @var{p}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {BinomialDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {BinomialDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {BinomialDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (binocdf ([lx-1, ux], this.N, this.p)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = binornd (this.N, this.p, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = binornd (this.N, this.p, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); endif ## Check boundaries and constrain within support: Natural numbers lower = round (lower); upper = round (upper); lower(lower < 0) = 0; if (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {BinomialDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) ## Calculate untruncated mean and variance [um, uv] = binostat (this.N, this.p); ## Calculate truncated mean m = mean (this); ## Get lower and upper boundaries lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, binoinv (1, this.N, this.p)); ## Handle infinite support on the right if (isequal (ux, Inf)) ratio = 1 / diff (binocdf ([lx-1, ux], this.N, this.p)); x = 0:lx-1; v = ratio * (uv + (um - m) ^ 2 - sum (((x - m) .^ 2) .* ... binopdf (x, this.N, this.p))); else x = lx:ux; v = sum (((x - m) .^ 2) .* pdf (this, x)); endif else [~, v] = binostat (this.N, this.p); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, ntrials, varargin) ## Check input arguments if (nargin < 3) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 4) freq = ones (size (x)); else freq = varargin{2}; endif ## Fit data [pshat, psci] = mle (x, "distribution", "binomial", "alpha", alpha, ... "ntrials", ntrials, "frequency", freq); phat = [ntrials, pshat]; pci = [[ntrials; ntrials], psci(:)]; [~, acov] = binolike (phat, x, freq); ## Create fitted distribution object pd = BinomialDistribution.makeFitted (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) N = phat(1); p = phat(2); pd = BinomialDistribution (N, p); pd.ParameterCI = pci; pd.ParameterIsFixed = [true, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (N, p) if (! (isscalar (N) && isnumeric (N) && isreal (N) && isfinite (N) && N > 0 && fix (N) == N)) error ("BinomialDistribution: N must be a positive integer scalar.") endif if (! (isscalar (p) && isnumeric (p) && isreal (p) && isfinite (p) && p >= 0 && p <= 1)) error (strcat (["BinomialDistribution: p must be a real"], ... [" scalar bounded in the range [0, 1]."])) endif endfunction ## Test output %!shared pd, t, t_inf %! pd = BinomialDistribution (5, 0.5); %! t = truncate (pd, 2, 4); %! t_inf = truncate (pd, 2, Inf); %!assert (cdf (pd, [0:5]), [0.0312, 0.1875, 0.5, 0.8125, 0.9688, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0.4, 0.8, 1, 1], 1e-4); %!assert (cdf (t_inf, [0:5]), [0, 0, 0.3846, 0.7692, 0.9615, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.1875, 0.5, 0.8125, 0.9688, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0.4, 0.8, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 2, 2, 3, 3, 5], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2, 2, 3, 3, 4], 1e-4); %!assert (icdf (t_inf, [0:0.2:1]), [2, 2, 3, 3, 4, 5], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 2, 3, 3, 5, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2, 3, 3, 4, NaN], 1e-4); %!assert (iqr (pd), 1); %!assert (iqr (t), 1); %!assert (mean (pd), 2.5, 1e-10); %!assert (mean (t), 2.8, 1e-10); %!assert (mean (t_inf), 2.8846, 1e-4); %!assert (median (pd), 2.5); %!assert (median (t), 3); %!assert (pdf (pd, [0:5]), [0.0312, 0.1562, 0.3125, 0.3125, 0.1562, 0.0312], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.4, 0.4, 0.2, 0], 1e-4); %!assert (pdf (t_inf, [0:5]), [0, 0, 0.3846, 0.3846, 0.1923, 0.0385], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.1180, 1e-4); %!assert (std (t), 0.7483, 1e-4); %!assert (std (t_inf), 0.8470, 1e-4); %!assert (var (pd), 1.2500, 1e-4); %!assert (var (t), 0.5600, 1e-4); %!assert (var (t_inf), 0.7175, 1e-4); ## Test input validation ## 'BinomialDistribution' constructor %!error ... %! BinomialDistribution(Inf, 0.5) %!error ... %! BinomialDistribution(i, 0.5) %!error ... %! BinomialDistribution("a", 0.5) %!error ... %! BinomialDistribution([1, 2], 0.5) %!error ... %! BinomialDistribution(NaN, 0.5) %!error ... %! BinomialDistribution(1, 1.01) %!error ... %! BinomialDistribution(1, -0.01) %!error ... %! BinomialDistribution(1, Inf) %!error ... %! BinomialDistribution(1, i) %!error ... %! BinomialDistribution(1, "a") %!error ... %! BinomialDistribution(1, [1, 2]) %!error ... %! BinomialDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (BinomialDistribution, 2, "uper") %!error ... %! cdf (BinomialDistribution, 2, 3) ## 'paramci' method %!shared x %! rand ("seed", 2); %! x = binornd (5, 0.5, [1, 100]); %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", 0) %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", 1) %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", [0.5 2]) %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", "") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", {0.05}) %!error ... %! paramci (BinomialDistribution.fit (x, 6), "parameter", "p", ... %! "alpha", {0.05}) %!error ... %! paramci (BinomialDistribution.fit (x, 6), ... %! "parameter", {"N", "p", "param"}) %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", 0.01, ... %! "parameter", {"N", "p", "param"}) %!error ... %! paramci (BinomialDistribution.fit (x, 6), "parameter", "param") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "parameter", "N") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "NAME", "value") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", 0.01, ... %! "NAME", "value") %!error ... %! paramci (BinomialDistribution.fit (x, 6), "alpha", 0.01, ... %! "parameter", "p", "NAME", "value") ## 'plot' method %!error ... %! plot (BinomialDistribution, "Parent") %!error ... %! plot (BinomialDistribution, "PlotType", 12) %!error ... %! plot (BinomialDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (BinomialDistribution, "PlotType", "pdfcdf") %!error ... %! plot (BinomialDistribution, "Discrete", "pdfcdf") %!error ... %! plot (BinomialDistribution, "Discrete", [1, 0]) %!error ... %! plot (BinomialDistribution, "Discrete", {true}) %!error ... %! plot (BinomialDistribution, "Parent", 12) %!error ... %! plot (BinomialDistribution, "Parent", "hax") %!error ... %! plot (BinomialDistribution, "invalidNAME", "pdf") %!error ... %! plot (BinomialDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (BinomialDistribution, 2) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 3) %!error ... %! proflik (BinomialDistribution.fit (x, 6), [1, 2]) %!error ... %! proflik (BinomialDistribution.fit (x, 6), {1}) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, ones (2)) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "Display") %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "Display", 1) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "Display", {1}) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "Display", {"on"}) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "Display", ["on"; "on"]) %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "Display", "onnn") %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, "NAME", "on") %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, {"NAME"}, "on") %!error ... %! proflik (BinomialDistribution.fit (x, 6), 2, {[1 2 3]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (BinomialDistribution) %!error ... %! truncate (BinomialDistribution, 2) %!error ... %! truncate (BinomialDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = BinomialDistribution(1, 0.5); %! pd(2) = BinomialDistribution(1, 0.6); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/BirnbaumSaundersDistribution.m000066400000000000000000001107251475240274700261500ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef BirnbaumSaundersDistribution ## -*- texinfo -*- ## @deftypefn {statistics} BirnbaumSaundersDistribution ## ## Gamma probability distribution object. ## ## A @code{BirnbaumSaundersDistribution} object consists of parameters, a ## model description, and sample data for a gamma probability distribution. ## ## The gamma distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{a} @tab Shape parameter @tab @math{α > 0} ## @item @qcode{b} @tab Scale parameter @tab @math{β > 0} ## @end multitable ## ## There are several ways to create a @code{BirnbaumSaundersDistribution} ## object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{BirnbaumSaundersDistribution (@var{a}, ## @var{b})} to create a gamma distribution with specified parameter values. ## @item Use the static method @qcode{BirnbaumSaundersDistribution.fit ## (@var{x}, @var{censor}, @var{freq}, @var{options})} to a distribution to ## data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{BirnbaumSaundersDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{BirnbaumSaundersDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{fitdist, makedist, bisacdf, bisainv, bisapdf, bisarnd, lognfit, ## bisalike, bisastat} ## @end deftypefn properties (Dependent = true) beta gamma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "BirnbaumSaundersDistribution"; DistributionCode = "bisa"; NumParameters = 2; ParameterNames = {"beta", "gamma"}; ParameterDescription = {"Scale", "Shape"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = BirnbaumSaundersDistribution (beta, gamma) if (nargin == 0) beta = 1; gamma = 1; endif checkparams (beta, gamma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [beta, gamma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "gamma distribution"); endfunction function disp (this) __disp__ (this, "gamma distribution"); endfunction function this = set.beta (this, beta) checkparams (beta, this.gamma); this.InputData = []; this.ParameterValues(1) = beta; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function beta = get.beta (this) beta = this.ParameterValues(1); endfunction function this = set.gamma (this, gamma) checkparams (this.beta, gamma); this.InputData = []; this.ParameterValues(2) = gamma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function gamma = get.gamma (this) gamma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {BirnbaumSaundersDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = bisacdf (x, this.beta, this.gamma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= bisacdf (lx, this.beta, this.gamma); p(! (lb | ub)) /= diff (bisacdf ([lx, ux], this.beta, this.gamma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = bisacdf (this.Truncation(1), this.beta, this.gamma); up = bisacdf (this.Truncation(2), this.beta, this.gamma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = bisainv (np, this.beta, this.gamma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = bisainv (p, this.beta, this.gamma); endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = bisastat (this.beta, this.gamma); endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = bisacdf ([lx, ux], this.beta, this.gamma); m = bisainv (sum (Fa_b) / 2, this.beta, this.gamma); else m = bisainv (0.5, this.beta, this.gamma); endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - bisalike ([this.beta, this.gamma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {BirnbaumSaundersDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = bisapdf (x, this.beta, this.gamma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (bisacdf ([lx, ux], this.beta, this.gamma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {} plot (@var{pd}) ## @deftypefnx {BirnbaumSaundersDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {BirnbaumSaundersDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {BirnbaumSaundersDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {BirnbaumSaundersDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {BirnbaumSaundersDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the gamma distribution, @qcode{@var{pnum} = 1} selects the parameter ## @qcode{a} and @qcode{@var{pnum} = 2} selects the parameter @var{gamma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {BirnbaumSaundersDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {BirnbaumSaundersDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {BirnbaumSaundersDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (bisacdf ([lx, ux], this.beta, this.gamma)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = bisarnd (this.beta, this.gamma, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = bisarnd (this.beta, this.gamma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {BirnbaumSaundersDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = bisastat (this.beta, this.gamma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = bisafit (x, alpha, censor, freq, options); [~, acov] = bisalike (phat, x, censor, freq); ## Create fitted distribution object pd = BirnbaumSaundersDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) beta = phat(1); gamma = phat(2); pd = BirnbaumSaundersDistribution (beta, gamma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (beta, gamma) if (! (isscalar (beta) && isnumeric (beta) && isreal (beta) && isfinite (beta) && beta > 0)) error ("BirnbaumSaundersDistribution: BETA must be a positive real scalar.") endif if (! (isscalar (gamma) && isnumeric (gamma) && isreal (gamma) && isfinite (gamma) && gamma > 0)) error ("BirnbaumSaundersDistribution: GAMMA must be a positive real scalar.") endif endfunction %!demo %! ## Generate a data set of 5000 random samples from a Birnbaum-Saunders %! ## distribution with parameters β = 1 and γ = 0.5. Fit a Birnbaum-Saunders %! ## distribution to this data and plot a PDF of the fitted distribution %! ## superimposed on a histogram of the data %! %! pd = makedist ("BirnbaumSaunders", "beta", 1, "gamma", 0.5) %! randg ("seed", 21); %! data = random (pd, 5000, 1); %! pd = fitdist (data, "BirnbaumSaunders") %! plot (pd) %! msg = "Fitted Birnbaum-Saunders distribution with a = %0.2f and b = %0.2f"; %! title (sprintf (msg, pd.beta, pd.gamma)) %!demo %! ## Plot the PDF of a Birnbaum-Saunders distribution, with parameters beta = 1 %! ## and gamma = 0.5, truncated at [0, 2] intervals. Generate 10000 random %! ## samples from this truncated distribution and superimpose a histogram with %! ## 100 bins scaled accordingly %! %! pd = makedist ("BirnbaumSaunders", "beta", 1, "gamma", 0.5) %! t = truncate (pd, 0, 2) %! randg ("seed", 21); %! data = random (t, 10000, 1); %! plot (t) %! title ("Birnbaum-Saunders distribution (a = 2, b = 4) truncated at [0.1, 0.8]") %! hold on %! hist (data, 100, 50) %! hold off %!demo %! ## Generate a data set of 100 random samples from a Birnbaum-Saunders %! ## distribution with parameters β = 1 and γ = 0.5. Fit a Birnbaum-Saunders %! ## distribution to this data and plot its CDF superimposed over an empirical %! ## CDF of the data %! %! pd = makedist ("BirnbaumSaunders", "beta", 1, "gamma", 0.5) %! randg ("seed", 21); %! data = random (pd, 100, 1); %! pd = fitdist (data, "BirnbaumSaunders") %! plot (pd, "plottype", "cdf") %! title (sprintf ("Fitted Beta distribution with a = %0.2f and b = %0.2f", ... %! pd.beta, pd.gamma)) %! legend ({"empirical CDF", "fitted CDF"}, "location", "east") ## Test output %!shared pd, t %! pd = BirnbaumSaundersDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.5, 0.7602, 0.8759, 0.9332, 0.9632], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.6687, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.6585, 0.7602, 0.8759, 0.9332, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.6687, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.4411, 0.7767, 1.2875, 2.2673, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2293, 2.5073, 2.8567, 3.3210, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.7767, 1.2875, 2.2673, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.5073, 2.8567, 3.3210, 4, NaN], 1e-4); %!assert (iqr (pd), 1.4236, 1e-4); %!assert (iqr (t), 0.8968, 1e-4); %!assert (mean (pd), 1.5, eps); %!assert (mean (t), 2.7723, 1e-4); %!assert (median (pd), 1, 1e-4); %!assert (median (t), 2.6711, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.3989, 0.1648, 0.0788, 0.0405, 0.0216], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.9528, 0.4559, 0.2340, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0.2497, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.5, eps); %!assert (std (t), 0.5528, 1e-4); %!assert (var (pd), 2.25, eps); %!assert (var (t), 0.3056, 1e-4); ## Test input validation ## 'BirnbaumSaundersDistribution' constructor %!error ... %! BirnbaumSaundersDistribution(0, 1) %!error ... %! BirnbaumSaundersDistribution(Inf, 1) %!error ... %! BirnbaumSaundersDistribution(i, 1) %!error ... %! BirnbaumSaundersDistribution("beta", 1) %!error ... %! BirnbaumSaundersDistribution([1, 2], 1) %!error ... %! BirnbaumSaundersDistribution(NaN, 1) %!error ... %! BirnbaumSaundersDistribution(1, 0) %!error ... %! BirnbaumSaundersDistribution(1, -1) %!error ... %! BirnbaumSaundersDistribution(1, Inf) %!error ... %! BirnbaumSaundersDistribution(1, i) %!error ... %! BirnbaumSaundersDistribution(1, "beta") %!error ... %! BirnbaumSaundersDistribution(1, [1, 2]) %!error ... %! BirnbaumSaundersDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (BirnbaumSaundersDistribution, 2, "uper") %!error ... %! cdf (BirnbaumSaundersDistribution, 2, 3) ## 'paramci' method %!shared x %! rand ("seed", 5); %! x = bisarnd (1, 1, [100, 1]); %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha") %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", 0) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", 1) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", "") %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "parameter", ... %! "beta", "alpha", {0.05}) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), ... %! "parameter", {"beta", "gamma", "param"}) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"beta", "gamma", "param"}) %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "parameter", "param") %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "NAME", "value") %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", 0.01, ... %! "NAME", "value") %!error ... %! paramci (BirnbaumSaundersDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "beta", "NAME", "value") ## 'plot' method %!error ... %! plot (BirnbaumSaundersDistribution, "Parent") %!error ... %! plot (BirnbaumSaundersDistribution, "PlotType", 12) %!error ... %! plot (BirnbaumSaundersDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (BirnbaumSaundersDistribution, "PlotType", "pdfcdf") %!error ... %! plot (BirnbaumSaundersDistribution, "Discrete", "pdfcdf") %!error ... %! plot (BirnbaumSaundersDistribution, "Discrete", [1, 0]) %!error ... %! plot (BirnbaumSaundersDistribution, "Discrete", {true}) %!error ... %! plot (BirnbaumSaundersDistribution, "Parent", 12) %!error ... %! plot (BirnbaumSaundersDistribution, "Parent", "hax") %!error ... %! plot (BirnbaumSaundersDistribution, "invalidNAME", "pdf") %!error ... %! plot (BirnbaumSaundersDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (BirnbaumSaundersDistribution, 2) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 3) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), [1, 2]) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), {1}) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "Display") %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (BirnbaumSaundersDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (BirnbaumSaundersDistribution) %!error ... %! truncate (BirnbaumSaundersDistribution, 2) %!error ... %! truncate (BirnbaumSaundersDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = BirnbaumSaundersDistribution(1, 1); %! pd(2) = BirnbaumSaundersDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/BurrDistribution.m000066400000000000000000001113541475240274700236150ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef BurrDistribution ## -*- texinfo -*- ## @deftypefn {statistics} BurrDistribution ## ## Burr probability distribution object. ## ## A @code{BurrDistribution} object consists of parameters, a model ## description, and sample data for a Burr probability distribution. ## ## The Burr distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{alpha} @tab Shape @tab @math{-Inf < alpha < Inf} ## @item @qcode{c} @tab Scale @tab @math{c > 0} ## @item @qcode{k} @tab Location @tab @math{-Inf < k < Inf} ## @end multitable ## ## There are several ways to create a @code{BurrDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{BurrDistribution (@var{alpha}, @var{c})} ## to create a generalized extreme value distribution ## with specified parameter values. ## @item Use the static method @qcode{BurrDistribution.fit ## (@var{x}, @var{alpha}, @var{freq})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{BurrDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{BurrDistribution} object contains the following ## methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Burr distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{fitdist, makedist, burrcdf, burrinv, burrpdf, burrrnd, burrfit, ## burrlike, burrstat} ## @end deftypefn properties (Dependent = true) alpha c k endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "BurrDistribution"; DistributionCode = "burr"; NumParameters = 3; ParameterNames = {"alpha", "c", "k"}; ParameterDescription = {"Scale", "1st shape", "2nd shape"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin, realmin; Inf, Inf, Inf]; ParameterLogCI = [false, true, false]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = BurrDistribution (alpha, c, k) if (nargin == 0) alpha = 1; c = 1; k = 1; endif checkparams (alpha, c, k); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [alpha, c, k]; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "normal distribution"); endfunction function disp (this) __disp__ (this, "normal distribution"); endfunction function this = set.alpha (this, alpha) checkparams (alpha, this.c, this.k); this.InputData = []; this.ParameterValues(1) = alpha; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function alpha = get.alpha (this) alpha = this.ParameterValues(1); endfunction function this = set.c (this, c) checkparams (this.alpha, c, this.k); this.InputData = []; this.ParameterValues(2) = c; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function c = get.c (this) c = this.ParameterValues(2); endfunction function this = set.k (this, k) checkparams (this.alpha, this.c, k); this.InputData = []; this.ParameterValues(3) = k; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function k = get.k (this) k = this.ParameterValues(3); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {BurrDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = burrcdf (x, this.alpha, this.c, this.k); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= burrcdf (lx, this.alpha, this.c, this.k); p(! (lb | ub)) /= diff (burrcdf ([lx, ux], this.alpha, this.c, this.k)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = burrcdf (this.Truncation(1), this.alpha, this.c, this.k); up = burrcdf (this.Truncation(2), this.alpha, this.c, this.k); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = burrinv (np, this.alpha, this.c, this.k); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = burrinv (p, this.alpha, this.c, this.k); endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = burrstat (this.alpha, this.c, this.k); endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = burrcdf ([lx, ux], this.alpha, this.c, this.k); m = burrinv (sum (Fa_b) / 2, this.alpha, this.c, this.k); else m = burrinv (0.5, this.alpha, this.c, this.k); endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - burrlike ([this.alpha, this.c, this.k], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {BurrDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = burrpdf (x, this.alpha, this.c, this.k); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (burrcdf ([lx, ux], this.alpha, this.c, this.k)); endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {} plot (@var{pd}) ## @deftypefnx {BurrDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {BurrDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {BurrDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {BurrDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {BurrDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the generalized extreme value distribution, @qcode{@var{pnum} = 1} ## selects the parameter @qcode{alpha}, @qcode{@var{pnum} = 2} selects the ## parameter @var{c}, and @qcode{@var{pnum} = 3} selects the parameter ## @var{k}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {BurrDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {BurrDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {BurrDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (burrcdf ([lx, ux], this.alpha, this.c, this.k)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = burrrnd (this.alpha, this.c, this.k, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = burrrnd (this.alpha, this.c, this.k, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {BurrDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = burrstat (this.alpha, this.c, this.k); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = burrfit (x, alpha, censor, freq, options); [~, acov] = burrlike (phat, x, censor, freq); ## Create fitted distribution object pd = BurrDistribution.makeFitted (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) alpha = phat(1); c = phat(2); k = phat(3); pd = BurrDistribution (alpha, c, k); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (alpha, c, k) if (! (isscalar (alpha) && isnumeric (alpha) && isreal (alpha) && isfinite (alpha) && alpha > 0)) error ("BurrDistribution: ALPHA must be a positive real scalar.") endif if (! (isscalar (c) && isnumeric (c) && isreal (c) && isfinite (c) && c > 0)) error ("BurrDistribution: C must be a positive real scalar.") endif if (! (isscalar (k) && isnumeric (k) && isreal (k) && isfinite (k) && k > 0)) error ("BurrDistribution: K must be a positive real scalar.") endif endfunction %!demo %! ## Generate a data set of 5000 random samples from a Burr type XII %! ## distribution with parameters alpha = 1, c = 2, and k = 1. Fit a Burr type %! ## XII distribution to this data and plot a PDF of the fitted distribution %! ## superimposed on a histogram of the data %! %! pd = makedist ("Burr", "alpha", 1, "c", 2, "k", 1) %! rand ("seed", 21); %! data = random (pd, 5000, 1); %! pd = fitdist (data, "Burr") %! plot (pd) %! msg = strcat (["Fitted Burr type XII distribution with"], ... %! [" alpha = %0.2f, c = %0.2f, and k = %0.2f"]); %! title (sprintf (msg, pd.alpha, pd.c, pd.k)) %!demo %! ## Plot the PDF of a Burr type XII distribution, with parameters alpha = 1, %! ## c = 2, and k = 1, truncated at [0, 2] intervals. Generate 10000 random %! ## samples from this truncated distribution and superimpose a histogram with %! ## 100 bins scaled accordingly %! %! pd = makedist ("Burr", "alpha", 1, "c", 2, "k", 1) %! t = truncate (pd, 0.5, 2.5) %! rand ("seed", 21); %! data = random (t, 10000, 1); %! plot (t) %! title ("Burr type XII distribution (alpha = 1, c = 2, k = 1) truncated at [0.5, 2.5]") %! hold on %! hist (data, 100, 50) %! hold off %!demo %! ## Generate a data set of 100 random samples from a Burr type XII %! ## distribution with parameters alpha = 1, c = 2, and k = 1. Fit a Burr type %! ## XII distribution to this data and plot its CDF superimposed over an %! ## empirical CDF of the data %! %! pd = makedist ("Burr", "alpha", 1, "c", 2, "k", 1) %! rand ("seed", 21); %! data = random (pd, 100, 1); %! pd = fitdist (data, "Burr") %! plot (pd, "plottype", "cdf") %! msg = strcat (["Fitted Burr type XII distribution with"], ... %! [" alpha = %0.2f, c = %0.2f, and k = %0.2f"]); %! title (sprintf (msg, pd.alpha, pd.c, pd.k)) %! legend ({"empirical CDF", "fitted CDF"}, "location", "east") ## Test output %!shared pd, t %! pd = BurrDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.5, 0.6667, 0.75, 0.8, 0.8333], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.625, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.6, 0.6667, 0.75, 0.8], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.625, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.25, 0.6667, 1.5, 4, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2609, 2.5714, 2.9474, 3.4118, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.6667, 1.5, 4, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.5714, 2.9474, 3.4118, 4, NaN], 1e-4); %!assert (iqr (pd), 2.6667, 1e-4); %!assert (iqr (t), 0.9524, 1e-4); %!assert (mean (pd), Inf); %!assert (mean (t), 2.8312, 1e-4); %!assert (median (pd), 1, 1e-4); %!assert (median (t), 2.75, 1e-4); %!assert (pdf (pd, [0:5]), [1, 0.25, 0.1111, 0.0625, 0.04, 0.0278], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.8333, 0.4687, 0.3, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.25, 0.1111, 0.0625, 0.04, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 0.8333, 0.4687, 0.3, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), Inf); %!assert (std (t), 0.5674, 1e-4); %!assert (var (pd), Inf); %!assert (var (t), 0.3220, 1e-4); ## Test input validation ## 'BurrDistribution' constructor %!error ... %! BurrDistribution(0, 1, 1) %!error ... %! BurrDistribution(-1, 1, 1) %!error ... %! BurrDistribution(Inf, 1, 1) %!error ... %! BurrDistribution(i, 1, 1) %!error ... %! BurrDistribution("a", 1, 1) %!error ... %! BurrDistribution([1, 2], 1, 1) %!error ... %! BurrDistribution(NaN, 1, 1) %!error ... %! BurrDistribution(1, 0, 1) %!error ... %! BurrDistribution(1, -1, 1) %!error ... %! BurrDistribution(1, Inf, 1) %!error ... %! BurrDistribution(1, i, 1) %!error ... %! BurrDistribution(1, "a", 1) %!error ... %! BurrDistribution(1, [1, 2], 1) %!error ... %! BurrDistribution(1, NaN, 1) %!error ... %! BurrDistribution(1, 1, 0) %!error ... %! BurrDistribution(1, 1, -1) %!error ... %! BurrDistribution(1, 1, Inf) %!error ... %! BurrDistribution(1, 1, i) %!error ... %! BurrDistribution(1, 1, "a") %!error ... %! BurrDistribution(1, 1, [1, 2]) %!error ... %! BurrDistribution(1, 1, NaN) ## 'cdf' method %!error ... %! cdf (BurrDistribution, 2, "uper") %!error ... %! cdf (BurrDistribution, 2, 3) ## 'paramci' method %!shared x %! rand ("seed", 4); %! x = burrrnd (1, 1, 1, [1, 100]); %!error ... %! paramci (BurrDistribution.fit (x), "alpha") %!error ... %! paramci (BurrDistribution.fit (x), "alpha", 0) %!error ... %! paramci (BurrDistribution.fit (x), "alpha", 1) %!error ... %! paramci (BurrDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (BurrDistribution.fit (x), "alpha", "") %!error ... %! paramci (BurrDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (BurrDistribution.fit (x), "parameter", "c", "alpha", {0.05}) %!error ... %! paramci (BurrDistribution.fit (x), "parameter", {"alpha", "c", "k", "param"}) %!error ... %! paramci (BurrDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"alpha", "c", "k", "param"}) %!error ... %! paramci (BurrDistribution.fit (x), "parameter", "param") %!error ... %! paramci (BurrDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (BurrDistribution.fit (x), "NAME", "value") %!error ... %! paramci (BurrDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (BurrDistribution.fit (x), "alpha", 0.01, "parameter", "c", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (BurrDistribution, "Parent") %!error ... %! plot (BurrDistribution, "PlotType", 12) %!error ... %! plot (BurrDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (BurrDistribution, "PlotType", "pdfcdf") %!error ... %! plot (BurrDistribution, "Discrete", "pdfcdf") %!error ... %! plot (BurrDistribution, "Discrete", [1, 0]) %!error ... %! plot (BurrDistribution, "Discrete", {true}) %!error ... %! plot (BurrDistribution, "Parent", 12) %!error ... %! plot (BurrDistribution, "Parent", "hax") %!error ... %! plot (BurrDistribution, "invalidNAME", "pdf") %!error ... %! plot (BurrDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (BurrDistribution, 2) %!error ... %! proflik (BurrDistribution.fit (x), 4) %!error ... %! proflik (BurrDistribution.fit (x), [1, 2]) %!error ... %! proflik (BurrDistribution.fit (x), {1}) %!error ... %! proflik (BurrDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (BurrDistribution.fit (x), 1, "Display") %!error ... %! proflik (BurrDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (BurrDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (BurrDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (BurrDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (BurrDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (BurrDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (BurrDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (BurrDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (BurrDistribution) %!error ... %! truncate (BurrDistribution, 2) %!error ... %! truncate (BurrDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = BurrDistribution(1, 1, 1); %! pd(2) = BurrDistribution(1, 3, 1); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/ExponentialDistribution.m000066400000000000000000001001221475240274700251600ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ExponentialDistribution ## -*- texinfo -*- ## @deftypefn {statistics} ExponentialDistribution ## ## Exponential probability distribution object. ## ## A @code{ExponentialDistribution} object consists of parameters, a model ## description, and sample data for a exponential probability distribution. ## ## The exponential distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Mean @tab @math{mu > 0} ## @end multitable ## ## There are several ways to create a @code{ExponentialDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{ExponentialDistribution (@var{mu})} ## to create a exponential distribution with specified parameter values. ## @item Use the static method @qcode{ExponentialDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{ExponentialDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{ExponentialDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{fitdist, makedist, expcdf, expinv, exppdf, exprnd, expfit, ## explike, expstat} ## @end deftypefn properties (Dependent = true) mu endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "ExponentialDistribution"; DistributionCode = "exp"; NumParameters = 1; ParameterNames = {"mu"}; ParameterDescription = {"Mean"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin; Inf]; ParameterLogCI = true; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = ExponentialDistribution (mu) if (nargin == 0) mu = 1; endif checkparams (mu); this.InputData = []; this.IsTruncated = false; this.ParameterValues = mu; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "exponential distribution"); endfunction function disp (this) __disp__ (this, "exponential distribution"); endfunction function this = set.mu (this, mu) checkparams (mu); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {ExponentialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = expcdf (x, this.mu); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= expcdf (lx, this.mu); p(! (lb | ub)) /= diff (expcdf ([lx, ux], this.mu)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = expcdf (this.Truncation(1), this.mu); up = expcdf (this.Truncation(2), this.mu); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = expinv (np, this.mu); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = expinv (p, this.mu); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = expstat (this.mu); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = expcdf ([lx, ux], this.mu); m = expinv (sum (Fa_b) / 2, this.mu); else m = this.mu .* log (2); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - explike (this.mu, this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {ExponentialDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = exppdf (x, this.mu); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (expcdf ([lx, ux], this.mu)); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {} plot (@var{pd}) ## @deftypefnx {ExponentialDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {ExponentialDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {ExponentialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {ExponentialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {ExponentialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the exponential distribution, @qcode{@var{pnum} = 1} selects the ## parameter @var{mu}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {ExponentialDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {ExponentialDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {ExponentialDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (expcdf ([lx, ux], this.mu)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = exprnd (this.mu, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = exprnd (this.mu, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif ## Check boundaries and constrain within support [0, Inf) lower(lower < 0) = 0; this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {ExponentialDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = expstat (this.mu); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif ## Fit data [phat, pci] = expfit (x, alpha, censor, freq); [~, acov] = explike (phat, x, censor, freq); ## Create fitted distribution object pd = ExponentialDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); pd = ExponentialDistribution (mu); pd.ParameterCI = pci; pd.ParameterIsFixed = false; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu) && mu > 0)) error ("ExponentialDistribution: MU must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = ExponentialDistribution (1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.6321, 0.8647, 0.9502, 0.9817, 0.9933], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7311, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.7769, 0.8647, 0.9502, 0.9817], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.7311, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.2231, 0.5108, 0.9163, 1.6094, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1899, 2.4244, 2.7315, 3.1768, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.5108, 0.9163, 1.6094, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4244, 2.7315, 3.1768, 4, NaN], 1e-4); %!assert (iqr (pd), 1.0986, 1e-4); %!assert (iqr (t), 0.8020, 1e-4); %!assert (mean (pd), 1); %!assert (mean (t), 2.6870, 1e-4); %!assert (median (pd), 0.6931, 1e-4); %!assert (median (t), 2.5662, 1e-4); %!assert (pdf (pd, [0:5]), [1, 0.3679, 0.1353, 0.0498, 0.0183, 0.0067], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.1565, 0.4255, 0.1565, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.3679, 0.1353, 0.0498, 0.0183, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 1.1565, 0.4255, 0.1565, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1); %!assert (std (t), 0.5253, 1e-4); %!assert (var (pd), 1); %!assert (var (t), 0.2759, 1e-4); ## Test input validation ## 'ExponentialDistribution' constructor %!error ... %! ExponentialDistribution(0) %!error ... %! ExponentialDistribution(-1) %!error ... %! ExponentialDistribution(Inf) %!error ... %! ExponentialDistribution(i) %!error ... %! ExponentialDistribution("a") %!error ... %! ExponentialDistribution([1, 2]) %!error ... %! ExponentialDistribution(NaN) ## 'cdf' method %!error ... %! cdf (ExponentialDistribution, 2, "uper") %!error ... %! cdf (ExponentialDistribution, 2, 3) ## 'paramci' method %!shared x %! x = exprnd (1, [100, 1]); %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha") %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", 0) %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", 1) %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", "") %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (ExponentialDistribution.fit (x), "parameter", "mu", ... %! "alpha", {0.05}) %!error ... %! paramci (ExponentialDistribution.fit (x), "parameter", {"mu", "param"}) %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "param"}) %!error ... %! paramci (ExponentialDistribution.fit (x), "parameter", "param") %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", 0.01, "parameter", "parm") %!error ... %! paramci (ExponentialDistribution.fit (x), "NAME", "value") %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (ExponentialDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "mu", "NAME", "value") ## 'plot' method %!error ... %! plot (ExponentialDistribution, "Parent") %!error ... %! plot (ExponentialDistribution, "PlotType", 12) %!error ... %! plot (ExponentialDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (ExponentialDistribution, "PlotType", "pdfcdf") %!error ... %! plot (ExponentialDistribution, "Discrete", "pdfcdf") %!error ... %! plot (ExponentialDistribution, "Discrete", [1, 0]) %!error ... %! plot (ExponentialDistribution, "Discrete", {true}) %!error ... %! plot (ExponentialDistribution, "Parent", 12) %!error ... %! plot (ExponentialDistribution, "Parent", "hax") %!error ... %! plot (ExponentialDistribution, "invalidNAME", "pdf") %!error ... %! plot (ExponentialDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (ExponentialDistribution, 2) %!error ... %! proflik (ExponentialDistribution.fit (x), 3) %!error ... %! proflik (ExponentialDistribution.fit (x), [1, 2]) %!error ... %! proflik (ExponentialDistribution.fit (x), {1}) %!error ... %! proflik (ExponentialDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "Display") %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (ExponentialDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (ExponentialDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (ExponentialDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (ExponentialDistribution) %!error ... %! truncate (ExponentialDistribution, 2) %!error ... %! truncate (ExponentialDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = ExponentialDistribution(1); %! pd(2) = ExponentialDistribution(3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/ExtremeValueDistribution.m000066400000000000000000001037231475240274700253120ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef ExtremeValueDistribution ## -*- texinfo -*- ## @deftypefn {statistics} ExtremeValueDistribution ## ## Extreme value probability distribution object. ## ## A @code{ExtremeValueDistribution} object consists of parameters, a model ## description, and sample data for a extreme value probability distribution. ## ## The extreme value distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Location parameter @tab @math{-Inf < mu < Inf} ## @item @qcode{sigma} @tab Scale parameter @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{ExtremeValueDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{ExtremeValueDistribution (@var{mu}, @var{sigma})} ## to create a extreme value distribution with specified parameter values. ## @item Use the static method @qcode{ExtremeValueDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{ExtremeValueDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{ExtremeValueDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{fitdist, makedist, evcdf, evinv, evpdf, evrnd, evfit, ## evlike, evstat} ## @end deftypefn properties (Dependent = true) mu sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "ExtremeValueDistribution"; DistributionCode = "ev"; NumParameters = 2; ParameterNames = {"mu", "sigma"}; ParameterDescription = {"Location", "Scale"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = ExtremeValueDistribution (mu, sigma) if (nargin == 0) mu = 0; sigma = 1; endif checkparams (mu, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "extreme value distribution"); endfunction function disp (this) __disp__ (this, "extreme value distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {ExtremeValueDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = evcdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= evcdf (lx, this.mu, this.sigma); p(! (lb | ub)) /= diff (evcdf ([lx, ux], this.mu, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = evcdf (this.Truncation(1), this.mu, this.sigma); up = evcdf (this.Truncation(2), this.mu, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = evinv (np, this.mu, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = evinv (p, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = evstat (this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = evcdf ([lx, ux], this.mu, this.sigma); m = evinv (sum (Fa_b) / 2, this.mu, this.sigma); else m = evinv (0.5, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - evlike ([this.mu, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {ExtremeValueDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = evpdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (evcdf ([lx, ux], this.mu, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {} plot (@var{pd}) ## @deftypefnx {ExtremeValueDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {ExtremeValueDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {ExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {ExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {ExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the extreme value distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {ExtremeValueDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {ExtremeValueDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {ExtremeValueDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (evcdf ([lx, ux], this.mu, this.sigma)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = evrnd (this.mu, this.sigma, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = evrnd (this.mu, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {ExtremeValueDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = evstat (this.mu, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = evfit (x, alpha, censor, freq, options); [~, acov] = evlike (phat, x, censor, freq); ## Create fitted distribution object pd = ExtremeValueDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); sigma = phat(2); pd = ExtremeValueDistribution (mu, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) error ("ExtremeValueDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("ExtremeValueDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = ExtremeValueDistribution (0, 1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0.6321, 0.9340, 0.9994, 1, 1, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 1, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.9887, 0.9994, 1, 1], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 1, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [-Inf, -1.4999, -0.6717, -0.0874, 0.4759, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.0298, 2.0668, 2.1169, 2.1971, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, -0.6717, -0.0874, 0.4759, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.0668, 2.1169, 2.1971, 4, NaN], 1e-4); %!assert (iqr (pd), 1.5725, 1e-4); %!assert (iqr (t), 0.1338, 1e-4); %!assert (mean (pd), -0.5772, 1e-4); %!assert (mean (t), 2.1206, 1e-4); %!assert (median (pd), -0.3665, 1e-4); %!assert (median (t), 2.0897, 1e-4); %!assert (pdf (pd, [0:5]), [0.3679, 0.1794, 0.0046, 0, 0, 0], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 7.3891, 0.0001, 0, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0.2546, 0.1794, 0.0046, 0, 0, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 7.3891, 0.0001, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.2825, 1e-4); %!assert (std (t), 0.1091, 1e-4); %!assert (var (pd), 1.6449, 1e-4); %!assert (var (t), 0.0119, 1e-4); ## Test input validation ## 'ExtremeValueDistribution' constructor %!error ... %! ExtremeValueDistribution(Inf, 1) %!error ... %! ExtremeValueDistribution(i, 1) %!error ... %! ExtremeValueDistribution("a", 1) %!error ... %! ExtremeValueDistribution([1, 2], 1) %!error ... %! ExtremeValueDistribution(NaN, 1) %!error ... %! ExtremeValueDistribution(1, 0) %!error ... %! ExtremeValueDistribution(1, -1) %!error ... %! ExtremeValueDistribution(1, Inf) %!error ... %! ExtremeValueDistribution(1, i) %!error ... %! ExtremeValueDistribution(1, "a") %!error ... %! ExtremeValueDistribution(1, [1, 2]) %!error ... %! ExtremeValueDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (ExtremeValueDistribution, 2, "uper") %!error ... %! cdf (ExtremeValueDistribution, 2, 3) ## 'paramci' method %!shared x %! rand ("seed", 1); %! x = evrnd (1, 1, [1000, 1]); %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha") %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", 0) %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", 1) %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", "") %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (ExtremeValueDistribution.fit (x), ... %! "parameter", "mu", "alpha", {0.05}) %!error ... %! paramci (ExtremeValueDistribution.fit (x), ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (ExtremeValueDistribution.fit (x), "parameter", "param") %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (ExtremeValueDistribution.fit (x), "NAME", "value") %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (ExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "mu", "NAME", "value") ## 'plot' method %!error ... %! plot (ExtremeValueDistribution, "Parent") %!error ... %! plot (ExtremeValueDistribution, "PlotType", 12) %!error ... %! plot (ExtremeValueDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (ExtremeValueDistribution, "PlotType", "pdfcdf") %!error ... %! plot (ExtremeValueDistribution, "Discrete", "pdfcdf") %!error ... %! plot (ExtremeValueDistribution, "Discrete", [1, 0]) %!error ... %! plot (ExtremeValueDistribution, "Discrete", {true}) %!error ... %! plot (ExtremeValueDistribution, "Parent", 12) %!error ... %! plot (ExtremeValueDistribution, "Parent", "hax") %!error ... %! plot (ExtremeValueDistribution, "invalidNAME", "pdf") %!error ... %! plot (ExtremeValueDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (ExtremeValueDistribution, 2) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 3) %!error ... %! proflik (ExtremeValueDistribution.fit (x), [1, 2]) %!error ... %! proflik (ExtremeValueDistribution.fit (x), {1}) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "Display") %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (ExtremeValueDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (ExtremeValueDistribution) %!error ... %! truncate (ExtremeValueDistribution, 2) %!error ... %! truncate (ExtremeValueDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = ExtremeValueDistribution(1, 1); %! pd(2) = ExtremeValueDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/GammaDistribution.m000066400000000000000000001020011475240274700237120ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef GammaDistribution ## -*- texinfo -*- ## @deftypefn {statistics} GammaDistribution ## ## Gamma probability distribution object. ## ## A @code{GammaDistribution} object consists of parameters, a model ## description, and sample data for a gamma probability distribution. ## ## The gamma distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{a} @tab Shape parameter @tab @math{α > 0} ## @item @qcode{b} @tab Scale parameter @tab @math{β > 0} ## @end multitable ## ## There are several ways to create a @code{GammaDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{GammaDistribution (@var{a}, @var{b})} ## to create a gamma distribution with specified parameter values. ## @item Use the static method @qcode{GammaDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{GammaDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{GammaDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{fitdist, makedist, gamcdf, gaminv, gampdf, gamrnd, lognfit, ## gamlike, gamstat} ## @end deftypefn properties (Dependent = true) a b endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "GammaDistribution"; DistributionCode = "gam"; NumParameters = 2; ParameterNames = {"a", "b"}; ParameterDescription = {"Shape", "Scale"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = GammaDistribution (a, b) if (nargin == 0) a = 1; b = 1; endif checkparams (a, b); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [a, b]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "gamma distribution"); endfunction function disp (this) __disp__ (this, "gamma distribution"); endfunction function this = set.a (this, a) checkparams (a, this.b); this.InputData = []; this.ParameterValues(1) = a; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function a = get.a (this) a = this.ParameterValues(1); endfunction function this = set.b (this, b) checkparams (this.a, b); this.InputData = []; this.ParameterValues(2) = b; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function b = get.b (this) b = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {GammaDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = gamcdf (x, this.a, this.b); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= gamcdf (lx, this.a, this.b); p(! (lb | ub)) /= diff (gamcdf ([lx, ux], this.a, this.b)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = gamcdf (this.Truncation(1), this.a, this.b); up = gamcdf (this.Truncation(2), this.a, this.b); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = gaminv (np, this.a, this.b); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = gaminv (p, this.a, this.b); endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = gamstat (this.a, this.b); endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = gamcdf ([lx, ux], this.a, this.b); m = gaminv (sum (Fa_b) / 2, this.a, this.b); else m = gaminv (0.5, this.a, this.b); endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - gamlike ([this.a, this.b], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {GammaDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = gampdf (x, this.a, this.b); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (gamcdf ([lx, ux], this.a, this.b)); endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {} plot (@var{pd}) ## @deftypefnx {GammaDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {GammaDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {GammaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {GammaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {GammaDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the gamma distribution, @qcode{@var{pnum} = 1} selects the parameter ## @qcode{a} and @qcode{@var{pnum} = 2} selects the parameter @var{b}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {GammaDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {GammaDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {GammaDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (gamcdf ([lx, ux], this.a, this.b)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = gamrnd (this.a, this.b, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = gamrnd (this.a, this.b, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {GammaDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = gamstat (this.a, this.b); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = gamfit (x, alpha, censor, freq, options); [~, acov] = gamlike (phat, x, censor, freq); ## Create fitted distribution object pd = GammaDistribution.makeFitted (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) a = phat(1); b = phat(2); pd = GammaDistribution (a, b); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (a, b) if (! (isscalar (a) && isnumeric (a) && isreal (a) && isfinite (a) && a > 0)) error ("GammaDistribution: A must be a positive real scalar.") endif if (! (isscalar (b) && isnumeric (b) && isreal (b) && isfinite (b) && b > 0)) error ("GammaDistribution: B must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = GammaDistribution (1, 1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.6321, 0.8647, 0.9502, 0.9817, 0.9933], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7311, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.7769, 0.8647, 0.9502, 0.9817], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.7311, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.2231, 0.5108, 0.9163, 1.6094, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1899, 2.4244, 2.7315, 3.1768, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.5108, 0.9163, 1.6094, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4244, 2.7315, 3.1768, 4, NaN], 1e-4); %!assert (iqr (pd), 1.0986, 1e-4); %!assert (iqr (t), 0.8020, 1e-4); %!assert (mean (pd), 1); %!assert (mean (t), 2.6870, 1e-4); %!assert (median (pd), 0.6931, 1e-4); %!assert (median (t), 2.5662, 1e-4); %!assert (pdf (pd, [0:5]), [1, 0.3679, 0.1353, 0.0498, 0.0183, 0.0067], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.1565, 0.4255, 0.1565, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.3679, 0.1353, 0.0498, 0.0183, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 1.1565, 0.4255, 0.1565, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1); %!assert (std (t), 0.5253, 1e-4); %!assert (var (pd), 1); %!assert (var (t), 0.2759, 1e-4); ## Test input validation ## 'GammaDistribution' constructor %!error ... %! GammaDistribution(0, 1) %!error ... %! GammaDistribution(Inf, 1) %!error ... %! GammaDistribution(i, 1) %!error ... %! GammaDistribution("a", 1) %!error ... %! GammaDistribution([1, 2], 1) %!error ... %! GammaDistribution(NaN, 1) %!error ... %! GammaDistribution(1, 0) %!error ... %! GammaDistribution(1, -1) %!error ... %! GammaDistribution(1, Inf) %!error ... %! GammaDistribution(1, i) %!error ... %! GammaDistribution(1, "a") %!error ... %! GammaDistribution(1, [1, 2]) %!error ... %! GammaDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (GammaDistribution, 2, "uper") %!error ... %! cdf (GammaDistribution, 2, 3) ## 'paramci' method %!shared x %! x = gamrnd (1, 1, [100, 1]); %!error ... %! paramci (GammaDistribution.fit (x), "alpha") %!error ... %! paramci (GammaDistribution.fit (x), "alpha", 0) %!error ... %! paramci (GammaDistribution.fit (x), "alpha", 1) %!error ... %! paramci (GammaDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (GammaDistribution.fit (x), "alpha", "") %!error ... %! paramci (GammaDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (GammaDistribution.fit (x), "parameter", "a", "alpha", {0.05}) %!error ... %! paramci (GammaDistribution.fit (x), "parameter", {"a", "b", "param"}) %!error ... %! paramci (GammaDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"a", "b", "param"}) %!error ... %! paramci (GammaDistribution.fit (x), "parameter", "param") %!error ... %! paramci (GammaDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (GammaDistribution.fit (x), "NAME", "value") %!error ... %! paramci (GammaDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (GammaDistribution.fit (x), "alpha", 0.01, "parameter", "a", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (GammaDistribution, "Parent") %!error ... %! plot (GammaDistribution, "PlotType", 12) %!error ... %! plot (GammaDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (GammaDistribution, "PlotType", "pdfcdf") %!error ... %! plot (GammaDistribution, "Discrete", "pdfcdf") %!error ... %! plot (GammaDistribution, "Discrete", [1, 0]) %!error ... %! plot (GammaDistribution, "Discrete", {true}) %!error ... %! plot (GammaDistribution, "Parent", 12) %!error ... %! plot (GammaDistribution, "Parent", "hax") %!error ... %! plot (GammaDistribution, "invalidNAME", "pdf") %!error ... %! plot (GammaDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (GammaDistribution, 2) %!error ... %! proflik (GammaDistribution.fit (x), 3) %!error ... %! proflik (GammaDistribution.fit (x), [1, 2]) %!error ... %! proflik (GammaDistribution.fit (x), {1}) %!error ... %! proflik (GammaDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (GammaDistribution.fit (x), 1, "Display") %!error ... %! proflik (GammaDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (GammaDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (GammaDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (GammaDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (GammaDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (GammaDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (GammaDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (GammaDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (GammaDistribution) %!error ... %! truncate (GammaDistribution, 2) %!error ... %! truncate (GammaDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = GammaDistribution(1, 1); %! pd(2) = GammaDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/GeneralizedExtremeValueDistribution.m000066400000000000000000001114541475240274700274640ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef GeneralizedExtremeValueDistribution ## -*- texinfo -*- ## @deftypefn {statistics} GeneralizedExtremeValueDistribution ## ## Generalized extreme value probability distribution object. ## ## A @code{GeneralizedExtremeValueDistribution} object consists of parameters, ## a model description, and sample data for a generalized extreme value ## probability distribution. ## ## The generalized extreme value distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{k} @tab Shape @tab @math{-Inf < k < Inf} ## @item @qcode{sigma} @tab Scale @tab @math{sigma > 0} ## @item @qcode{mu} @tab Location @tab @math{-Inf < mu < Inf} ## @end multitable ## ## There are several ways to create a @code{GeneralizedExtremeValueDistribution} ## object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{GeneralizedExtremeValueDistribution ## (@var{k}, @var{sigma})} to create a generalized extreme value distribution ## with specified parameter values. ## @item Use the static method @qcode{GeneralizedExtremeValueDistribution.fit ## (@var{x}, @var{k}, @var{freq})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{GeneralizedExtremeValueDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{GeneralizedExtremeValueDistribution} object contains the following ## methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{fitdist, makedist, gevcdf, gevinv, gevpdf, gevrnd, gevfit, ## gevlike, gevstat} ## @end deftypefn properties (Dependent = true) k sigma mu endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "GeneralizedExtremeValueDistribution"; DistributionCode = "gev"; NumParameters = 3; ParameterNames = {"k", "sigma", "mu"}; ParameterDescription = {"Shape", "Scale", "Location"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin, -Inf; Inf, Inf, Inf]; ParameterLogCI = [false, true, false]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = GeneralizedExtremeValueDistribution (k, sigma, mu) if (nargin == 0) k = 0; sigma = 1; mu = 0; endif checkparams (k, sigma, mu); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [k, sigma, mu]; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "normal distribution"); endfunction function disp (this) __disp__ (this, "normal distribution"); endfunction function this = set.k (this, k) checkparams (k, this.sigma, this.mu); this.InputData = []; this.ParameterValues(1) = k; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function k = get.k (this) k = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.k, sigma, this.mu); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction function this = set.mu (this, mu) checkparams (this.k, this.sigma, mu); this.InputData = []; this.ParameterValues(3) = mu; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(3); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = gevcdf (x, this.k, this.sigma, this.mu); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= gevcdf (lx, this.k, this.sigma, this.mu); p(! (lb | ub)) /= diff (gevcdf ([lx, ux], this.k, this.sigma, this.mu)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = gevcdf (this.Truncation(1), this.k, this.sigma, this.mu); up = gevcdf (this.Truncation(2), this.k, this.sigma, this.mu); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = gevinv (np, this.k, this.sigma, this.mu); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = gevinv (p, this.k, this.sigma, this.mu); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = gevstat (this.k, this.sigma, this.mu); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = gevcdf ([lx, ux], this.k, this.sigma, this.mu); m = gevinv (sum (Fa_b) / 2, this.k, this.sigma, this.mu); else m = gevinv (0.5, this.k, this.sigma, this.mu); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - gevlike ([this.k, this.sigma, this.mu], ... this.InputData.data, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = gevpdf (x, this.k, this.sigma, this.mu); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (gevcdf ([lx, ux], this.k, this.sigma, this.mu)); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {} plot (@var{pd}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the generalized extreme value distribution, @qcode{@var{pnum} = 1} ## selects the parameter @qcode{k}, @qcode{@var{pnum} = 2} selects the ## parameter @var{sigma}, and @qcode{@var{pnum} = 3} selects the parameter ## @var{mu}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {GeneralizedExtremeValueDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (gevcdf ([lx, ux], this.k, this.sigma, this.mu)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = gevrnd (this.k, this.sigma, this.mu, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = gevrnd (this.k, this.sigma, this.mu, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedExtremeValueDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = gevstat (this.k, this.sigma, this.mu); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) freq = []; else freq = varargin{2}; endif if (nargin < 4) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{3}; endif ## Fit data [phat, pci] = gevfit (x, alpha, freq, options); [~, acov] = gevlike (phat, x, freq); ## Create fitted distribution object pd = GeneralizedExtremeValueDistribution.makeFitted ... (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) k = phat(1); sigma = phat(2); mu = phat(3); pd = GeneralizedExtremeValueDistribution (k, sigma, mu); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (k, sigma, mu) if (! (isscalar (k) && isnumeric (k) && isreal (k) && isfinite (k))) error ("GeneralizedExtremeValueDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error (strcat (["GeneralizedExtremeValueDistribution: SIGMA must be"], ... [" a positive real scalar."])) endif if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) error ("GeneralizedExtremeValueDistribution: MU must be a real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = GeneralizedExtremeValueDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0.3679, 0.6922, 0.8734, 0.9514, 0.9819, 0.9933], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7195, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.8, 0.8734, 0.9514, 0.9819], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.7195, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [-Inf, -0.4759, 0.0874, 0.6717, 1.4999, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1999, 2.4433, 2.7568, 3.2028, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.0874, 0.6717, 1.4999, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4433, 2.7568, 3.2028, 4, NaN], 1e-4); %!assert (iqr (pd), 1.5725, 1e-4); %!assert (iqr (t), 0.8164, 1e-4); %!assert (mean (pd), 0.5772, 1e-4); %!assert (mean (t), 2.7043, 1e-4); %!assert (median (pd), 0.3665, 1e-4); %!assert (median (t), 2.5887, 1e-4); %!assert (pdf (pd, [0:5]), [0.3679, 0.2546, 0.1182, 0.0474, 0.0180, 0.0067], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.0902, 0.4369, 0.1659, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0.1794, 0.2546, 0.1182, 0.0474, 0.0180, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 1.0902, 0.4369, 0.1659, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.2825, 1e-4); %!assert (std (t), 0.5289, 1e-4); %!assert (var (pd), 1.6449, 1e-4); %!assert (var (t), 0.2798, 1e-4); ## Test input validation ## 'GeneralizedExtremeValueDistribution' constructor %!error ... %! GeneralizedExtremeValueDistribution(Inf, 1, 1) %!error ... %! GeneralizedExtremeValueDistribution(i, 1, 1) %!error ... %! GeneralizedExtremeValueDistribution("a", 1, 1) %!error ... %! GeneralizedExtremeValueDistribution([1, 2], 1, 1) %!error ... %! GeneralizedExtremeValueDistribution(NaN, 1, 1) %!error ... %! GeneralizedExtremeValueDistribution(1, 0, 1) %!error ... %! GeneralizedExtremeValueDistribution(1, -1, 1) %!error ... %! GeneralizedExtremeValueDistribution(1, Inf, 1) %!error ... %! GeneralizedExtremeValueDistribution(1, i, 1) %!error ... %! GeneralizedExtremeValueDistribution(1, "a", 1) %!error ... %! GeneralizedExtremeValueDistribution(1, [1, 2], 1) %!error ... %! GeneralizedExtremeValueDistribution(1, NaN, 1) %!error ... %! GeneralizedExtremeValueDistribution(1, 1, Inf) %!error ... %! GeneralizedExtremeValueDistribution(1, 1, i) %!error ... %! GeneralizedExtremeValueDistribution(1, 1, "a") %!error ... %! GeneralizedExtremeValueDistribution(1, 1, [1, 2]) %!error ... %! GeneralizedExtremeValueDistribution(1, 1, NaN) ## 'cdf' method %!error ... %! cdf (GeneralizedExtremeValueDistribution, 2, "uper") %!error ... %! cdf (GeneralizedExtremeValueDistribution, 2, 3) ## 'paramci' method %!shared x %! x = gevrnd (1, 1, 1, [1, 100]); %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha") %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", 0) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", 1) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", "") %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), ... %! "parameter", "sigma", "alpha", {0.05}) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), ... %! "parameter", {"k", "sigma", "mu", "param"}) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"k", "sigma", "mu", "param"}) %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "parameter", "param") %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "NAME", "value") %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "NAME", "value") %!error ... %! paramci (GeneralizedExtremeValueDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "sigma", "NAME", "value") ## 'plot' method %!error ... %! plot (GeneralizedExtremeValueDistribution, "Parent") %!error ... %! plot (GeneralizedExtremeValueDistribution, "PlotType", 12) %!error ... %! plot (GeneralizedExtremeValueDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (GeneralizedExtremeValueDistribution, "PlotType", "pdfcdf") %!error ... %! plot (GeneralizedExtremeValueDistribution, "Discrete", "pdfcdf") %!error ... %! plot (GeneralizedExtremeValueDistribution, "Discrete", [1, 0]) %!error ... %! plot (GeneralizedExtremeValueDistribution, "Discrete", {true}) %!error ... %! plot (GeneralizedExtremeValueDistribution, "Parent", 12) %!error ... %! plot (GeneralizedExtremeValueDistribution, "Parent", "hax") %!error ... %! plot (GeneralizedExtremeValueDistribution, "invalidNAME", "pdf") %!error ... %! plot (GeneralizedExtremeValueDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (GeneralizedExtremeValueDistribution, 2) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 4) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), [1, 2]) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), {1}) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, "Display") %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, ... %! "Display", ["on"; "on"]) %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (GeneralizedExtremeValueDistribution.fit (x), 1, {[1 2 3 4]}, ... %! "Display", "on") ## 'truncate' method %!error ... %! truncate (GeneralizedExtremeValueDistribution) %!error ... %! truncate (GeneralizedExtremeValueDistribution, 2) %!error ... %! truncate (GeneralizedExtremeValueDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = GeneralizedExtremeValueDistribution(1, 1, 1); %! pd(2) = GeneralizedExtremeValueDistribution(1, 3, 1); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/GeneralizedParetoDistribution.m000066400000000000000000001100461475240274700263040ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef GeneralizedParetoDistribution ## -*- texinfo -*- ## @deftypefn {statistics} GeneralizedParetoDistribution ## ## Generalized Pareto probability distribution object. ## ## A @code{GeneralizedParetoDistribution} object consists of parameters, a ## model description, and sample data for a generalized Pareto probability ## distribution. ## ## The generalized Pareto distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{k} @tab Shape @tab @math{-Inf < k < Inf} ## @item @qcode{sigma} @tab Scale @tab @math{sigma > 0} ## @item @qcode{theta} @tab Location @tab @math{-Inf < theta < Inf} ## @end multitable ## ## There are several ways to create a @code{GeneralizedParetoDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{GeneralizedParetoDistribution (@var{k}, @var{sigma})} ## to create a generalized Pareto distribution with specified parameter values. ## @item Use the static method @qcode{GeneralizedParetoDistribution.fit (@var{x}, ## @var{k}, @var{freq})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{GeneralizedParetoDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{GeneralizedParetoDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the generalized Pareto distribution can be found ## at @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{fitdist, makedist, gpcdf, gpinv, gppdf, gprnd, gpfit, ## gplike, gpstat} ## @end deftypefn properties (Dependent = true) k sigma theta endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "GeneralizedParetoDistribution"; DistributionCode = "gp"; NumParameters = 3; ParameterNames = {"k", "sigma", "theta"}; ParameterDescription = {"Shape", "Scale", "Location"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin, -Inf; Inf, Inf, Inf]; ParameterLogCI = [false, true, false]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = GeneralizedParetoDistribution (k, sigma, theta) if (nargin == 0) k = 1; sigma = 1; theta = 1; endif checkparams (k, sigma, theta); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [k, sigma, theta]; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "normal distribution"); endfunction function disp (this) __disp__ (this, "normal distribution"); endfunction function this = set.k (this, k) checkparams (k, this.sigma, this.theta); this.InputData = []; this.ParameterValues(1) = k; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function k = get.k (this) k = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.k, sigma, this.theta); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction function this = set.theta (this, theta) checkparams (this.k, this.sigma, theta); this.InputData = []; this.ParameterValues(3) = theta; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function theta = get.theta (this) theta = this.ParameterValues(3); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {GeneralizedParetoDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = gpcdf (x, this.k, this.sigma, this.theta); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= gpcdf (lx, this.k, this.sigma, this.theta); p(! (lb | ub)) /= diff (gpcdf ([lx, ux], this.k, this.sigma, this.theta)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = gpcdf (this.Truncation(1), this.k, this.sigma, this.theta); up = gpcdf (this.Truncation(2), this.k, this.sigma, this.theta); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = gpinv (np, this.k, this.sigma, this.theta); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = gpinv (p, this.k, this.sigma, this.theta); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = gpstat (this.k, this.sigma, this.theta); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = gpcdf ([lx, ux], this.k, this.sigma, this.theta); m = gpinv (sum (Fa_b) / 2, this.k, this.sigma, this.theta); else m = gpinv (0.5, this.k, this.sigma, this.theta); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - gplike ([this.k, this.sigma, this.theta], ... this.InputData.data, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {GeneralizedParetoDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = gppdf (x, this.k, this.sigma, this.theta); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (gpcdf ([lx, ux], this.k, this.sigma, this.theta)); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {} plot (@var{pd}) ## @deftypefnx {GeneralizedParetoDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {GeneralizedParetoDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {GeneralizedParetoDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {GeneralizedParetoDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {GeneralizedParetoDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the generalized Pareto distribution, @qcode{@var{pnum} = 1} selects ## the parameter @qcode{k}, @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}, and @qcode{@var{pnum} = 3} selects the parameter @var{theta}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {GeneralizedParetoDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {GeneralizedParetoDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {GeneralizedParetoDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (gpcdf ([lx, ux], this.k, this.sigma, this.theta)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = gprnd (this.k, this.sigma, this.theta, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = gprnd (this.k, this.sigma, this.theta, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {GeneralizedParetoDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = gpstat (this.k, this.sigma, this.theta); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, theta, varargin) ## Check input arguments if (nargin < 3) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 4) freq = []; else freq = varargin{2}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{3}; endif ## Fit data [phat, pci] = gpfit (x, theta, alpha, freq, options); [~, acov] = gplike (phat, x, freq); ## Create fitted distribution object pd = GeneralizedParetoDistribution.makeFitted (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) k = phat(1); sigma = phat(2); theta = phat(3); pd = GeneralizedParetoDistribution (k, sigma, theta); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false, true]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (k, sigma, theta) if (! (isscalar (k) && isnumeric (k) && isreal (k) && isfinite (k))) error ("GeneralizedParetoDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("GeneralizedParetoDistribution: SIGMA must be a positive real scalar.") endif if (! (isscalar (theta) && isnumeric (theta) && isreal (theta) && isfinite (theta))) error ("GeneralizedParetoDistribution: THETA must be a real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = GeneralizedParetoDistribution (1, 1, 1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0, 0.5, 0.6667, 0.75, 0.8], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.6667, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.3333, 0.5, 0.6667, 0.75], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.6667, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [1, 1.25, 1.6667, 2.5, 5, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2222, 2.5, 2.8571, 3.3333, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 1.6667, 2.5, 5, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.5, 2.8571, 3.3333, 4, NaN], 1e-4); %!assert (iqr (pd), 2.6667, 1e-4); %!assert (iqr (t), 0.9143, 1e-4); %!assert (mean (pd), Inf); %!assert (mean (t), 2.7726, 1e-4); %!assert (median (pd), 2); %!assert (median (t), 2.6667, 1e-4); %!assert (pdf (pd, [0:5]), [0, 1, 0.25, 0.1111, 0.0625, 0.04], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1, 0.4444, 0.25, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 1, 0.25, 0.1111, 0.0625, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 1, 0.4444, 0.25, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), Inf); %!assert (std (t), 0.5592, 1e-4); %!assert (var (pd), Inf); %!assert (var (t), 0.3128, 1e-4); ## Test input validation ## 'GeneralizedParetoDistribution' constructor %!error ... %! GeneralizedParetoDistribution(Inf, 1, 1) %!error ... %! GeneralizedParetoDistribution(i, 1, 1) %!error ... %! GeneralizedParetoDistribution("a", 1, 1) %!error ... %! GeneralizedParetoDistribution([1, 2], 1, 1) %!error ... %! GeneralizedParetoDistribution(NaN, 1, 1) %!error ... %! GeneralizedParetoDistribution(1, 0, 1) %!error ... %! GeneralizedParetoDistribution(1, -1, 1) %!error ... %! GeneralizedParetoDistribution(1, Inf, 1) %!error ... %! GeneralizedParetoDistribution(1, i, 1) %!error ... %! GeneralizedParetoDistribution(1, "a", 1) %!error ... %! GeneralizedParetoDistribution(1, [1, 2], 1) %!error ... %! GeneralizedParetoDistribution(1, NaN, 1) %!error ... %! GeneralizedParetoDistribution(1, 1, Inf) %!error ... %! GeneralizedParetoDistribution(1, 1, i) %!error ... %! GeneralizedParetoDistribution(1, 1, "a") %!error ... %! GeneralizedParetoDistribution(1, 1, [1, 2]) %!error ... %! GeneralizedParetoDistribution(1, 1, NaN) ## 'cdf' method %!error ... %! cdf (GeneralizedParetoDistribution, 2, "uper") %!error ... %! cdf (GeneralizedParetoDistribution, 2, 3) ## 'paramci' method %!shared x %! x = gprnd (1, 1, 1, [1, 100]); %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha") %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", 0) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", 1) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", [0.5 2]) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", "") %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", {0.05}) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), ... %! "parameter", "sigma", "alpha", {0.05}) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), ... %! "parameter", {"k", "sigma", "param"}) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", 0.01, ... %! "parameter", {"k", "sigma", "param"}) %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "parameter", "param") %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "NAME", "value") %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", 0.01, ... %! "NAME", "value") %!error ... %! paramci (GeneralizedParetoDistribution.fit (x, 1), "alpha", 0.01, ... %! "parameter", "sigma", "NAME", "value") ## 'plot' method %!error ... %! plot (GeneralizedParetoDistribution, "Parent") %!error ... %! plot (GeneralizedParetoDistribution, "PlotType", 12) %!error ... %! plot (GeneralizedParetoDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (GeneralizedParetoDistribution, "PlotType", "pdfcdf") %!error ... %! plot (GeneralizedParetoDistribution, "Discrete", "pdfcdf") %!error ... %! plot (GeneralizedParetoDistribution, "Discrete", [1, 0]) %!error ... %! plot (GeneralizedParetoDistribution, "Discrete", {true}) %!error ... %! plot (GeneralizedParetoDistribution, "Parent", 12) %!error ... %! plot (GeneralizedParetoDistribution, "Parent", "hax") %!error ... %! plot (GeneralizedParetoDistribution, "invalidNAME", "pdf") %!error ... %! plot (GeneralizedParetoDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (GeneralizedParetoDistribution, 2) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 3) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), [1, 2]) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), {1}) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, ones (2)) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, "Display") %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, "Display", 1) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, "Display", {1}) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, "Display", {"on"}) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, ... %! "Display", ["on"; "on"]) %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, "Display", "onnn") %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, "NAME", "on") %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, {"NAME"}, "on") %!error ... %! proflik (GeneralizedParetoDistribution.fit (x, 1), 1, {[1 2 3 4]}, ... %! "Display", "on") ## 'truncate' method %!error ... %! truncate (GeneralizedParetoDistribution) %!error ... %! truncate (GeneralizedParetoDistribution, 2) %!error ... %! truncate (GeneralizedParetoDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = GeneralizedParetoDistribution(1, 1, 1); %! pd(2) = GeneralizedParetoDistribution(1, 3, 1); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/HalfNormalDistribution.m000066400000000000000000001031561475240274700247270ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef HalfNormalDistribution ## -*- texinfo -*- ## @deftypefn {statistics} HalfNormalDistribution ## ## Half-normal probability distribution object. ## ## A @code{HalfNormalDistribution} object consists of parameters, a model ## description, and sample data for a half-normal probability distribution. ## ## The half-normal distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Location @tab @math{-Inf < mu < Inf} ## @item @qcode{sigma} @tab Scale @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{HalfNormalDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{HalfNormalDistribution (@var{mu}, @var{sigma})} ## to create a half-normal distribution with specified parameter values. ## @item Use the static method @qcode{HalfNormalDistribution.fit (@var{x}, ## @var{mu}, @var{freq})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{HalfNormalDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{HalfNormalDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{fitdist, makedist, hncdf, hninv, hnpdf, hnrnd, hnfit, ## hnlike, hnstat} ## @end deftypefn properties (Dependent = true) mu sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "HalfNormalDistribution"; DistributionCode = "hn"; NumParameters = 2; ParameterNames = {"mu", "sigma"}; ParameterDescription = {"Location", "Scale"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = HalfNormalDistribution (mu, sigma) if (nargin == 0) mu = 0; sigma = 1; endif checkparams (mu, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "normal distribution"); endfunction function disp (this) __disp__ (this, "normal distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {HalfNormalDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = hncdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= hncdf (lx, this.mu, this.sigma); p(! (lb | ub)) /= diff (hncdf ([lx, ux], this.mu, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = hncdf (this.Truncation(1), this.mu, this.sigma); up = hncdf (this.Truncation(2), this.mu, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = hninv (np, this.mu, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = hninv (p, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = hnstat (this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = hncdf ([lx, ux], this.mu, this.sigma); m = hninv (sum (Fa_b) / 2, this.mu, this.sigma); else m = hninv (0.5, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - hnlike ([this.mu, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {HalfNormalDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = hnpdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (hncdf ([lx, ux], this.mu, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {} plot (@var{pd}) ## @deftypefnx {HalfNormalDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {HalfNormalDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {HalfNormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {HalfNormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {HalfNormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the half-normal distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {HalfNormalDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {HalfNormalDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {HalfNormalDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (hncdf ([lx, ux], this.mu, this.sigma)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = hnrnd (this.mu, this.sigma, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = hnrnd (this.mu, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {HalfNormalDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = hnstat (this.mu, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, mu, varargin) ## Check input arguments if (nargin < 3) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 4) freq = []; else freq = varargin{2}; endif ## Fit data [phat, pci] = hnfit (x, mu, alpha, freq); [~, acov] = hnlike (phat, x, freq); ## Create fitted distribution object pd = HalfNormalDistribution.makeFitted (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) mu = phat(1); sigma = phat(2); pd = HalfNormalDistribution (mu, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [true, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) error ("HalfNormalDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("HalfNormalDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = HalfNormalDistribution (0, 1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.6827, 0.9545, 0.9973, 0.9999, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.9420, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.8664, 0.9545, 0.9973, 0.9999], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.9420, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.2533, 0.5244, 0.8416, 1.2816, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.0923, 2.2068, 2.3607, 2.6064, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.5244, 0.8416, 1.2816, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.2068, 2.3607, 2.6064, 4, NaN], 1e-4); %!assert (iqr (pd), 0.8317, 1e-4); %!assert (iqr (t), 0.4111, 1e-4); %!assert (mean (pd), 0.7979, 1e-4); %!assert (mean (t), 2.3706, 1e-4); %!assert (median (pd), 0.6745, 1e-4); %!assert (median (t), 2.2771, 1e-4); %!assert (pdf (pd, [0:5]), [0.7979, 0.4839, 0.1080, 0.0089, 0.0003, 0], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 2.3765, 0.1951, 0.0059, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.4839, 0.1080, 0.0089, 0.0003, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 2.3765, 0.1951, 0.0059, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 0.6028, 1e-4); %!assert (std (t), 0.3310, 1e-4); %!assert (var (pd), 0.3634, 1e-4); %!assert (var (t), 0.1096, 1e-4); ## Test input validation ## 'HalfNormalDistribution' constructor %!error ... %! HalfNormalDistribution(Inf, 1) %!error ... %! HalfNormalDistribution(i, 1) %!error ... %! HalfNormalDistribution("a", 1) %!error ... %! HalfNormalDistribution([1, 2], 1) %!error ... %! HalfNormalDistribution(NaN, 1) %!error ... %! HalfNormalDistribution(1, 0) %!error ... %! HalfNormalDistribution(1, -1) %!error ... %! HalfNormalDistribution(1, Inf) %!error ... %! HalfNormalDistribution(1, i) %!error ... %! HalfNormalDistribution(1, "a") %!error ... %! HalfNormalDistribution(1, [1, 2]) %!error ... %! HalfNormalDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (HalfNormalDistribution, 2, "uper") %!error ... %! cdf (HalfNormalDistribution, 2, 3) ## 'paramci' method %!shared x %! x = hnrnd (1, 1, [1, 100]); %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha") %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", 0) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", 1) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", [0.5 2]) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", "") %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", {0.05}) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "parameter", "sigma", ... %! "alpha", {0.05}) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "parameter", "param") %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (HalfNormalDistribution.fit (x, 1),"NAME", "value") %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", 0.01, ... %! "NAME", "value") %!error ... %! paramci (HalfNormalDistribution.fit (x, 1), "alpha", 0.01, ... %! "parameter", "sigma", "NAME", "value") ## 'plot' method %!error ... %! plot (HalfNormalDistribution, "Parent") %!error ... %! plot (HalfNormalDistribution, "PlotType", 12) %!error ... %! plot (HalfNormalDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (HalfNormalDistribution, "PlotType", "pdfcdf") %!error ... %! plot (HalfNormalDistribution, "Discrete", "pdfcdf") %!error ... %! plot (HalfNormalDistribution, "Discrete", [1, 0]) %!error ... %! plot (HalfNormalDistribution, "Discrete", {true}) %!error ... %! plot (HalfNormalDistribution, "Parent", 12) %!error ... %! plot (HalfNormalDistribution, "Parent", "hax") %!error ... %! plot (HalfNormalDistribution, "invalidNAME", "pdf") %!error ... %! plot (HalfNormalDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (HalfNormalDistribution, 2) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 3) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), [1, 2]) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), {1}) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 1) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, ones (2)) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "Display") %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "Display", 1) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "Display", {1}) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "Display", {"on"}) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "Display", ["on"; "on"]) %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "Display", "onnn") %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, "NAME", "on") %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, {"NAME"}, "on") %!error ... %! proflik (HalfNormalDistribution.fit (x, 1), 2, {[1 2 3 4]}, ... %! "Display", "on") ## 'truncate' method %!error ... %! truncate (HalfNormalDistribution) %!error ... %! truncate (HalfNormalDistribution, 2) %!error ... %! truncate (HalfNormalDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = HalfNormalDistribution(1, 1); %! pd(2) = HalfNormalDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/InverseGaussianDistribution.m000066400000000000000000001050711475240274700260100ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef InverseGaussianDistribution ## -*- texinfo -*- ## @deftypefn {statistics} InverseGaussianDistribution ## ## Logistic probability distribution object. ## ## A @code{InverseGaussianDistribution} object consists of parameters, a model ## description, and sample data for a logistic probability distribution. ## ## The logistic distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Scale parameter @tab @math{mu >= 0} ## @item @qcode{lambda} @tab Shape parameter @tab @math{lambda > 0} ## @end multitable ## ## There are several ways to create a @code{InverseGaussianDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{InverseGaussianDistribution (@var{mu}, @var{lambda})} ## to create a logistic distribution with specified parameter values. ## @item Use the static method @qcode{InverseGaussianDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{InverseGaussianDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{InverseGaussianDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{fitdist, makedist, invgcdf, invginv, invgpdf, invgrnd, lognfit, ## invglike, invgstat} ## @end deftypefn properties (Dependent = true) mu lambda endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "InverseGaussianDistribution"; DistributionCode = "invg"; NumParameters = 2; ParameterNames = {"mu", "lambda"}; ParameterDescription = {"Scale", "Shape"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = InverseGaussianDistribution (mu, lambda) if (nargin == 0) mu = 1; lambda = 1; endif checkparams (mu, lambda); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, lambda]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "logistic distribution"); endfunction function disp (this) __disp__ (this, "logistic distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.lambda); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.lambda (this, lambda) checkparams (this.mu, lambda); this.InputData = []; this.ParameterValues(2) = lambda; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function lambda = get.lambda (this) lambda = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {InverseGaussianDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = invgcdf (x, this.mu, this.lambda); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= invgcdf (lx, this.mu, this.lambda); p(! (lb | ub)) /= diff (invgcdf ([lx, ux], this.mu, this.lambda)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = invgcdf (this.Truncation(1), this.mu, this.lambda); up = invgcdf (this.Truncation(2), this.mu, this.lambda); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = invginv (np, this.mu, this.lambda); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = invginv (p, this.mu, this.lambda); endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = invgstat (this.mu, this.lambda); endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = invgcdf ([lx, ux], this.mu, this.lambda); m = invginv (sum (Fa_b) / 2, this.mu, this.lambda); else m = invginv (0.5, this.mu, this.lambda); endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - invglike ([this.mu, this.lambda], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {InverseGaussianDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = invgpdf (x, this.mu, this.lambda); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (invgcdf ([lx, ux], this.mu, this.lambda)); endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {} plot (@var{pd}) ## @deftypefnx {InverseGaussianDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {InverseGaussianDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {InverseGaussianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {InverseGaussianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {InverseGaussianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the inverse Gaussian distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{lambda}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {InverseGaussianDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {InverseGaussianDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {InverseGaussianDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (invgcdf ([lx, ux], this.mu, this.lambda)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = invgrnd (this.mu, this.lambda, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = invgrnd (this.mu, this.lambda, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {InverseGaussianDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = invgstat (this.mu, this.lambda); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = invgfit (x, alpha, censor, freq, options); [~, acov] = invglike (phat, x, censor, freq); ## Create fitted distribution object pd = InverseGaussianDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); lambda = phat(2); pd = InverseGaussianDistribution (mu, lambda); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, lambda) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu) && mu > 0)) error ("InverseGaussianDistribution: MU must be a positive real scalar.") endif if (! (isscalar (lambda) && isnumeric (lambda) && isreal (lambda) && isfinite (lambda) && lambda > 0)) error ("InverseGaussianDistribution: LAMBDA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = InverseGaussianDistribution (1, 1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.6681, 0.8855, 0.9532, 0.9791, 0.9901], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7234, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.8108, 0.8855, 0.9532, 0.9791], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.7234, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.3320, 0.5411, 0.8483, 1.4479, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1889, 2.4264, 2.7417, 3.1993, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.5411, 0.8483, 1.4479, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4264, 2.7417, 3.1993, 4, NaN], 1e-4); %!assert (iqr (pd), 0.8643, 1e-4); %!assert (iqr (t), 0.8222, 1e-4); %!assert (mean (pd), 1); %!assert (mean (t), 2.6953, 1e-4); %!assert (median (pd), 0.6758, 1e-4); %!assert (median (t), 2.5716, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.3989, 0.1098, 0.0394, 0.0162, 0.0072], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.1736, 0.4211, 0.1730, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.3989, 0.1098, 0.0394, 0.0162, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 1.1736, 0.4211, 0.1730, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1); %!assert (std (t), 0.5332, 1e-4); %!assert (var (pd), 1); %!assert (var (t), 0.2843, 1e-4); ## Test input validation ## 'InverseGaussianDistribution' constructor %!error ... %! InverseGaussianDistribution(0, 1) %!error ... %! InverseGaussianDistribution(Inf, 1) %!error ... %! InverseGaussianDistribution(i, 1) %!error ... %! InverseGaussianDistribution("a", 1) %!error ... %! InverseGaussianDistribution([1, 2], 1) %!error ... %! InverseGaussianDistribution(NaN, 1) %!error ... %! InverseGaussianDistribution(1, 0) %!error ... %! InverseGaussianDistribution(1, -1) %!error ... %! InverseGaussianDistribution(1, Inf) %!error ... %! InverseGaussianDistribution(1, i) %!error ... %! InverseGaussianDistribution(1, "a") %!error ... %! InverseGaussianDistribution(1, [1, 2]) %!error ... %! InverseGaussianDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (InverseGaussianDistribution, 2, "uper") %!error ... %! cdf (InverseGaussianDistribution, 2, 3) ## 'paramci' method %!shared x %! x = invgrnd (1, 1, [1, 100]); %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha") %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", 0) %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", 1) %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", "") %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (InverseGaussianDistribution.fit (x), "parameter", "mu", ... %! "alpha", {0.05}) %!error ... %! paramci (InverseGaussianDistribution.fit (x), ... %! "parameter", {"mu", "lambda", "param"}) %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "lambda", "param"}) %!error ... %! paramci (InverseGaussianDistribution.fit (x), "parameter", "param") %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (InverseGaussianDistribution.fit (x), "NAME", "value") %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (InverseGaussianDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "mu", "NAME", "value") ## 'plot' method %!error ... %! plot (InverseGaussianDistribution, "Parent") %!error ... %! plot (InverseGaussianDistribution, "PlotType", 12) %!error ... %! plot (InverseGaussianDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (InverseGaussianDistribution, "PlotType", "pdfcdf") %!error ... %! plot (InverseGaussianDistribution, "Discrete", "pdfcdf") %!error ... %! plot (InverseGaussianDistribution, "Discrete", [1, 0]) %!error ... %! plot (InverseGaussianDistribution, "Discrete", {true}) %!error ... %! plot (InverseGaussianDistribution, "Parent", 12) %!error ... %! plot (InverseGaussianDistribution, "Parent", "hax") %!error ... %! plot (InverseGaussianDistribution, "invalidNAME", "pdf") %!error ... %! plot (InverseGaussianDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (InverseGaussianDistribution, 2) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 3) %!error ... %! proflik (InverseGaussianDistribution.fit (x), [1, 2]) %!error ... %! proflik (InverseGaussianDistribution.fit (x), {1}) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "Display") %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (InverseGaussianDistribution.fit (x), 1, {[1 2 3]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (InverseGaussianDistribution) %!error ... %! truncate (InverseGaussianDistribution, 2) %!error ... %! truncate (InverseGaussianDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = InverseGaussianDistribution(1, 1); %! pd(2) = InverseGaussianDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/LogisticDistribution.m000066400000000000000000001031671475240274700244630ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef LogisticDistribution ## -*- texinfo -*- ## @deftypefn {statistics} LogisticDistribution ## ## Logistic probability distribution object. ## ## A @code{LogisticDistribution} object consists of parameters, a model ## description, and sample data for a logistic probability distribution. ## ## The logistic distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Mean of logarithmic values @tab @math{mu >= 0} ## @item @qcode{sigma} @tab Scale of logarithmic values @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{LogisticDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{LogisticDistribution (@var{mu}, @var{sigma})} ## to create a logistic distribution with specified parameter values. ## @item Use the static method @qcode{LogisticDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{LogisticDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{LogisticDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, s}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{fitdist, makedist, logicdf, logiinv, logipdf, logirnd, lognfit, ## logilike, logistat} ## @end deftypefn properties (Dependent = true) mu sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "LogisticDistribution"; DistributionCode = "logi"; NumParameters = 2; ParameterNames = {"mu", "sigma"}; ParameterDescription = {"Mean of logarithmic values", ... "Scale of logarithmic values"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [0, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = LogisticDistribution (mu, sigma) if (nargin == 0) mu = 0; sigma = 1; endif checkparams (mu, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "logistic distribution"); endfunction function disp (this) __disp__ (this, "logistic distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {LogisticDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = logicdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= logicdf (lx, this.mu, this.sigma); p(! (lb | ub)) /= diff (logicdf ([lx, ux], this.mu, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = logicdf (this.Truncation(1), this.mu, this.sigma); up = logicdf (this.Truncation(2), this.mu, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = logiinv (np, this.mu, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = logiinv (p, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = logistat (this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = logicdf ([lx, ux], this.mu, this.sigma); m = logiinv (sum (Fa_b) / 2, this.mu, this.sigma); else m = this.mu; endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - logilike ([this.mu, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {LogisticDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = logipdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (logicdf ([lx, ux], this.mu, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {} plot (@var{pd}) ## @deftypefnx {LogisticDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {LogisticDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {LogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {LogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {LogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the logistic distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {LogisticDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {LogisticDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {LogisticDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = logicdf (this.Truncation(1), this.mu, this.sigma); up = logicdf (this.Truncation(2), this.mu, this.sigma); u = unifrnd (lp, up, varargin{:}); r = - log (1 ./ u - 1) .* this.sigma + this.mu; else r = logirnd (this.mu, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {LogisticDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = logistat (this.mu, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = logifit (x, alpha, censor, freq, options); [~, acov] = logilike (phat, x, censor, freq); ## Create fitted distribution object pd = LogisticDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); sigma = phat(2); pd = LogisticDistribution (mu, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu) && mu >= 0)) error ("LogisticDistribution: MU must be a nonnegative real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("LogisticDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = LogisticDistribution (0, 1); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0.5, 0.7311, 0.8808, 0.9526, 0.9820, 0.9933], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7091, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.8176, 0.8808, 0.9526, 0.9820], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.7091, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [-Inf, -1.3863, -0.4055, 0.4055, 1.3863, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2088, 2.4599, 2.7789, 3.2252, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, -0.4055, 0.4055, 1.3863, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4599, 2.7789, 3.2252, 4, NaN], 1e-4); %!assert (iqr (pd), 2.1972, 1e-4); %!assert (iqr (t), 0.8286, 1e-4); %!assert (mean (pd), 0, 1e-4); %!assert (mean (t), 2.7193, 1e-4); %!assert (median (pd), 0); %!assert (median (t), 2.6085, 1e-4); %!assert (pdf (pd, [0:5]), [0.25, 0.1966, 0.1050, 0.0452, 0.0177, 0.0066], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.0373, 0.4463, 0.1745, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0.1966, 0.1966, 0.1050, 0.0452, 0.0177, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 1.0373, 0.4463, 0.1745, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.8138, 1e-4); %!assert (std (t), 0.5320, 1e-4); %!assert (var (pd), 3.2899, 1e-4); %!assert (var (t), 0.2830, 1e-4); ## Test input validation ## 'LogisticDistribution' constructor %!error ... %! LogisticDistribution(Inf, 1) %!error ... %! LogisticDistribution(i, 1) %!error ... %! LogisticDistribution("a", 1) %!error ... %! LogisticDistribution([1, 2], 1) %!error ... %! LogisticDistribution(NaN, 1) %!error ... %! LogisticDistribution(1, 0) %!error ... %! LogisticDistribution(1, -1) %!error ... %! LogisticDistribution(1, Inf) %!error ... %! LogisticDistribution(1, i) %!error ... %! LogisticDistribution(1, "a") %!error ... %! LogisticDistribution(1, [1, 2]) %!error ... %! LogisticDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (LogisticDistribution, 2, "uper") %!error ... %! cdf (LogisticDistribution, 2, 3) ## 'paramci' method %!shared x %! x = logirnd (1, 1, [1, 100]); %!error ... %! paramci (LogisticDistribution.fit (x), "alpha") %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", 0) %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", 1) %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", "") %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (LogisticDistribution.fit (x), "parameter", "mu", "alpha", {0.05}) %!error ... %! paramci (LogisticDistribution.fit (x), "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (LogisticDistribution.fit (x), "parameter", "param") %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (LogisticDistribution.fit (x), "NAME", "value") %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (LogisticDistribution.fit (x), "alpha", 0.01, "parameter", "mu", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (LogisticDistribution, "Parent") %!error ... %! plot (LogisticDistribution, "PlotType", 12) %!error ... %! plot (LogisticDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (LogisticDistribution, "PlotType", "pdfcdf") %!error ... %! plot (LogisticDistribution, "Discrete", "pdfcdf") %!error ... %! plot (LogisticDistribution, "Discrete", [1, 0]) %!error ... %! plot (LogisticDistribution, "Discrete", {true}) %!error ... %! plot (LogisticDistribution, "Parent", 12) %!error ... %! plot (LogisticDistribution, "Parent", "hax") %!error ... %! plot (LogisticDistribution, "invalidNAME", "pdf") %!error ... %! plot (LogisticDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (LogisticDistribution, 2) %!error ... %! proflik (LogisticDistribution.fit (x), 3) %!error ... %! proflik (LogisticDistribution.fit (x), [1, 2]) %!error ... %! proflik (LogisticDistribution.fit (x), {1}) %!error ... %! proflik (LogisticDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (LogisticDistribution.fit (x), 1, "Display") %!error ... %! proflik (LogisticDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (LogisticDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (LogisticDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (LogisticDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (LogisticDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (LogisticDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (LogisticDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (LogisticDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (LogisticDistribution) %!error ... %! truncate (LogisticDistribution, 2) %!error ... %! truncate (LogisticDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = LogisticDistribution(1, 1); %! pd(2) = LogisticDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/LoglogisticDistribution.m000066400000000000000000001036751475240274700251710ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef LoglogisticDistribution ## -*- texinfo -*- ## @deftypefn {statistics} LoglogisticDistribution ## ## Loglogistic probability distribution object. ## ## A @code{LoglogisticDistribution} object consists of parameters, a model ## description, and sample data for a loglogistic probability distribution. ## ## The loglogistic distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Mean of logarithmic values @tab @math{mu >= 0} ## @item @qcode{sigma} @tab Scale of logarithmic values @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{LoglogisticDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{LoglogisticDistribution (@var{mu}, @var{sigma})} ## to create a loglogistic distribution with specified parameter values. ## @item Use the static method @qcode{LoglogisticDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{LoglogisticDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{LoglogisticDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Loglogistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, s}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{fitdist, makedist, loglcdf, loglinv, loglpdf, loglrnd, lognfit, ## logllike, loglstat} ## @end deftypefn properties (Dependent = true) mu sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "LoglogisticDistribution"; DistributionCode = "logl"; NumParameters = 2; ParameterNames = {"mu", "sigma"}; ParameterDescription = {"Mean of logarithmic values", ... "Scale of logarithmic values"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [0, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = LoglogisticDistribution (mu, sigma) if (nargin == 0) mu = 0; sigma = 1; endif checkparams (mu, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Log-Logistic distribution"); endfunction function disp (this) __disp__ (this, "Log-Logistic distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {LoglogisticDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = loglcdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= loglcdf (lx, this.mu, this.sigma); p(! (lb | ub)) /= diff (loglcdf ([lx, ux], this.mu, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = loglcdf (this.Truncation(1), this.mu, this.sigma); up = loglcdf (this.Truncation(2), this.mu, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = loglinv (np, this.mu, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = loglinv (p, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = loglstat (this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = loglcdf ([lx, ux], this.mu, this.sigma); m = loglinv (sum (Fa_b) / 2, this.mu, this.sigma); else m = exp (this.mu); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - logllike ([this.mu, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {LoglogisticDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = loglpdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (loglcdf ([lx, ux], this.mu, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {} plot (@var{pd}) ## @deftypefnx {LoglogisticDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {LoglogisticDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {LoglogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {LoglogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {LoglogisticDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the loglogistic distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {LoglogisticDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {LoglogisticDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {LoglogisticDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif a = exp (this.mu); b = 1 / this.sigma; if (this.IsTruncated) lp = loglcdf (this.Truncation(1), this.mu, this.sigma); up = loglcdf (this.Truncation(2), this.mu, this.sigma); u = unifrnd (lp, up, varargin{:}); r = exp (this.mu) .* (u ./ (1 - u)) .^ (this.sigma); else r = loglrnd (this.mu, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {LoglogisticDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = loglstat (this.mu, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = loglfit (x, alpha, censor, freq, options); [~, acov] = logllike (phat, x, censor, freq); ## Create fitted distribution object pd = LoglogisticDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); sigma = phat(2); pd = LoglogisticDistribution (mu, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu) && mu >= 0)) error ("LoglogisticDistribution: MU must be a nonnegative real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("LoglogisticDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = LoglogisticDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.5, 0.6667, 0.75, 0.8, 0.8333], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.625, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.6, 0.6667, 0.75, 0.8], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.625, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.25, 0.6667, 1.5, 4, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2609, 2.5714, 2.9474, 3.4118, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.6667, 1.5, 4, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.5714, 2.9474, 3.4118, 4, NaN], 1e-4); %!assert (iqr (pd), 2.6667, 1e-4); %!assert (iqr (t), 0.9524, 1e-4); %!assert (mean (pd), Inf); %!assert (mean (t), 2.8312, 1e-4); %!assert (median (pd), 1, 1e-4); %!assert (median (t), 2.75, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.25, 0.1111, 0.0625, 0.04, 0.0278], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.8333, 0.4687, 0.3, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.25, 0.1111, 0.0625, 0.04, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 0.8333, 0.4687, 0.3, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), Inf); %!assert (std (t), 0.5674, 1e-4); %!assert (var (pd), Inf); %!assert (var (t), 0.3220, 1e-4); ## Test input validation ## 'LoglogisticDistribution' constructor %!error ... %! LoglogisticDistribution(Inf, 1) %!error ... %! LoglogisticDistribution(i, 1) %!error ... %! LoglogisticDistribution("a", 1) %!error ... %! LoglogisticDistribution([1, 2], 1) %!error ... %! LoglogisticDistribution(NaN, 1) %!error ... %! LoglogisticDistribution(1, 0) %!error ... %! LoglogisticDistribution(1, -1) %!error ... %! LoglogisticDistribution(1, Inf) %!error ... %! LoglogisticDistribution(1, i) %!error ... %! LoglogisticDistribution(1, "a") %!error ... %! LoglogisticDistribution(1, [1, 2]) %!error ... %! LoglogisticDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (LoglogisticDistribution, 2, "uper") %!error ... %! cdf (LoglogisticDistribution, 2, 3) ## 'paramci' method %!shared x %! x = loglrnd (1, 1, [1, 100]); %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha") %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", 0) %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", 1) %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", "") %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (LoglogisticDistribution.fit (x), "parameter", "mu", "alpha", {0.05}) %!error ... %! paramci (LoglogisticDistribution.fit (x), "parameter", {"mu", "sigma", "pa"}) %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (LoglogisticDistribution.fit (x), "parameter", "param") %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", 0.01, "parameter", "parm") %!error ... %! paramci (LoglogisticDistribution.fit (x), "NAME", "value") %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (LoglogisticDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "mu", "NAME", "value") ## 'plot' method %!error ... %! plot (LoglogisticDistribution, "Parent") %!error ... %! plot (LoglogisticDistribution, "PlotType", 12) %!error ... %! plot (LoglogisticDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (LoglogisticDistribution, "PlotType", "pdfcdf") %!error ... %! plot (LoglogisticDistribution, "Discrete", "pdfcdf") %!error ... %! plot (LoglogisticDistribution, "Discrete", [1, 0]) %!error ... %! plot (LoglogisticDistribution, "Discrete", {true}) %!error ... %! plot (LoglogisticDistribution, "Parent", 12) %!error ... %! plot (LoglogisticDistribution, "Parent", "hax") %!error ... %! plot (LoglogisticDistribution, "invalidNAME", "pdf") %!error ... %! plot (LoglogisticDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (LoglogisticDistribution, 2) %!error ... %! proflik (LoglogisticDistribution.fit (x), 3) %!error ... %! proflik (LoglogisticDistribution.fit (x), [1, 2]) %!error ... %! proflik (LoglogisticDistribution.fit (x), {1}) %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "Display") %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (LoglogisticDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (LoglogisticDistribution) %!error ... %! truncate (LoglogisticDistribution, 2) %!error ... %! truncate (LoglogisticDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = LoglogisticDistribution(1, 1); %! pd(2) = LoglogisticDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/LognormalDistribution.m000066400000000000000000001025221475240274700246320ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef LognormalDistribution ## -*- texinfo -*- ## @deftypefn {statistics} LognormalDistribution ## ## Lognormal probability distribution object. ## ## A @code{LognormalDistribution} object consists of parameters, a model ## description, and sample data for a lognormal probability distribution. ## ## The lognormal distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Mean of logarithmic values @tab @math{-Inf < mu < Inf} ## @item @qcode{sigma} @tab Standard deviation of logarithmic values @tab ## @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{LognormalDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{LognormalDistribution (@var{mu}, @var{sigma})} ## to create a lognormal distribution with specified parameter values. ## @item Use the static method @qcode{LognormalDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{LognormalDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{LognormalDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{fitdist, makedist, logncdf, logninv, lognpdf, lognrnd, lognfit, ## lognlike, lognstat} ## @end deftypefn properties (Dependent = true) mu sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "LognormalDistribution"; DistributionCode = "logn"; NumParameters = 2; ParameterNames = {"mu", "sigma"}; ParameterDescription = {"Mean of logarithmic values", ... "Standard deviation of logarithmic values"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = LognormalDistribution (mu, sigma) if (nargin == 0) mu = 0; sigma = 1; endif checkparams (mu, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "lognormal distribution"); endfunction function disp (this) __disp__ (this, "lognormal distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {LognormalDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = logncdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= logncdf (lx, this.mu, this.sigma); p(! (lb | ub)) /= diff (logncdf ([lx, ux], this.mu, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = logncdf (this.Truncation(1), this.mu, this.sigma); up = logncdf (this.Truncation(2), this.mu, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = logninv (np, this.mu, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = logninv (p, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = lognstat (this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = logncdf ([lx, ux], this.mu, this.sigma); m = logninv (sum (Fa_b) / 2, this.mu, this.sigma); else m = logninv (0.5, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - lognlike ([this.mu, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {LognormalDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = lognpdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (logncdf ([lx, ux], this.mu, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {} plot (@var{pd}) ## @deftypefnx {LognormalDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {LognormalDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {LognormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {LognormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {LognormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the lognormal distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {LognormalDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {LognormalDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {LognormalDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = logncdf (this.Truncation(1), this.mu, this.sigma); up = logncdf (this.Truncation(2), this.mu, this.sigma); u = unifrnd (lp, up, varargin{:}); r = exp (this.mu + this.sigma .* (-sqrt (2) * erfcinv (2 * u))); else r = lognrnd (this.mu, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {LognormalDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = lognstat (this.mu, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = lognfit (x, alpha, censor, freq, options); [~, acov] = lognlike (phat, x, censor, freq); ## Create fitted distribution object pd = LognormalDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); sigma = phat(2); pd = LognormalDistribution (mu, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) error ("LognormalDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("LognormalDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = LognormalDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.5, 0.7559, 0.8640, 0.9172, 0.9462], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.6705, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.6574, 0.7559, 0.8640, 0.9172], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.6705, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.4310, 0.7762, 1.2883, 2.3201, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2256, 2.5015, 2.8517, 3.3199, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.7762, 1.2883, 2.3201, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.5015, 2.8517, 3.3199, 4, NaN], 1e-4); %!assert (iqr (pd), 1.4536, 1e-4); %!assert (iqr (t), 0.8989, 1e-4); %!assert (mean (pd), 1.6487, 1e-4); %!assert (mean (t), 2.7692, 1e-4); %!assert (median (pd), 1, 1e-4); %!assert (median (t), 2.6653, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.3989, 0.1569, 0.0727, 0.0382, 0.0219], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.9727, 0.4509, 0.2366, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.3989, 0.1569, 0.0727, 0.0382, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 0.9727, 0.4509, 0.2366, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 2.1612, 1e-4); %!assert (std (t), 0.5540, 1e-4); %!assert (var (pd), 4.6708, 1e-4); %!assert (var (t), 0.3069, 1e-4); ## Test input validation ## 'LognormalDistribution' constructor %!error ... %! LognormalDistribution(Inf, 1) %!error ... %! LognormalDistribution(i, 1) %!error ... %! LognormalDistribution("a", 1) %!error ... %! LognormalDistribution([1, 2], 1) %!error ... %! LognormalDistribution(NaN, 1) %!error ... %! LognormalDistribution(1, 0) %!error ... %! LognormalDistribution(1, -1) %!error ... %! LognormalDistribution(1, Inf) %!error ... %! LognormalDistribution(1, i) %!error ... %! LognormalDistribution(1, "a") %!error ... %! LognormalDistribution(1, [1, 2]) %!error ... %! LognormalDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (LognormalDistribution, 2, "uper") %!error ... %! cdf (LognormalDistribution, 2, 3) ## 'paramci' method %!shared x %! randn ("seed", 1); %! x = lognrnd (1, 1, [1, 100]); %!error ... %! paramci (LognormalDistribution.fit (x), "alpha") %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", 0) %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", 1) %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", "") %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (LognormalDistribution.fit (x), "parameter", "mu", "alpha", {0.05}) %!error ... %! paramci (LognormalDistribution.fit (x), "parameter", {"mu", "sigma", "parm"}) %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (LognormalDistribution.fit (x), "parameter", "param") %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (LognormalDistribution.fit (x), "NAME", "value") %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (LognormalDistribution.fit (x), "alpha", 0.01, "parameter", "mu", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (LognormalDistribution, "Parent") %!error ... %! plot (LognormalDistribution, "PlotType", 12) %!error ... %! plot (LognormalDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (LognormalDistribution, "PlotType", "pdfcdf") %!error ... %! plot (LognormalDistribution, "Discrete", "pdfcdf") %!error ... %! plot (LognormalDistribution, "Discrete", [1, 0]) %!error ... %! plot (LognormalDistribution, "Discrete", {true}) %!error ... %! plot (LognormalDistribution, "Parent", 12) %!error ... %! plot (LognormalDistribution, "Parent", "hax") %!error ... %! plot (LognormalDistribution, "invalidNAME", "pdf") %!error ... %! plot (LognormalDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (LognormalDistribution, 2) %!error ... %! proflik (LognormalDistribution.fit (x), 3) %!error ... %! proflik (LognormalDistribution.fit (x), [1, 2]) %!error ... %! proflik (LognormalDistribution.fit (x), {1}) %!error ... %! proflik (LognormalDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (LognormalDistribution.fit (x), 1, "Display") %!error ... %! proflik (LognormalDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (LognormalDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (LognormalDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (LognormalDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (LognormalDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (LognormalDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (LognormalDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (LognormalDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (LognormalDistribution) %!error ... %! truncate (LognormalDistribution, 2) %!error ... %! truncate (LognormalDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = LognormalDistribution(1, 1); %! pd(2) = LognormalDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/LoguniformDistribution.m000066400000000000000000000536771475240274700250410ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef LoguniformDistribution ## -*- texinfo -*- ## @deftypefn {statistics} LoguniformDistribution ## ## Log-uniform probability distribution object. ## ## A @code{LoguniformDistribution} object consists of parameters, a model ## description, and sample data for a log-uniform probability distribution. ## ## The log-uniform distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{Lower} @tab Lower limit @tab @math{0 < Lower < Upper} ## @item @qcode{Upper} @tab Upper limit @tab @math{Lower < Upper < Inf} ## @end multitable ## ## There are several ways to create a @code{LoguniformDistribution} object. ## ## @itemize ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{LoguniformDistribution (@var{Lower})} ## to create a log-uniform distribution with specified parameter values. ## @end itemize ## ## It is highly recommended to use the @code{makedist} function to create ## probability distribution objects, instead of the constructor. ## ## A @code{LoguniformDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{Truncation} @tab @qcode{IsTruncated} ## @end multitable ## ## A @code{LoguniformDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{pdf}, @code{plot}, @code{random}, @code{std}, @code{truncate}, ## @code{var}. ## ## Further information about the log-uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Reciprocal_distribution} ## ## @seealso{fitdist, makedist} ## @end deftypefn properties (Dependent = true) Lower Upper endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "LoguniformDistribution"; DistributionCode = "logu"; NumParameters = 2; ParameterNames = {"Lower", "Upper"}; ParameterDescription = {"Outcome probabilities"}; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues Truncation IsTruncated endproperties methods (Hidden) function this = LoguniformDistribution (Lower, Upper) if (nargin == 0) Lower = 1; Upper = 4; endif checkparams (Lower, Upper); this.IsTruncated = false; this.ParameterValues = [Lower, Upper]; endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "loguniform distribution"); endfunction function disp (this) __disp__ (this, "loguniform distribution"); endfunction function this = set.Lower (this, Lower) checkparams (Lower, this.Upper); this.ParameterValues(1) = Lower; endfunction function Lower = get.Lower (this) Lower = this.ParameterValues(1); endfunction function this = set.Upper (this, Upper) checkparams (this.Lower, Upper); this.ParameterValues(2) = Upper; endfunction function Upper = get.Upper (this) Upper = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {LoguniformDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations d = log (this.Upper / this.Lower); p = log (x / this.Lower) / d; p(xthis.Upper) = 1; if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= log (lx / this.Lower) / d; p(! (lb | ub)) /= diff (log ([lx, ux] ./ this.Lower) ./ d); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif ## Do the computations is_nan = p < 0 | p > 1 | isnan (p); is_val = p >= 0 & p <= 1; if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); d = log (this.Upper / this.Lower); lp = log (lx / this.Lower) / d; up = log (ux / this.Lower) / d; ## Adjust p values within range of p @ lower limit and p @ upper limit p = lp + (up - lp) .* p; is_nan = p < lp | p > up | isnan (p); is_val = p >= lp & p <= up; endif x = p; x(is_nan) = NaN; x(is_val) = (this.Upper .^ p(is_val)) ./ (this.Lower .^ (p(is_val) - 1)); endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = (this.Upper - this.Lower) / log (this.Upper / this.Lower); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif m = icdf (this, 0.5); endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif d = log (this.Upper / this.Lower); y = 1 ./ (x .* d); y(x < this.Lower | x > this.Upper) = 0; if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (log ([lx, ux] ./ this.Lower) ./ d); endif endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {} plot (@var{pd}) ## @deftypefnx {LoguniformDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {LoguniformDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {LoguniformDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {LoguniformDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {LoguniformDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif sz = [varargin{:}]; r = icdf (this, rand (sz)); endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); endif if (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; endfunction ## -*- texinfo -*- ## @deftypefn {LoguniformDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else a = this.Lower; b = this.Upper; l = log (b / a); v = (b ^ 2 - a ^2) / (2 * l) - ((b - a) / l) ^ 2; endif endfunction endmethods endclassdef function checkparams (Lower, Upper) if (! (isscalar (Lower) && isnumeric (Lower) && isreal (Lower) && isfinite (Lower) && Lower > 0)) error ("LoguniformDistribution: LOWER must be a positive real scalar.") endif if (! (isscalar (Upper) && isnumeric (Upper) && isreal (Upper) && isfinite (Upper))) error ("LoguniformDistribution: UPPER must be a real scalar.") endif if (! (Lower < Upper)) error ("LoguniformDistribution: LOWER must be less than UPPER.") endif endfunction ## Test output %!shared pd, t %! pd = LoguniformDistribution (1, 4); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0, 1, 2, 3, 4, 5]), [0, 0, 0.5, 0.7925, 1, 1], 1e-4); %!assert (cdf (t, [0, 1, 2, 3, 4, 5]), [0, 0, 0, 0.5850, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.2925, 0.5, 0.7925, 1], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.5850, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [1, 1.3195, 1.7411, 2.2974, 3.0314, 4], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.2974, 2.6390, 3.0314, 3.4822, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 1.7411, 2.2974, 3.0314, 4, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.6390, 3.0314, 3.4822, 4, NaN], 1e-4); %!assert (iqr (pd), 1.4142, 1e-4); %!assert (iqr (t), 0.9852, 1e-4); %!assert (mean (pd), 2.1640, 1e-4); %!assert (mean (t), 2.8854, 1e-4); %!assert (median (pd), 2); %!assert (median (t), 2.8284, 1e-4); %!assert (pdf (pd, [0, 1, 2, 3, 4, 5]), [0, 0.7213, 0.3607, 0.2404, 0.1803, 0], 1e-4); %!assert (pdf (t, [0, 1, 2, 3, 4, 5]), [0, 0, 0.7213, 0.4809, 0.3607, 0], 1e-4); %!assert (pdf (pd, [-1, 1, 2, 3, 4, NaN]), [0, 0.7213, 0.3607, 0.2404, 0.1803, NaN], 1e-4); %!assert (pdf (t, [-1, 1, 2, 3, 4, NaN]), [0, 0, 0.7213, 0.4809, 0.3607, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (pd, 1000, 1) < 1), false); %!assert (any (random (pd, 1000, 1) > 4), false); %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 0.8527, 1e-4); %!assert (std (t), 0.5751, 1e-4); %!assert (var (pd), 0.7270, 1e-4); %!assert (var (t), 0.3307, 1e-4); ## Test input validation ## 'LoguniformDistribution' constructor %!error ... %! LoguniformDistribution (i, 1) %!error ... %! LoguniformDistribution (Inf, 1) %!error ... %! LoguniformDistribution ([1, 2], 1) %!error ... %! LoguniformDistribution ("a", 1) %!error ... %! LoguniformDistribution (NaN, 1) %!error ... %! LoguniformDistribution (1, i) %!error ... %! LoguniformDistribution (1, Inf) %!error ... %! LoguniformDistribution (1, [1, 2]) %!error ... %! LoguniformDistribution (1, "a") %!error ... %! LoguniformDistribution (1, NaN) %!error ... %! LoguniformDistribution (2, 1) ## 'cdf' method %!error ... %! cdf (LoguniformDistribution, 2, "uper") %!error ... %! cdf (LoguniformDistribution, 2, 3) ## 'plot' method %!error ... %! plot (LoguniformDistribution, "Parent") %!error ... %! plot (LoguniformDistribution, "PlotType", 12) %!error ... %! plot (LoguniformDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (LoguniformDistribution, "PlotType", "pdfcdf") %!error ... %! plot (LoguniformDistribution, "Discrete", "pdfcdf") %!error ... %! plot (LoguniformDistribution, "Discrete", [1, 0]) %!error ... %! plot (LoguniformDistribution, "Discrete", {true}) %!error ... %! plot (LoguniformDistribution, "Parent", 12) %!error ... %! plot (LoguniformDistribution, "Parent", "hax") %!error ... %! plot (LoguniformDistribution, "invalidNAME", "pdf") %!error ... %! plot (LoguniformDistribution, "PlotType", "probability") ## 'truncate' method %!error ... %! truncate (LoguniformDistribution) %!error ... %! truncate (LoguniformDistribution, 2) %!error ... %! truncate (LoguniformDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = LoguniformDistribution(1, 4); %! pd(2) = LoguniformDistribution(2, 5); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error pdf (pd, 1) %!error plot (pd) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/MultinomialDistribution.m000066400000000000000000000562731475240274700252050ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef MultinomialDistribution ## -*- texinfo -*- ## @deftypefn {statistics} MultinomialDistribution ## ## Multinomial probability distribution object. ## ## A @code{MultinomialDistribution} object consists of parameters, a model ## description, and sample data for a multinomial probability distribution. ## ## The multinomial distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{Probabilities} @tab Outcome probabilities @tab ## @math{0 <= Probabilities(i) <= 1; sum_i (Probabilities) = 1} ## @end multitable ## ## There are several ways to create a @code{MultinomialDistribution} object. ## ## @itemize ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{MultinomialDistribution (@var{Probabilities})} ## to create a multinomial distribution with specified parameter values. ## @end itemize ## ## It is highly recommended to use the @code{makedist} function to create ## probability distribution objects, instead of the constructor. ## ## A @code{MultinomialDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{Truncation} @tab @qcode{IsTruncated} ## @end multitable ## ## A @code{MultinomialDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{pdf}, @code{plot}, @code{random}, @code{std}, @code{truncate}, ## @code{var}. ## ## Further information about the multinomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Multinomial_distribution} ## ## @seealso{fitdist, makedist, mnpdf, mnrnd} ## @end deftypefn properties (Dependent = true) Probabilities endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "MultinomialDistribution"; DistributionCode = "mn"; NumParameters = 1; ParameterNames = {"Probabilities"}; ParameterDescription = {"Outcome probabilities"}; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues Truncation IsTruncated endproperties methods (Hidden) function this = MultinomialDistribution (Probabilities) if (nargin == 0) Probabilities = [0.5, 0.5]; endif checkparams (Probabilities); this.IsTruncated = false; this.ParameterValues = Probabilities; endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "multinomial distribution"); endfunction function disp (this) __disp__ (this, "multinomial distribution"); endfunction function this = set.Probabilities (this, Probabilities) checkparams (Probabilities); this.ParameterValues(1) = Probabilities; endfunction function Probabilities = get.Probabilities (this) Probabilities = this.ParameterValues(:)'; endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {MultinomialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Check input data if (! isreal (x)) error ("cdf: X must be real."); endif probs = this.Probabilities; ## Check for truncation and normalize truncated probabilities vector if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); probs = this.Probabilities([lx:ux]); probs = probs .* (1 / sum (probs)); x = x - lx + 1; endif ## Do the computations is_nan = isnan (x); sz = size (x); xf = floor (x); pk = length (probs); ## Create cumulative probability vector pc = cumsum (probs); pc(end) = 1; # Force last element to 1 xf(xf > pk) = pk; xf(xf < 1) = 1; xf(is_nan) = 1; p = pc(xf); p(x < 1) = 0; p(is_nan) = NaN; p = reshape (p, sz); ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif probs = this.Probabilities; ## Do the computations sz = size(p); p = p(:); x = zeros (numel (p), 1); pc = cumsum(this.Probabilities); pc = [0 pc(1:(end-1))]; is_one = p == 0; is_nan = isnan (p) | p > 1 | p < 0; for i = 1:length (pc) x(p > pc(i)) = i; endfor x(is_one) = 1; x(is_nan) = NaN; ## Check for truncation and clip edges if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); lp = pc(lx); up = pc(ux); lb = p >= 0 & p <= lp; ub = p <= 1 & p >= up; x(lb) = lx; x(ub) = ux; endif x = reshape (x, sz); endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif probs = this.Probabilities; if (this.IsTruncated) x = 1:numel (probs); w = x >= this.Truncation(1) & x <= this.Truncation(2); x = x(w); y = pdf (this, x); m = sum (y .* x); else m = sum (this.Probabilities .* (1:numel (probs))); endif endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif m = icdf (this, 0.5); endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif probs = this.Probabilities; size_x = size (x); is_nan = isnan (x); if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); is_out = x < lx | x > ux | (x-floor (x)) > 0 | is_nan; tprobs = probs([lx:ux]); tprobs = tprobs .* (1 / sum (tprobs)); probs([lx:ux]) = tprobs; copy_x = x; copy_x(is_out) = 1; y = probs(copy_x); y(is_out) = 0; y(is_nan) = NaN; y = reshape (y, size_x); return else is_out = x < 1 | x > length (probs) | (x-floor (x)) > 0 | is_nan; copy_x = x; copy_x(is_out) = 1; y = probs(copy_x); y(is_out) = 0; y(is_nan) = NaN; y = reshape (y, size_x); endif endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {} plot (@var{pd}) ## @deftypefnx {MultinomialDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {MultinomialDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, true, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {MultinomialDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {MultinomialDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {MultinomialDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif sz = [varargin{:}]; ps = prod (sz); if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); tprobs = pdf (this, [1:numel(this.Probabilities)]); tprobs = tprobs([lx:ux]); cp = cumsum (tprobs); else cp = cumsum (this.Probabilities); endif bins = min ([0, cp], 1); bins(end) = 1; [~, r] = histc (rand (ps, 1), bins); r = reshape (r, sz); if (this.IsTruncated) r = r + lx - 1; endif endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: is_nan input argument."); endif ## Constrain within the length of Probabilities vector lower = round (lower); upper = round (upper); k = numel (this.Probabilities); lower(lower < 1) = 1; upper(upper > k) = k; if (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; endfunction ## -*- texinfo -*- ## @deftypefn {MultinomialDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif probs = this.Probabilities; if (this.IsTruncated) x = 1:numel (probs); w = x >= this.Truncation(1) & x <= this.Truncation(2); x = x(w); y = pdf (this, x); m = sum (y .* x); v = sum (y .* (x - m) .^ 2); else v = sum (probs .* (1:numel (probs)) .^ 2) - mean (this) ^ 2; endif endfunction endmethods endclassdef function checkparams (Probabilities) if (! (isvector (Probabilities) && isnumeric (Probabilities) && isreal (Probabilities) && isfinite (Probabilities) && abs (sum (Probabilities) - 1) < eps * 100)) error (["MultinomialDistribution: PROBABILITIES must be a vector", ... " of positive real scalars that sum up to 1."]) endif endfunction ## Test output %!shared pd, t %! pd = MultinomialDistribution ([0.1, 0.2, 0.3, 0.2, 0.1, 0.1]); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [2, 3, 4]), [0.3, 0.6, 0.8], eps); %!assert (cdf (t, [2, 3, 4]), [0.2857, 0.7143, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.1, 0.3, 0.6, 0.8], eps); %!assert (cdf (pd, [1.5, 2-eps, 3, 4]), [0.1, 0.1, 0.6, 0.8], eps); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0.2857, 0.7143, 1], 1e-4); %!assert (cdf (t, [1.5, 2-eps, 3, 4]), [0, 0, 0.7143, 1], 1e-4); %!assert (cdf (pd, [1, 2.5, 4, 6]), [0.1, 0.3, 0.8, 1], eps); %!assert (icdf (pd, [0, 0.2857, 0.7143, 1]), [1, 2, 4, 6]); %!assert (icdf (t, [0, 0.2857, 0.7143, 1]), [2, 2, 4, 4]); %!assert (icdf (t, [0, 0.35, 0.7143, 1]), [2, 3, 4, 4]); %!assert (icdf (t, [0, 0.35, 0.7143, 1, NaN]), [2, 3, 4, 4, NaN]); %!assert (icdf (t, [-0.5, 0, 0.35, 0.7143, 1, NaN]), [NaN, 2, 3, 4, 4, NaN]); %!assert (icdf (pd, [-0.5, 0, 0.35, 0.7143, 1, NaN]), [NaN, 1, 3, 4, 6, NaN]); %!assert (iqr (pd), 2); %!assert (iqr (t), 2); %!assert (mean (pd), 3.3, 1e-14); %!assert (mean (t), 3, eps); %!assert (median (pd), 3); %!assert (median (t), 3); %!assert (pdf (pd, [-5, 1, 2.5, 4, 6, NaN, 9]), [0, 0.1, 0, 0.2, 0.1, NaN, 0]); %!assert (pdf (pd, [-5, 1, 2, 3, 4, 6, NaN, 9]), ... %! [0, 0.1, 0.2, 0.3, 0.2, 0.1, NaN, 0]); %!assert (pdf (t, [-5, 1, 2, 3, 4, 6, NaN, 0]), ... %! [0, 0, 0.2857, 0.4286, 0.2857, 0, NaN, 0], 1e-4); %!assert (pdf (t, [-5, 1, 2, 4, 6, NaN, 0]), ... %! [0, 0, 0.2857, 0.2857, 0, NaN, 0], 1e-4); %!assert (unique (random (pd, 1000, 5)), [1, 2, 3, 4, 5, 6]'); %!assert (unique (random (t, 1000, 5)), [2, 3, 4]'); %!assert (std (pd), 1.4177, 1e-4); %!assert (std (t), 0.7559, 1e-4); %!assert (var (pd), 2.0100, 1e-4); %!assert (var (t), 0.5714, 1e-4); ## Test input validation ## 'MultinomialDistribution' constructor %!error ... %! MultinomialDistribution(0) %!error ... %! MultinomialDistribution(-1) %!error ... %! MultinomialDistribution(Inf) %!error ... %! MultinomialDistribution(i) %!error ... %! MultinomialDistribution("a") %!error ... %! MultinomialDistribution([1, 2]) %!error ... %! MultinomialDistribution(NaN) ## 'cdf' method %!error ... %! cdf (MultinomialDistribution, 2, "uper") %!error ... %! cdf (MultinomialDistribution, 2, 3) %!error ... %! cdf (MultinomialDistribution, i) ## 'plot' method %!error ... %! plot (MultinomialDistribution, "Parent") %!error ... %! plot (MultinomialDistribution, "PlotType", 12) %!error ... %! plot (MultinomialDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (MultinomialDistribution, "PlotType", "pdfcdf") %!error ... %! plot (MultinomialDistribution, "Discrete", "pdfcdf") %!error ... %! plot (MultinomialDistribution, "Discrete", [1, 0]) %!error ... %! plot (MultinomialDistribution, "Discrete", {true}) %!error ... %! plot (MultinomialDistribution, "Parent", 12) %!error ... %! plot (MultinomialDistribution, "Parent", "hax") %!error ... %! plot (MultinomialDistribution, "invalidNAME", "pdf") %!error ... %! plot (MultinomialDistribution, "PlotType", "probability") ## 'truncate' method %!error ... %! truncate (MultinomialDistribution) %!error ... %! truncate (MultinomialDistribution, 2) %!error ... %! truncate (MultinomialDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = MultinomialDistribution([0.1, 0.2, 0.3, 0.4]); %! pd(2) = MultinomialDistribution([0.1, 0.2, 0.3, 0.4]); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error pdf (pd, 1) %!error plot (pd) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/NakagamiDistribution.m000066400000000000000000001034221475240274700244100ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef NakagamiDistribution ## -*- texinfo -*- ## @deftypefn {statistics} NakagamiDistribution ## ## Normal probability distribution object. ## ## A @code{NakagamiDistribution} object consists of parameters, a model ## description, and sample data for a normal probability distribution. ## ## The normal distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Number of successes @tab @math{mu > 0} ## @item @qcode{omega} @tab Probability of success @tab @math{omega > 0} ## @end multitable ## ## There are several ways to create a @code{NakagamiDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{NakagamiDistribution (@var{mu}, @var{omega})} ## to create a normal distribution with specified parameter values. ## @item Use the static method @qcode{NakagamiDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{NakagamiDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{NakagamiDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Nakagami_distribution} ## ## @seealso{fitdist, makedist, nakacdf, nakainv, nakapdf, nakarnd, nakafit, ## nakalike, nakastat} ## @end deftypefn properties (Dependent = true) mu omega endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "NakagamiDistribution"; DistributionCode = "naka"; NumParameters = 2; ParameterNames = {"mu", "omega"}; ParameterDescription = {"Shape parameter", "Spread parameter"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [0.5, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = NakagamiDistribution (mu, omega) if (nargin == 0) mu = 1; omega = 1; endif checkparams (mu, omega); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, omega]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "normal distribution"); endfunction function disp (this) __disp__ (this, "normal distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.omega); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.omega (this, omega) checkparams (this.mu, omega); this.InputData = []; this.ParameterValues(2) = omega; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function omega = get.omega (this) omega = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {NakagamiDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = nakacdf (x, this.mu, this.omega); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= nakacdf (lx, this.mu, this.omega); p(! (lb | ub)) /= diff (nakacdf ([lx, ux], this.mu, this.omega)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = nakacdf (this.Truncation(1), this.mu, this.omega); up = nakacdf (this.Truncation(2), this.mu, this.omega); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = nakainv (np, this.mu, this.omega); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = nakainv (p, this.mu, this.omega); endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = nakastat (this.mu, this.omega); endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = nakacdf ([lx, ux], this.mu, this.omega); m = nakainv (sum (Fa_b) / 2, this.mu, this.omega); else m = nakainv (0.5, this.mu, this.omega); endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - nakalike ([this.mu, this.omega], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {NakagamiDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = nakapdf (x, this.mu, this.omega); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (nakacdf ([lx, ux], this.mu, this.omega)); endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {} plot (@var{pd}) ## @deftypefnx {NakagamiDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {NakagamiDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {NakagamiDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {NakagamiDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {NakagamiDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the negative binomial distribution, @qcode{@var{pnum} = 1} selects ## the parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{omega}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {NakagamiDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {NakagamiDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {NakagamiDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (nakacdf ([lx, ux], this.mu, this.omega)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = nakarnd (this.mu, this.omega, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = nakarnd (this.mu, this.omega, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); endif ## Check boundaries and constrain within support: Natural numbers lower = round (lower); upper = round (upper); lower(lower < 0) = 0; if (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {NakagamiDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = nakastat (this.mu, this.omega); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = nakafit (x, alpha, censor, freq, options); [~, acov] = nakalike (phat, x, censor, freq); ## Create fitted distribution object pd = NakagamiDistribution.makeFitted (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); omega = phat(2); pd = NakagamiDistribution (mu, omega); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, omega) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu) && mu >= 0.5)) error ("NakagamiDistribution: MU must be a real scalar of at least 0.5.") endif if (! (isscalar (omega) && isnumeric (omega) && isreal (omega) && isfinite (omega) && omega > 0)) error ("NakagamiDistribution: OMEGA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = NakagamiDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.6321, 0.9817, 0.9999, 1, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.9933, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.8946, 0.9817, 0.9999, 1], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0, 0.9933, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.4724, 0.7147, 0.9572, 1.2686, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.0550, 2.1239, 2.2173, 2.3684, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.7147, 0.9572, 1.2686, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.1239, 2.2173, 2.3684, 4, NaN], 1e-4); %!assert (iqr (pd), 0.6411, 1e-4); %!assert (iqr (t), 0.2502, 1e-4); %!assert (mean (pd), 0.8862, 1e-4); %!assert (mean (t), 2.2263, 1e-4); %!assert (median (pd), 0.8326, 1e-4); %!assert (median (t), 2.1664, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.7358, 0.0733, 0.0007, 0, 0], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 4, 0.0404, 0, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.7358, 0.0733, 0.0007, 0, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 4, 0.0404, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 0.4633, 1e-4); %!assert (std (t), 0.2083, 1e-4); %!assert (var (pd), 0.2146, 1e-4); %!assert (var (t), 0.0434, 1e-4); ## Test input validation ## 'NakagamiDistribution' constructor %!error ... %! NakagamiDistribution(Inf, 1) %!error ... %! NakagamiDistribution(i, 1) %!error ... %! NakagamiDistribution("a", 1) %!error ... %! NakagamiDistribution([1, 2], 1) %!error ... %! NakagamiDistribution(NaN, 1) %!error ... %! NakagamiDistribution(1, 0) %!error ... %! NakagamiDistribution(1, -1) %!error ... %! NakagamiDistribution(1, Inf) %!error ... %! NakagamiDistribution(1, i) %!error ... %! NakagamiDistribution(1, "a") %!error ... %! NakagamiDistribution(1, [1, 2]) %!error ... %! NakagamiDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (NakagamiDistribution, 2, "uper") %!error ... %! cdf (NakagamiDistribution, 2, 3) ## 'paramci' method %!shared x %! x = nakarnd (1, 0.5, [1, 100]); %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha") %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", 0) %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", 1) %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", "") %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (NakagamiDistribution.fit (x), "parameter", "mu", "alpha", {0.05}) %!error ... %! paramci (NakagamiDistribution.fit (x), "parameter", {"mu", "omega", "param"}) %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "omega", "param"}) %!error ... %! paramci (NakagamiDistribution.fit (x), "parameter", "param") %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (NakagamiDistribution.fit (x), "NAME", "value") %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (NakagamiDistribution.fit (x), "alpha", 0.01, "parameter", "mu", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (NakagamiDistribution, "Parent") %!error ... %! plot (NakagamiDistribution, "PlotType", 12) %!error ... %! plot (NakagamiDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (NakagamiDistribution, "PlotType", "pdfcdf") %!error ... %! plot (NakagamiDistribution, "Discrete", "pdfcdf") %!error ... %! plot (NakagamiDistribution, "Discrete", [1, 0]) %!error ... %! plot (NakagamiDistribution, "Discrete", {true}) %!error ... %! plot (NakagamiDistribution, "Parent", 12) %!error ... %! plot (NakagamiDistribution, "Parent", "hax") %!error ... %! plot (NakagamiDistribution, "invalidNAME", "pdf") %!error ... %! plot (NakagamiDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (NakagamiDistribution, 2) %!error ... %! proflik (NakagamiDistribution.fit (x), 3) %!error ... %! proflik (NakagamiDistribution.fit (x), [1, 2]) %!error ... %! proflik (NakagamiDistribution.fit (x), {1}) %!error ... %! proflik (NakagamiDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "Display") %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (NakagamiDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (NakagamiDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (NakagamiDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (NakagamiDistribution) %!error ... %! truncate (NakagamiDistribution, 2) %!error ... %! truncate (NakagamiDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = NakagamiDistribution(1, 0.5); %! pd(2) = NakagamiDistribution(1, 0.6); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/NegativeBinomialDistribution.m000066400000000000000000001074541475240274700261260ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef NegativeBinomialDistribution ## -*- texinfo -*- ## @deftypefn {statistics} NegativeBinomialDistribution ## ## Negative binomial probability distribution object. ## ## A @code{NegativeBinomialDistribution} object consists of parameters, a ## model description, and sample data for a negative binomial probability ## distribution. ## ## The negative binomial distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{R} @tab Number of successes @tab @math{R > 0} ## @item @qcode{P} @tab Probability of success @tab @math{0 < P <= 1} ## @end multitable ## ## There are several ways to create a @code{NegativeBinomialDistribution} ## object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{NegativeBinomialDistribution (@var{R}, ## @var{P})} to create a negative binomial distribution with specified ## parameter values. ## @item Use the static method @qcode{NegativeBinomialDistribution.fit ## (@var{x}, @var{censor}, @var{freq}, @var{options})} to a distribution to ## data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{NegativeBinomialDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{NegativeBinomialDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the negative binomial distribution can be found ## at @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{fitdist, makedist, nbincdf, nbininv, nbinpdf, nbinrnd, nbinfit, ## nbinlike, nbinstat} ## @end deftypefn properties (Dependent = true) R P endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "NegativeBinomialDistribution"; DistributionCode = "nbin"; NumParameters = 2; ParameterNames = {"R", "P"}; ParameterDescription = {"Number of successes", "Probability of success"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, 1]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = NegativeBinomialDistribution (R, P) if (nargin == 0) R = 1; P = 0.5; endif checkparams (R, P); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [R, P]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "negative binomial distribution"); endfunction function disp (this) __disp__ (this, "negative binomial distribution"); endfunction function this = set.R (this, R) checkparams (R, this.P); this.InputData = []; this.ParameterValues(1) = R; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function R = get.R (this) R = this.ParameterValues(1); endfunction function this = set.P (this, P) checkparams (this.R, P); this.InputData = []; this.ParameterValues(2) = P; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function P = get.P (this) P = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {NegativeBinomialDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = nbincdf (x, this.R, this.P); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= nbincdf (lx - 1, this.R, this.P); p(! (lb | ub)) /= diff (nbincdf ([lx-1, ux], this.R, this.P)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif umax = nbininv (1, this.R, this.P); if (this.IsTruncated) ## Find out of range p values is_nan = p < 0 | p > 1; ## Get lower and upper boundaries lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, umax); lp = nbincdf (lx - 1, this.R, this.P); up = nbincdf (ux, this.R, this.P); p = lp + p * (up - lp); p(is_nan) = NaN; endif x = nbininv (p, this.R, this.P); if (this.IsTruncated) x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif [um, uv] = nbinstat (this.R, this.P); if (this.IsTruncated) lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, nbininv (1, this.R, this.P)); ## Handle infinite support on the right if (isequal (ux, Inf)) ratio = 1 / diff (nbincdf ([lx-1, ux], this.R, this.P)); x = 0:lx-1; m = ratio * (um - sum (x .* nbinpdf (x, this.R, this.P))); else x = lx:ux; m = sum (x .* pdf (this, x)); endif else m = um; endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = nbincdf ([lx, ux], this.R, this.P); m = nbininv (sum (Fa_b) / 2, this.R, this.P); else m = nbininv (0.5, this.R, this.P); endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - nbinlike ([this.R, this.P], this.InputData.data); endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {NegativeBinomialDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = nbinpdf (x, this.R, this.P); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (nbincdf ([lx-1, ux], this.R, this.P)); endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {} plot (@var{pd}) ## @deftypefnx {NegativeBinomialDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {NegativeBinomialDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, true, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {NegativeBinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {NegativeBinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {NegativeBinomialDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the negative binomial distribution, @qcode{@var{pnum} = 1} selects ## the parameter @qcode{R} and @qcode{@var{pnum} = 2} selects the parameter ## @var{P}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {NegativeBinomialDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {NegativeBinomialDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {NegativeBinomialDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (nbincdf ([lx-1, ux], this.R, this.P)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = nbinrnd (this.R, this.P, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = nbinrnd (this.R, this.P, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); endif ## Check boundaries and constrain within support: Natural numbers lower = round (lower); upper = round (upper); lower(lower < 0) = 0; if (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {NegativeBinomialDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) ## Calculate untruncated mean and variance [um, uv] = nbinstat (this.R, this.P); ## Calculate truncated mean m = mean (this); ## Get lower and upper boundaries lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, nbininv (1, this.R, this.P)); ## Handle infinite support on the right if (isequal (ux, Inf)) ratio = 1 / diff (nbincdf ([lx-1, ux], this.R, this.P)); x = 0:lx-1; v = ratio * (uv + (um - m) ^ 2 - sum (((x - m) .^ 2) .* ... nbinpdf (x, this.R, this.P))); else x = lx:ux; v = sum (((x - m) .^ 2) .* pdf (this, x)); endif else [~, v] = nbinstat (this.R, this.P); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) freq = []; else freq = varargin{2}; endif if (nargin < 4) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{3}; endif ## Fit data [phat, pci] = nbinfit (x, alpha, freq, options); [~, acov] = nbinlike (phat, x, freq); ## Create fitted distribution object pd = NegativeBinomialDistribution.makeFitted (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) R = phat(1); P = phat(2); pd = NegativeBinomialDistribution (R, P); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (R, P) if (! (isscalar (R) && isnumeric (R) && isreal (R) && isfinite (R) && R > 0)) error ("NegativeBinomialDistribution: R must be a positive scalar.") endif if (! (isscalar (P) && isnumeric (P) && isreal (P) && isfinite (P) && P > 0 && P <= 1)) error (strcat (["NegativeBinomialDistribution: P must be a real"], ... [" scalar bounded in the range (0, 1]."])) endif endfunction ## Test output %!shared pd, t, t_inf %! pd = NegativeBinomialDistribution (5, 0.5); %! t = truncate (pd, 2, 4); %! t_inf = truncate (pd, 2, Inf); %!assert (cdf (pd, [0:5]), [0.0312, 0.1094, 0.2266, 0.3633, 0.5, 0.6230], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0.3, 0.65, 1, 1], 1e-4); %!assert (cdf (t_inf, [0:5]), [0, 0, 0.1316, 0.2851, 0.4386, 0.5768], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.1094, 0.2266, 0.3633, 0.5000], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0.3, 0.65, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 2, 4, 5, 7, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2, 3, 3, 4, 4], 1e-4); %!assert (icdf (t_inf, [0:0.2:1]), [2, 3, 4, 6, 8, Inf], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 4, 5, 7, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 3, 3, 4, 4, NaN], 1e-4); %!assert (iqr (pd), 4); %!assert (iqr (t), 2); %!assert (mean (pd), 5); %!assert (mean (t), 3.0500, 1e-4); %!assert (mean (t_inf), 5.5263, 1e-4); %!assert (median (pd), 4); %!assert (median (t), 3); %!assert (pdf (pd, [0:5]), [0.0312, 0.0781, 0.1172, 0.1367, 0.1367, 0.1230], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.3, 0.35, 0.35, 0], 1e-4); %!assert (pdf (t_inf, [0:5]), [0, 0, 0.1316, 0.1535, 0.1535, 0.1382], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.0781, 0.1172, 0.1367, 0.1367, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 0.3, 0.35, 0.35, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 3.1623, 1e-4); %!assert (std (t), 0.8047, 1e-4); %!assert (std (t_inf), 2.9445, 1e-4); %!assert (var (pd), 10); %!assert (var (t), 0.6475, 1e-4); %!assert (var (t_inf), 8.6704, 1e-4); ## Test input validation ## 'NegativeBinomialDistribution' constructor %!error ... %! NegativeBinomialDistribution(Inf, 1) %!error ... %! NegativeBinomialDistribution(i, 1) %!error ... %! NegativeBinomialDistribution("a", 1) %!error ... %! NegativeBinomialDistribution([1, 2], 1) %!error ... %! NegativeBinomialDistribution(NaN, 1) %!error ... %! NegativeBinomialDistribution(1, 0) %!error ... %! NegativeBinomialDistribution(1, -1) %!error ... %! NegativeBinomialDistribution(1, Inf) %!error ... %! NegativeBinomialDistribution(1, i) %!error ... %! NegativeBinomialDistribution(1, "a") %!error ... %! NegativeBinomialDistribution(1, [1, 2]) %!error ... %! NegativeBinomialDistribution(1, NaN) %!error ... %! NegativeBinomialDistribution(1, 1.2) ## 'cdf' method %!error ... %! cdf (NegativeBinomialDistribution, 2, "uper") %!error ... %! cdf (NegativeBinomialDistribution, 2, 3) ## 'paramci' method %!shared x %! x = nbinrnd (1, 0.5, [1, 100]); %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha") %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", 0) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", 1) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", "") %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "parameter", "R", ... %! "alpha", {0.05}) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), ... %! "parameter", {"R", "P", "param"}) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"R", "P", "param"}) %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "parameter", "param") %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "NAME", "value") %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", 0.01, ... %! "NAME", "value") %!error ... %! paramci (NegativeBinomialDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "R", "NAME", "value") ## 'plot' method %!error ... %! plot (NegativeBinomialDistribution, "Parent") %!error ... %! plot (NegativeBinomialDistribution, "PlotType", 12) %!error ... %! plot (NegativeBinomialDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (NegativeBinomialDistribution, "PlotType", "pdfcdf") %!error ... %! plot (NegativeBinomialDistribution, "Discrete", "pdfcdf") %!error ... %! plot (NegativeBinomialDistribution, "Discrete", [1, 0]) %!error ... %! plot (NegativeBinomialDistribution, "Discrete", {true}) %!error ... %! plot (NegativeBinomialDistribution, "Parent", 12) %!error ... %! plot (NegativeBinomialDistribution, "Parent", "hax") %!error ... %! plot (NegativeBinomialDistribution, "invalidNAME", "pdf") %!error ... %! plot (NegativeBinomialDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (NegativeBinomialDistribution, 2) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 3) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), [1, 2]) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), {1}) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "Display") %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (NegativeBinomialDistribution.fit (x), 1, {[1 2 3]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (NegativeBinomialDistribution) %!error ... %! truncate (NegativeBinomialDistribution, 2) %!error ... %! truncate (NegativeBinomialDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = NegativeBinomialDistribution(1, 0.5); %! pd(2) = NegativeBinomialDistribution(1, 0.6); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/NormalDistribution.m000066400000000000000000001016631475240274700241350ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef NormalDistribution ## -*- texinfo -*- ## @deftypefn {statistics} NormalDistribution ## ## Normal probability distribution object. ## ## A @code{NormalDistribution} object consists of parameters, a model ## description, and sample data for a normal probability distribution. ## ## The normal distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Mean @tab @math{-Inf < mu < Inf} ## @item @qcode{sigma} @tab Standard deviation @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{NormalDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{NormalDistribution (@var{mu}, @var{sigma})} ## to create a normal distribution with specified parameter values. ## @item Use the static method @qcode{NormalDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{NormalDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{NormalDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{fitdist, makedist, normcdf, norminv, normpdf, normrnd, normfit, ## normlike, normstat} ## @end deftypefn properties (Dependent = true) mu sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "NormalDistribution"; DistributionCode = "norm"; NumParameters = 2; ParameterNames = {"mu", "sigma"}; ParameterDescription = {"Mean", "Standard Deviation"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = NormalDistribution (mu, sigma) if (nargin == 0) mu = 0; sigma = 1; endif checkparams (mu, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "normal distribution"); endfunction function disp (this) __disp__ (this, "normal distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {NormalDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = normcdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= normcdf (lx, this.mu, this.sigma); p(! (lb | ub)) /= diff (normcdf ([lx, ux], this.mu, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = normcdf (this.Truncation(1), this.mu, this.sigma); up = normcdf (this.Truncation(2), this.mu, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = norminv (np, this.mu, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = norminv (p, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = normstat (this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = normcdf ([lx, ux], this.mu, this.sigma); m = norminv (sum (Fa_b) / 2, this.mu, this.sigma); else m = norminv (0.5, this.mu, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - normlike ([this.mu, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {NormalDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = normpdf (x, this.mu, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (normcdf ([lx, ux], this.mu, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {} plot (@var{pd}) ## @deftypefnx {NormalDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {NormalDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {NormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {NormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {NormalDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the normal distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {NormalDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {NormalDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {NormalDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = normcdf (this.Truncation(1), this.mu, this.sigma); up = normcdf (this.Truncation(2), this.mu, this.sigma); u = unifrnd (lp, up, varargin{:}); r = this.mu + this.sigma .* (-sqrt (2) * erfcinv (2 * u)); else r = normrnd (this.mu, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {NormalDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = normstat (this.mu, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [muhat, sigmahat, muci, sigmaci] = normfit ... (x, alpha, censor, freq, options); phat = [muhat, sigmahat]; pci = [muci(:), sigmaci(:)]; [~, acov] = normlike (phat, x, censor, freq); ## Create fitted distribution object pd = NormalDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); sigma = phat(2); pd = NormalDistribution (mu, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) error ("NormalDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("NormalDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = NormalDistribution; %! t = truncate (pd, -2, 2); %!assert (cdf (pd, [0:5]), [0.5, 0.8413, 0.9772, 0.9987, 1, 1], 1e-4); %!assert (cdf (t, [0:5]), [0.5, 0.8576, 1, 1, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.9332, 0.9772, 0.9987, 1], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0.9538, 1, 1, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [-Inf, -0.8416, -0.2533, 0.2533, 0.8416, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [-2, -0.7938, -0.2416, 0.2416, 0.7938, 2], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, -0.2533, 0.2533, 0.8416, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, -0.2416, 0.2416, 0.7938, 2, NaN], 1e-4); %!assert (iqr (pd), 1.3490, 1e-4); %!assert (iqr (t), 1.2782, 1e-4); %!assert (mean (pd), 0); %!assert (mean (t), 0, 3e-16); %!assert (median (pd), 0); %!assert (median (t), 0, 3e-16); %!assert (pdf (pd, [0:5]), [0.3989, 0.2420, 0.0540, 0.0044, 0.0001, 0], 1e-4); %!assert (pdf (t, [0:5]), [0.4180, 0.2535, 0.0566, 0, 0, 0], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0.2420, 0.2420, 0.0540, 0.0044, 0.0001, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0.2535, 0.2535, 0.0566, 0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < -2), false); %!assert (any (random (t, 1000, 1) > 2), false); %!assert (std (pd), 1); %!assert (std (t), 0.8796, 1e-4); %!assert (var (pd), 1); %!assert (var (t), 0.7737, 1e-4); ## Test input validation ## 'NormalDistribution' constructor %!error ... %! NormalDistribution(Inf, 1) %!error ... %! NormalDistribution(i, 1) %!error ... %! NormalDistribution("a", 1) %!error ... %! NormalDistribution([1, 2], 1) %!error ... %! NormalDistribution(NaN, 1) %!error ... %! NormalDistribution(1, 0) %!error ... %! NormalDistribution(1, -1) %!error ... %! NormalDistribution(1, Inf) %!error ... %! NormalDistribution(1, i) %!error ... %! NormalDistribution(1, "a") %!error ... %! NormalDistribution(1, [1, 2]) %!error ... %! NormalDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (NormalDistribution, 2, "uper") %!error ... %! cdf (NormalDistribution, 2, 3) ## 'paramci' method %!shared x %! x = normrnd (1, 1, [1, 100]); %!error ... %! paramci (NormalDistribution.fit (x), "alpha") %!error ... %! paramci (NormalDistribution.fit (x), "alpha", 0) %!error ... %! paramci (NormalDistribution.fit (x), "alpha", 1) %!error ... %! paramci (NormalDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (NormalDistribution.fit (x), "alpha", "") %!error ... %! paramci (NormalDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (NormalDistribution.fit (x), "parameter", "mu", "alpha", {0.05}) %!error ... %! paramci (NormalDistribution.fit (x), "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (NormalDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "param"}) %!error ... %! paramci (NormalDistribution.fit (x), "parameter", "param") %!error ... %! paramci (NormalDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (NormalDistribution.fit (x), "NAME", "value") %!error ... %! paramci (NormalDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (NormalDistribution.fit (x), "alpha", 0.01, "parameter", "mu", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (NormalDistribution, "Parent") %!error ... %! plot (NormalDistribution, "PlotType", 12) %!error ... %! plot (NormalDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (NormalDistribution, "PlotType", "pdfcdf") %!error ... %! plot (NormalDistribution, "Discrete", "pdfcdf") %!error ... %! plot (NormalDistribution, "Discrete", [1, 0]) %!error ... %! plot (NormalDistribution, "Discrete", {true}) %!error ... %! plot (NormalDistribution, "Parent", 12) %!error ... %! plot (NormalDistribution, "Parent", "hax") %!error ... %! plot (NormalDistribution, "invalidNAME", "pdf") %!error ... %! plot (NormalDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (NormalDistribution, 2) %!error ... %! proflik (NormalDistribution.fit (x), 3) %!error ... %! proflik (NormalDistribution.fit (x), [1, 2]) %!error ... %! proflik (NormalDistribution.fit (x), {1}) %!error ... %! proflik (NormalDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (NormalDistribution.fit (x), 1, "Display") %!error ... %! proflik (NormalDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (NormalDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (NormalDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (NormalDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (NormalDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (NormalDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (NormalDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (NormalDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (NormalDistribution) %!error ... %! truncate (NormalDistribution, 2) %!error ... %! truncate (NormalDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = NormalDistribution(1, 1); %! pd(2) = NormalDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/PiecewiseLinearDistribution.m000066400000000000000000000562321475240274700257560ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef PiecewiseLinearDistribution ## -*- texinfo -*- ## @deftypefn {statistics} PiecewiseLinearDistribution ## ## Piecewise linear probability distribution object. ## ## A @code{PiecewiseLinearDistribution} object consists of parameters, a model ## description, and sample data for a uniform probability distribution. ## ## The piecewise linear distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{x} @tab Vector of @math{x} values at which the cdf changes ## slope @tab @math{-Inf < x < Fx} ## @item @qcode{Fx} @tab Vector of CDF values that correspond to each value in ## @math{x} @tab @math{0 <= Fx <= 1} ## @end multitable ## ## There are several ways to create a @code{PiecewiseLinearDistribution} ## object. ## ## @itemize ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{PiecewiseLinearDistribution (@var{x}, ## @var{Fx})} to create a uniform distribution with specified parameter ## values. ## @end itemize ## ## It is highly recommended to use @code{makedist} function to create ## probability distribution objects, instead of the constructor. ## ## A @code{PiecewiseLinearDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{Truncation} @tab @qcode{IsTruncated} ## @end multitable ## ## A @code{PiecewiseLinearDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{pdf}, @code{plot}, @code{random}, @code{std}, @code{truncate}, ## @code{var}. ## ## Further information about the piecewise linear distribution can be found at ## @url{https://en.wikipedia.org/wiki/Piecewise_linear_function} ## ## @seealso{makedist, plcdf, plinv, plpdf, plrnd, plstat} ## @end deftypefn properties (Dependent = true) x Fx endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "PiecewiseLinearDistribution"; DistributionCode = "pl"; NumParameters = 2; ParameterNames = {"x", "Fx"}; ParameterDescription = {"x", "cdf = F(x)"}; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues Truncation IsTruncated endproperties methods (Hidden) function this = PiecewiseLinearDistribution (x, Fx) if (nargin == 0) x = [0; 1]; Fx = [0; 1]; else x = x(:); Fx = Fx(:); endif checkparams (x, Fx); this.IsTruncated = false; this.ParameterValues = [x, Fx]; endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Piecewise Linear distribution"); endfunction function disp (this) __disp__ (this, "Piecewise Linear distribution"); endfunction function this = set.x (this, x) checkparams (x, this.Fx); this.ParameterValues(:,1) = x; endfunction function x = get.x (this) x = this.ParameterValues(:,1); endfunction function this = set.Fx (this, Fx) checkparams (this.x, Fx); this.ParameterValues(:,2) = Fx; endfunction function Fx = get.Fx (this) Fx = this.ParameterValues(:,2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {PiecewiseLinearDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = plcdf (x, this.x, this.Fx); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= plcdf (lx, this.x, this.Fx); p(! (lb | ub)) /= diff (plcdf ([lx, ux], this.x, this.Fx)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = plcdf (this.Truncation(1), this.x, this.Fx); up = plcdf (this.Truncation(2), this.x, this.Fx); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = plinv (np, this.x, this.Fx); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = plinv (p, this.x, this.Fx); endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2), ... "ArrayValued", 1); else m = plstat (this.x, this.Fx); endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = plcdf ([lx, ux], this.x, this.Fx); m = plinv (sum (Fa_b) / 2, this.x, this.Fx); else m = plinv (0.5, this.x, this.Fx); endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = plpdf (x, this.x, this.Fx); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (plcdf ([lx, ux], this.x, this.Fx)); endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {} plot (@var{pd}) ## @deftypefnx {PiecewiseLinearDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {PiecewiseLinearDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {PiecewiseLinearDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {PiecewiseLinearDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {PiecewiseLinearDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) x = this.x(:)'; Fx = this.Fx(:)'; lp = plcdf (this.Truncation(1), x, Fx); up = plcdf (this.Truncation(2), x, Fx); u = unifrnd (lp, up, varargin{:}); r = zeros (size (u)); [~, bin] = histc (u(:)', Fx); r0 = x(bin); dx = diff (x); dF = diff (Fx); dr = (u(:)' - Fx(bin)) .* dx(bin) ./ dF(bin); r(:) = r0 + dr; else r = plrnd (this.x, this.Fx, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; endfunction ## -*- texinfo -*- ## @deftypefn {PiecewiseLinearDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2), ... "ArrayValued", 1); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2), ... "ArrayValued", 1); else [~, v] = plstat (this.x, this.Fx); endif endfunction endmethods endclassdef function checkparams (x, Fx) if (! (isvector (x) && isnumeric (x) && isreal (x) && isfinite (x))) error ("PiecewiseLinearDistribution: X must be a real vector.") endif if (! (isvector (Fx) && isnumeric (Fx) && isreal (Fx) && isfinite (Fx))) error ("PiecewiseLinearDistribution: Fx must be a real vector.") endif if (! isvector (x) || ! isvector (Fx) || ! isequal (size (x), size (Fx))) error (strcat (["PiecewiseLinearDistribution: X and FX must"], ... [" be vectors of equal size."])); endif if (length (x) < 2 || length (Fx) < 2) error (strcat (["PiecewiseLinearDistribution: X and FX must"], ... [" be at least two-elements long."])); endif if (any (Fx < 0) || any (Fx > 1)) error (strcat (["PiecewiseLinearDistribution: FX must be"], ... [" bounded in the range [0, 1]."])); endif endfunction ## Test output %!shared pd, t %! load patients %! [f, x] = ecdf (Weight); %! f = f(1:5:end); %! x = x(1:5:end); %! pd = PiecewiseLinearDistribution (x, f); %! t = truncate (pd, 130, 180); %!assert (cdf (pd, [120, 130, 140, 150, 200]), [0.0767, 0.25, 0.4629, 0.5190, 0.9908], 1e-4); %!assert (cdf (t, [120, 130, 140, 150, 200]), [0, 0, 0.4274, 0.5403, 1], 1e-4); %!assert (cdf (pd, [100, 250, NaN]), [0, 1, NaN], 1e-4); %!assert (cdf (t, [115, 290, NaN]), [0, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [111, 127.5, 136.62, 169.67, 182.17, 202], 1e-2); %!assert (icdf (t, [0:0.2:1]), [130, 134.15, 139.26, 162.5, 173.99, 180], 1e-2); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NA, 136.62, 169.67, 182.17, 202, NA], 1e-2); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NA, 139.26, 162.5, 173.99, 180, NA], 1e-2); %!assert (iqr (pd), 50.0833, 1e-4); %!assert (iqr (t), 36.8077, 1e-4); %!assert (mean (pd), 153.61, 1e-10); %!assert (mean (t), 152.311, 1e-3); %!assert (median (pd), 142, 1e-10); %!assert (median (t), 141.9462, 1e-4); %!assert (pdf (pd, [120, 130, 140, 150, 200]), [0.0133, 0.0240, 0.0186, 0.0024, 0.0046], 1e-4); %!assert (pdf (t, [120, 130, 140, 150, 200]), [0, 0.0482, 0.0373, 0.0048, 0], 1e-4); %!assert (pdf (pd, [100, 250, NaN]), [0, 0, NaN], 1e-4); %!assert (pdf (t, [100, 250, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 130), false); %!assert (any (random (t, 1000, 1) > 180), false); %!assert (std (pd), 26.5196, 1e-4); %!assert (std (t), 18.2941, 1e-4); %!assert (var (pd), 703.2879, 1e-4); %!assert (var (t), 334.6757, 1e-4); ## Test input validation ## 'PiecewiseLinearDistribution' constructor %!error ... %! PiecewiseLinearDistribution ([0, i], [0, 1]) %!error ... %! PiecewiseLinearDistribution ([0, Inf], [0, 1]) %!error ... %! PiecewiseLinearDistribution (["a", "c"], [0, 1]) %!error ... %! PiecewiseLinearDistribution ([NaN, 1], [0, 1]) %!error ... %! PiecewiseLinearDistribution ([0, 1], [0, i]) %!error ... %! PiecewiseLinearDistribution ([0, 1], [0, Inf]) %!error ... %! PiecewiseLinearDistribution ([0, 1], ["a", "c"]) %!error ... %! PiecewiseLinearDistribution ([0, 1], [NaN, 1]) %!error ... %! PiecewiseLinearDistribution ([0, 1], [0, 0.5, 1]) %!error ... %! PiecewiseLinearDistribution ([0], [1]) %!error ... %! PiecewiseLinearDistribution ([0, 0.5, 1], [0, 1, 1.5]) ## 'cdf' method %!error ... %! cdf (PiecewiseLinearDistribution, 2, "uper") %!error ... %! cdf (PiecewiseLinearDistribution, 2, 3) ## 'plot' method %!error ... %! plot (PiecewiseLinearDistribution, "Parent") %!error ... %! plot (PiecewiseLinearDistribution, "PlotType", 12) %!error ... %! plot (PiecewiseLinearDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (PiecewiseLinearDistribution, "PlotType", "pdfcdf") %!error ... %! plot (PiecewiseLinearDistribution, "Discrete", "pdfcdf") %!error ... %! plot (PiecewiseLinearDistribution, "Discrete", [1, 0]) %!error ... %! plot (PiecewiseLinearDistribution, "Discrete", {true}) %!error ... %! plot (PiecewiseLinearDistribution, "Parent", 12) %!error ... %! plot (PiecewiseLinearDistribution, "Parent", "hax") %!error ... %! plot (PiecewiseLinearDistribution, "invalidNAME", "pdf") %!error ... %! plot (PiecewiseLinearDistribution, "PlotType", "probability") ## 'truncate' method %!error ... %! truncate (PiecewiseLinearDistribution) %!error ... %! truncate (PiecewiseLinearDistribution, 2) %!error ... %! truncate (PiecewiseLinearDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = PiecewiseLinearDistribution (); %! pd(2) = PiecewiseLinearDistribution (); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error pdf (pd, 1) %!error plot (pd) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/PoissonDistribution.m000066400000000000000000001021451475240274700243330ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef PoissonDistribution ## -*- texinfo -*- ## @deftypefn {statistics} PoissonDistribution ## ## Poisson probability distribution object. ## ## A @code{PoissonDistribution} object consists of parameters, a model ## description, and sample data for a Poisson probability distribution. ## ## The Poisson distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{lambda} @tab Rate parameter @tab @math{lambda > 0} ## @end multitable ## ## There are several ways to create a @code{PoissonDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{PoissonDistribution (@var{lambda})} ## to create a Poisson distribution with specified parameter values. ## @item Use the static method @qcode{PoissonDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{PoissonDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{PoissonDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{fitdist, makedist, poisscdf, poissinv, poisspdf, poissrnd, ## poissfit, poisslike, poisstat} ## @end deftypefn properties (Dependent = true) lambda endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "PoissonDistribution"; DistributionCode = "poiss"; NumParameters = 1; ParameterNames = {"lambda"}; ParameterDescription = {"Rate"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin; Inf]; ParameterLogCI = true; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = PoissonDistribution (lambda) if (nargin == 0) lambda = 1; endif checkparams (lambda); this.InputData = []; this.IsTruncated = false; this.ParameterValues = lambda; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Poisson distribution"); endfunction function disp (this) __disp__ (this, "Poisson distribution"); endfunction function this = set.lambda (this, lambda) checkparams (lambda); this.InputData = []; this.ParameterValues(1) = lambda; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction function lambda = get.lambda (this) lambda = this.ParameterValues(1); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {PoissonDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = poisscdf (x, this.lambda); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= poisscdf (lx - 1, this.lambda); p(! (lb | ub)) /= diff (poisscdf ([lx-1, ux], this.lambda)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif umax = poissinv (1, this.lambda); if (this.IsTruncated) ## Find out of range p values is_nan = p < 0 | p > 1; ## Get lower and upper boundaries lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, umax); lp = poisscdf (lx - 1, this.lambda); up = poisscdf (ux, this.lambda); p = lp + p * (up - lp); p(is_nan) = NaN; endif x = poissinv (p, this.lambda); if (this.IsTruncated) x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif [um, uv] = poisstat (this.lambda); if (this.IsTruncated) lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, poissinv (1, this.lambda)); ## Handle infinite support on the right if (isequal (ux, Inf)) ratio = 1 / diff (poisscdf ([lx-1, ux], this.lambda)); x = 0:lx-1; m = ratio * (um - sum (x .* poisspdf (x, this.lambda))); else x = lx:ux; m = sum (x .* pdf (this, x)); endif else m = um; endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = poisscdf ([lx-1, ux], this.lambda); m = poissinv (sum (Fa_b) / 2, this.lambda); else m = poissinv (0.5, this.lambda); endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - poisslike (this.lambda, this.InputData.data, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {PoissonDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = poisspdf (x, this.lambda); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (poisscdf ([lx-1, ux], this.lambda)); endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {} plot (@var{pd}) ## @deftypefnx {PoissonDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {PoissonDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, true, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {PoissonDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {PoissonDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {PoissonDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the Poisson distribution, @qcode{@var{pnum} = 1} selects the ## parameter @var{lambda}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {PoissonDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {PoissonDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {PoissonDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (poisscdf ([lx, ux], this.lambda)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = poissrnd (this.lambda, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = poissrnd (this.lambda, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); endif ## Check boundaries and constrain within support: Natural numbers lower = round (lower); upper = round (upper); lower(lower < 0) = 0; if (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {PoissonDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) ## Calculate untruncated mean and variance [um, uv] = poisstat (this.lambda); ## Calculate truncated mean m = mean (this); ## Get lower and upper boundaries lx = ceil (this.Truncation(1)); ux = floor (this.Truncation(2)); ux = min (ux, poissinv (1, this.lambda)); ## Handle infinite support on the right if (isequal (ux, Inf)) ratio = 1 / diff (poisscdf ([lx-1, ux], this.lambda)); x = 0:lx-1; v = ratio * (uv + (um - m) ^ 2 - sum (((x - m) .^ 2) .* ... poisspdf (x, this.lambda))); else x = lx:ux; v = sum (((x - m) .^ 2) .* pdf (this, x)); endif else [~, v] = poisstat (this.lambda); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) freq = []; else freq = varargin{2}; endif ## Fit data [phat, pci] = poissfit (x, alpha, freq); [~, acov] = poisslike (phat, x, freq); ## Create fitted distribution object pd = PoissonDistribution.makeFitted ... (phat, pci, acov, x, freq); endfunction function pd = makeFitted (phat, pci, acov, x, freq) lambda = phat(1); pd = PoissonDistribution (lambda); pd.ParameterCI = pci; pd.ParameterIsFixed = false; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", [], "freq", freq); endfunction endmethods endclassdef function checkparams (lambda) if (! (isscalar (lambda) && isnumeric (lambda) && isreal (lambda) && isfinite (lambda) && lambda > 0)) error ("PoissonDistribution: LAMBDA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t, t_inf %! pd = PoissonDistribution; %! t = truncate (pd, 2, 4); %! t_inf = truncate (pd, 2, Inf); %!assert (cdf (pd, [0:5]), [0.3679, 0.7358, 0.9197, 0.9810, 0.9963, 0.9994], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0.7059, 0.9412, 1, 1], 1e-4); %!assert (cdf (t_inf, [0:5]), [0, 0, 0.6961, 0.9281, 0.9861, 0.9978], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4]), [0.7358, 0.9197, 0.9810, 0.9963], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4]), [0, 0.7059, 0.9412, 1], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0, 1, 1, 2, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2, 2, 2, 3, 4], 1e-4); %!assert (icdf (t_inf, [0:0.2:1]), [2, 2, 2, 2, 3, Inf], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 1, 1, 2, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2, 2, 3, 4, NaN], 1e-4); %!assert (iqr (pd), 2); %!assert (iqr (t), 1); %!assert (mean (pd), 1); %!assert (mean (t), 2.3529, 1e-4); %!assert (mean (t_inf), 2.3922, 1e-4); %!assert (median (pd), 1); %!assert (median (t), 2); %!assert (median (t_inf), 2); %!assert (pdf (pd, [0:5]), [0.3679, 0.3679, 0.1839, 0.0613, 0.0153, 0.0031], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.7059, 0.2353, 0.0588, 0], 1e-4); %!assert (pdf (t_inf, [0:5]), [0, 0, 0.6961, 0.2320, 0.0580, 0.0116], 1e-4); %!assert (pdf (pd, [-1, 1:4, NaN]), [0, 0.3679, 0.1839, 0.0613, 0.0153, NaN], 1e-4); %!assert (pdf (t, [-1, 1:4, NaN]), [0, 0, 0.7059, 0.2353, 0.0588, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1); %!assert (std (t), 0.5882, 1e-4); %!assert (std (t_inf), 0.6738, 1e-4); %!assert (var (pd), 1); %!assert (var (t), 0.3460, 1e-4); %!assert (var (t_inf), 0.4540, 1e-4); ## Test input validation ## 'PoissonDistribution' constructor %!error ... %! PoissonDistribution(0) %!error ... %! PoissonDistribution(-1) %!error ... %! PoissonDistribution(Inf) %!error ... %! PoissonDistribution(i) %!error ... %! PoissonDistribution("a") %!error ... %! PoissonDistribution([1, 2]) %!error ... %! PoissonDistribution(NaN) ## 'cdf' method %!error ... %! cdf (PoissonDistribution, 2, "uper") %!error ... %! cdf (PoissonDistribution, 2, 3) ## 'paramci' method %!shared x %! x = poissrnd (1, [1, 100]); %!error ... %! paramci (PoissonDistribution.fit (x), "alpha") %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", 0) %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", 1) %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", "") %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (PoissonDistribution.fit (x), "parameter", "lambda", "alpha", {0.05}) %!error ... %! paramci (PoissonDistribution.fit (x), "parameter", {"lambda", "param"}) %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"lambda", "param"}) %!error ... %! paramci (PoissonDistribution.fit (x), "parameter", "param") %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (PoissonDistribution.fit (x), "NAME", "value") %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (PoissonDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "lambda", "NAME", "value") ## 'plot' method %!error ... %! plot (PoissonDistribution, "Parent") %!error ... %! plot (PoissonDistribution, "PlotType", 12) %!error ... %! plot (PoissonDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (PoissonDistribution, "PlotType", "pdfcdf") %!error ... %! plot (PoissonDistribution, "Discrete", "pdfcdf") %!error ... %! plot (PoissonDistribution, "Discrete", [1, 0]) %!error ... %! plot (PoissonDistribution, "Discrete", {true}) %!error ... %! plot (PoissonDistribution, "Parent", 12) %!error ... %! plot (PoissonDistribution, "Parent", "hax") %!error ... %! plot (PoissonDistribution, "invalidNAME", "pdf") %!error ... %! plot (PoissonDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (PoissonDistribution, 2) %!error ... %! proflik (PoissonDistribution.fit (x), 3) %!error ... %! proflik (PoissonDistribution.fit (x), [1, 2]) %!error ... %! proflik (PoissonDistribution.fit (x), {1}) %!error ... %! proflik (PoissonDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (PoissonDistribution.fit (x), 1, "Display") %!error ... %! proflik (PoissonDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (PoissonDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (PoissonDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (PoissonDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (PoissonDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (PoissonDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (PoissonDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (PoissonDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (PoissonDistribution) %!error ... %! truncate (PoissonDistribution, 2) %!error ... %! truncate (PoissonDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = PoissonDistribution(1); %! pd(2) = PoissonDistribution(3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/RayleighDistribution.m000066400000000000000000000767731475240274700244660ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef RayleighDistribution ## -*- texinfo -*- ## @deftypefn {statistics} RayleighDistribution ## ## Rayleigh probability distribution object. ## ## A @code{RayleighDistribution} object consists of parameters, a model ## description, and sample data for a Rayleigh probability distribution. ## ## The Rayleigh distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{sigma} @tab Scale parameter @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{RayleighDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{RayleighDistribution (@var{sigma})} ## to create a Rayleigh distribution with specified parameter values. ## @item Use the static method @qcode{RayleighDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{RayleighDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{RayleighDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{fitdist, makedist, raylcdf, raylinv, raylpdf, raylrnd, raylfit, ## rayllike, raylstat} ## @end deftypefn properties (Dependent = true) sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "RayleighDistribution"; DistributionCode = "rayl"; NumParameters = 1; ParameterNames = {"sigma"}; ParameterDescription = {"Scale"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin; Inf]; ParameterLogCI = true; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = RayleighDistribution (sigma) if (nargin == 0) sigma = 1; endif checkparams (sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = sigma; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Rayleigh distribution"); endfunction function disp (this) __disp__ (this, "Rayleigh distribution"); endfunction function this = set.sigma (this, sigma) checkparams (sigma); this.InputData = []; this.ParameterValues(1) = sigma; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(1); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {RayleighDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = raylcdf (x, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= raylcdf (lx, this.sigma); p(! (lb | ub)) /= diff (raylcdf ([lx, ux], this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = raylcdf (this.Truncation(1), this.sigma); up = raylcdf (this.Truncation(2), this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = raylinv (np, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = raylinv (p, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = raylstat (this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = raylcdf ([lx, ux], this.sigma); m = raylinv (sum (Fa_b) / 2, this.sigma); else m = this.sigma .* sqrt (2 * log (2)); endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - rayllike (this.sigma, this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {RayleighDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = raylpdf (x, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (raylcdf ([lx, ux], this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {} plot (@var{pd}) ## @deftypefnx {RayleighDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {RayleighDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {RayleighDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {RayleighDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {RayleighDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the Rayleigh distribution, @qcode{@var{pnum} = 1} selects the ## parameter @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {RayleighDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {RayleighDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {RayleighDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = raylcdf (this.Truncation(1), this.sigma); up = raylcdf (this.Truncation(2), this.sigma); u = unifrnd (lp, up, varargin{:}); r = sqrt (-2 .* log (1 - u) .* this.sigma .^ 2); else r = raylrnd (this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif ## Check boundaries and constrain within support [0, Inf) lower(lower < 0) = 0; this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = true; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {RayleighDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = raylstat (this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif ## Fit data [phat, pci] = raylfit (x, alpha, censor, freq); [~, acov] = rayllike (phat, x, censor, freq); ## Create fitted distribution object pd = RayleighDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) sigma = phat(1); pd = RayleighDistribution (sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = false; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (sigma) if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("RayleighDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = RayleighDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.3935, 0.8647, 0.9889, 0.9997, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.9202, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.6753, 0.8647, 0.9889, 0.9997, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.9202, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.6680, 1.0108, 1.3537, 1.7941, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1083, 2.2402, 2.4135, 2.6831, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 1.0108, 1.3537, 1.7941, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.2402, 2.4135, 2.6831, 4, NaN], 1e-4); %!assert (iqr (pd), 0.9066, 1e-4); %!assert (iqr (t), 0.4609, 1e-4); %!assert (mean (pd), 1.2533, 1e-4); %!assert (mean (t), 2.4169, 1e-4); %!assert (median (pd), 1.1774, 1e-4); %!assert (median (t), 2.3198, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.6065, 0.2707, 0.0333, 0.0013, 0], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 2.0050, 0.2469, 0.0099, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0.4870, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 0.6551, 1e-4); %!assert (std (t), 0.3591, 1e-4); %!assert (var (pd), 0.4292, 1e-4); %!assert (var (t), 0.1290, 1e-4); ## Test input validation ## 'RayleighDistribution' constructor %!error ... %! RayleighDistribution(0) %!error ... %! RayleighDistribution(-1) %!error ... %! RayleighDistribution(Inf) %!error ... %! RayleighDistribution(i) %!error ... %! RayleighDistribution("a") %!error ... %! RayleighDistribution([1, 2]) %!error ... %! RayleighDistribution(NaN) ## 'cdf' method %!error ... %! cdf (RayleighDistribution, 2, "uper") %!error ... %! cdf (RayleighDistribution, 2, 3) ## 'paramci' method %!shared x %! x = raylrnd (1, [1, 100]); %!error ... %! paramci (RayleighDistribution.fit (x), "alpha") %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", 0) %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", 1) %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", "") %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (RayleighDistribution.fit (x), "parameter", "sigma", "alpha", {0.05}) %!error ... %! paramci (RayleighDistribution.fit (x), "parameter", {"sigma", "param"}) %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"sigma", "param"}) %!error ... %! paramci (RayleighDistribution.fit (x), "parameter", "param") %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (RayleighDistribution.fit (x), "NAME", "value") %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (RayleighDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "sigma", "NAME", "value") ## 'plot' method %!error ... %! plot (RayleighDistribution, "Parent") %!error ... %! plot (RayleighDistribution, "PlotType", 12) %!error ... %! plot (RayleighDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (RayleighDistribution, "PlotType", "pdfcdf") %!error ... %! plot (RayleighDistribution, "Discrete", "pdfcdf") %!error ... %! plot (RayleighDistribution, "Discrete", [1, 0]) %!error ... %! plot (RayleighDistribution, "Discrete", {true}) %!error ... %! plot (RayleighDistribution, "Parent", 12) %!error ... %! plot (RayleighDistribution, "Parent", "hax") %!error ... %! plot (RayleighDistribution, "invalidNAME", "pdf") %!error ... %! plot (RayleighDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (RayleighDistribution, 2) %!error ... %! proflik (RayleighDistribution.fit (x), 3) %!error ... %! proflik (RayleighDistribution.fit (x), [1, 2]) %!error ... %! proflik (RayleighDistribution.fit (x), {1}) %!error ... %! proflik (RayleighDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (RayleighDistribution.fit (x), 1, "Display") %!error ... %! proflik (RayleighDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (RayleighDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (RayleighDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (RayleighDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (RayleighDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (RayleighDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (RayleighDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (RayleighDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (RayleighDistribution) %!error ... %! truncate (RayleighDistribution, 2) %!error ... %! truncate (RayleighDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = RayleighDistribution(1); %! pd(2) = RayleighDistribution(3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/RicianDistribution.m000066400000000000000000001030441475240274700241050ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef RicianDistribution ## -*- texinfo -*- ## @deftypefn {statistics} RicianDistribution ## ## Rician probability distribution object. ## ## A @code{RicianDistribution} object consists of parameters, a model ## description, and sample data for a Rician probability distribution. ## ## The Rician distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{s} @tab Noncentrality parameter @tab @math{s >= 0} ## @item @qcode{sigma} @tab Scale parameter @tab @math{sigma > 0} ## @end multitable ## ## There are several ways to create a @code{RicianDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{RicianDistribution (@var{s}, @var{sigma})} ## to create a Rician distribution with specified parameter values. ## @item Use the static method @qcode{RicianDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{RicianDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{RicianDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{fitdist, makedist, ricecdf, riceinv, ricepdf, ricernd, ricefit, ## ricelike, ricestat} ## @end deftypefn properties (Dependent = true) s sigma endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "RicianDistribution"; DistributionCode = "rice"; NumParameters = 2; ParameterNames = {"s", "sigma"}; ParameterDescription = {"Non-centrality Distance", "Scale"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [0, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = RicianDistribution (s, sigma) if (nargin == 0) s = 1; sigma = 1; endif checkparams (s, sigma); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [s, sigma]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Rician distribution"); endfunction function disp (this) __disp__ (this, "Rician distribution"); endfunction function this = set.s (this, s) checkparams (s, this.sigma); this.InputData = []; this.ParameterValues(1) = s; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function s = get.s (this) s = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.s, sigma); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {RicianDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = ricecdf (x, this.s, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= ricecdf (lx, this.s, this.sigma); p(! (lb | ub)) /= diff (ricecdf ([lx, ux], this.s, this.sigma)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = ricecdf (this.Truncation(1), this.s, this.sigma); up = ricecdf (this.Truncation(2), this.s, this.sigma); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = riceinv (np, this.s, this.sigma); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = riceinv (p, this.s, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = ricestat (this.s, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = ricecdf ([lx, ux], this.s, this.sigma); m = riceinv (sum (Fa_b) / 2, this.s, this.sigma); else m = riceinv (0.5, this.s, this.sigma); endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - ricelike ([this.s, this.sigma], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {RicianDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = ricepdf (x, this.s, this.sigma); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (ricecdf ([lx, ux], this.s, this.sigma)); endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {} plot (@var{pd}) ## @deftypefnx {RicianDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {RicianDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {RicianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {RicianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {RicianDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the Rician distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{s} and @qcode{@var{pnum} = 2} selects the parameter ## @var{sigma}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {RicianDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {RicianDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {RicianDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (ricecdf ([lx, ux], this.s, this.sigma)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = ricernd (this.s, this.sigma, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = ricernd (this.s, this.sigma, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {RicianDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = ricestat (this.s, this.sigma); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = ricefit (x, alpha, censor, freq, options); [~, acov] = ricelike (phat, x, censor, freq); ## Create fitted distribution object pd = RicianDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) s = phat(1); sigma = phat(2); pd = RicianDistribution (s, sigma); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (s, sigma) if (! (isscalar (s) && isnumeric (s) && isreal (s) && isfinite (s) && s >= 0 )) error ("RicianDistribution: NU must be a non-negative real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("RicianDistribution: SIGMA must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = RicianDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.2671, 0.7310, 0.9563, 0.9971, 0.9999], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.8466, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.5120, 0.7310, 0.9563, 0.9971, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.8466, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.8501, 1.2736, 1.6863, 2.2011, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1517, 2.3296, 2.5545, 2.8868, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 1.2736, 1.6863, 2.2011, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.3296, 2.5545, 2.8868, 4, NaN], 1e-4); %!assert (iqr (pd), 1.0890, 1e-4); %!assert (iqr (t), 0.5928, 1e-4); %!assert (mean (pd), 1.5486, 1e-4); %!assert (mean (t), 2.5380, 1e-4); %!assert (median (pd), 1.4755, 1e-4); %!assert (median (t), 2.4341, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.4658, 0.3742, 0.0987, 0.0092, 0.0003], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.4063, 0.3707, 0.0346, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0.4864, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 0.7758, 1e-4); %!assert (std (t), 0.4294, 1e-4); %!assert (var (pd), 0.6019, 1e-4); %!assert (var (t), 0.1844, 1e-4); ## Test input validation ## 'RicianDistribution' constructor %!error ... %! RicianDistribution(-eps, 1) %!error ... %! RicianDistribution(-1, 1) %!error ... %! RicianDistribution(Inf, 1) %!error ... %! RicianDistribution(i, 1) %!error ... %! RicianDistribution("a", 1) %!error ... %! RicianDistribution([1, 2], 1) %!error ... %! RicianDistribution(NaN, 1) %!error ... %! RicianDistribution(1, 0) %!error ... %! RicianDistribution(1, -1) %!error ... %! RicianDistribution(1, Inf) %!error ... %! RicianDistribution(1, i) %!error ... %! RicianDistribution(1, "a") %!error ... %! RicianDistribution(1, [1, 2]) %!error ... %! RicianDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (RicianDistribution, 2, "uper") %!error ... %! cdf (RicianDistribution, 2, 3) ## 'paramci' method %!shared x %! x = gevrnd (1, 1, 1, [1, 100]); %!error ... %! paramci (RicianDistribution.fit (x), "alpha") %!error ... %! paramci (RicianDistribution.fit (x), "alpha", 0) %!error ... %! paramci (RicianDistribution.fit (x), "alpha", 1) %!error ... %! paramci (RicianDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (RicianDistribution.fit (x), "alpha", "") %!error ... %! paramci (RicianDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (RicianDistribution.fit (x), "parameter", "s", "alpha", {0.05}) %!error ... %! paramci (RicianDistribution.fit (x), "parameter", {"s", "sigma", "param"}) %!error ... %! paramci (RicianDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"s", "sigma", "param"}) %!error ... %! paramci (RicianDistribution.fit (x), "parameter", "param") %!error ... %! paramci (RicianDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (RicianDistribution.fit (x), "NAME", "value") %!error ... %! paramci (RicianDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (RicianDistribution.fit (x), "alpha", 0.01, "parameter", "s", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (RicianDistribution, "Parent") %!error ... %! plot (RicianDistribution, "PlotType", 12) %!error ... %! plot (RicianDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (RicianDistribution, "PlotType", "pdfcdf") %!error ... %! plot (RicianDistribution, "Discrete", "pdfcdf") %!error ... %! plot (RicianDistribution, "Discrete", [1, 0]) %!error ... %! plot (RicianDistribution, "Discrete", {true}) %!error ... %! plot (RicianDistribution, "Parent", 12) %!error ... %! plot (RicianDistribution, "Parent", "hax") %!error ... %! plot (RicianDistribution, "invalidNAME", "pdf") %!error ... %! plot (RicianDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (RicianDistribution, 2) %!error ... %! proflik (RicianDistribution.fit (x), 3) %!error ... %! proflik (RicianDistribution.fit (x), [1, 2]) %!error ... %! proflik (RicianDistribution.fit (x), {1}) %!error ... %! proflik (RicianDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (RicianDistribution.fit (x), 1, "Display") %!error ... %! proflik (RicianDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (RicianDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (RicianDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (RicianDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (RicianDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (RicianDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (RicianDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (RicianDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (RicianDistribution) %!error ... %! truncate (RicianDistribution, 2) %!error ... %! truncate (RicianDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = RicianDistribution(1, 1); %! pd(2) = RicianDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/TriangularDistribution.m000066400000000000000000000574651475240274700250270ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef TriangularDistribution ## -*- texinfo -*- ## @deftypefn {statistics} TriangularDistribution ## ## Triangular probability distribution object. ## ## A @code{TriangularDistribution} object consists of parameters, a model ## description, and sample data for a triangular probability distribution. ## ## The triangular distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{A} @tab Lower limit @tab @math{-Inf < A < Inf} ## @item @qcode{B} @tab Peak location @tab @math{A <= B <= C} ## @item @qcode{C} @tab Upper limit @tab @math{C > A} ## @end multitable ## ## There are several ways to create a @code{TriangularDistribution} object. ## ## @itemize ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{TriangularDistribution (@var{A}, @var{B} ## @var{C})} to create a triangular distribution with specified parameter ## values. ## @end itemize ## ## It is highly recommended to use @code{makedist} function to create ## probability distribution objects, instead of the constructor. ## ## A @code{TriangularDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{Truncation} @tab @qcode{IsTruncated} ## @end multitable ## ## A @code{TriangularDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{pdf}, @code{plot}, @code{random}, @code{std}, @code{truncate}, ## @code{var}. ## ## Further information about the triangular distribution can be found ## at @url{https://en.wikipedia.org/wiki/Triangular_distribution} ## ## @seealso{makedist, tricdf, triinv, tripdf, trirnd, tristat} ## @end deftypefn properties (Dependent = true) A B C endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "TriangularDistribution"; DistributionCode = "tri"; NumParameters = 3; ParameterNames = {"A", "B", "C"}; ParameterDescription = {"Lower limit", "Peak location", "Upper limit"}; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues Truncation IsTruncated endproperties methods (Hidden) function this = TriangularDistribution (A, B, C) if (nargin == 0) A = 0; B = 0.5; C = 1; endif checkparams (A, B, C); this.IsTruncated = false; this.ParameterValues = [A, B, C]; endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Triangular distribution"); endfunction function disp (this) __disp__ (this, "Triangular distribution"); endfunction function this = set.A (this, A) checkparams (A, this.B, this.C); this.ParameterValues(1) = A; endfunction function A = get.A (this) A = this.ParameterValues(1); endfunction function this = set.B (this, B) checkparams (this.A, B, this.C); this.ParameterValues(2) = B; endfunction function B = get.B (this) B = this.ParameterValues(2); endfunction function this = set.C (this, C) checkparams (this.A, this.B, C); this.ParameterValues(3) = C; endfunction function C = get.C (this) C = this.ParameterValues(3); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {TriangularDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = tricdf (x, this.A, this.B, this.C); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= tricdf (lx, this.A, this.B, this.C); p(! (lb | ub)) /= diff (tricdf ([lx, ux], this.A, this.B, this.C)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = tricdf (this.Truncation(1), this.A, this.B, this.C); up = tricdf (this.Truncation(2), this.A, this.B, this.C); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = triinv (np, this.A, this.B, this.C); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = triinv (p, this.A, this.B, this.C); endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = tristat (this.A, this.B, this.C); endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = tricdf ([lx, ux], this.A, this.B, this.C); m = triinv (sum (Fa_b) / 2, this.A, this.B, this.C); else m = triinv (0.5, this.A, this.B, this.C); endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = tripdf (x, this.A, this.B, this.C); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (tricdf ([lx, ux], this.A, this.B, this.C)); endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {} plot (@var{pd}) ## @deftypefnx {TriangularDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {TriangularDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {TriangularDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {TriangularDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {TriangularDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (tricdf ([lx, ux], this.A, this.B, this.C)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = trirnd (this.A, this.B, this.C, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = trirnd (this.A, this.B, this.C, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif ## Check boundaries and constrain within support [A, C] lower(lower < this.A) = this.A; upper(upper > this.C) = this.C; this.Truncation = [lower, upper]; this.IsTruncated = true; endfunction ## -*- texinfo -*- ## @deftypefn {TriangularDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = tristat (this.A, this.B, this.C); endif endfunction endmethods endclassdef function checkparams (A, B, C) if (! (isscalar (A) && isnumeric (A) && isreal (A) && isfinite (A))) error ("TriangularDistribution: lower limit A must be a real scalar.") endif if (! (isscalar (B) && isnumeric (B) && isreal (B) && isfinite (B))) error ("TriangularDistribution: mode B must be a real scalar.") endif if (! (isscalar (C) && isnumeric (C) && isreal (C) && isfinite (C))) error ("TriangularDistribution: upper limit C must be a real scalar.") endif if (! (A < C)) error (strcat (["TriangularDistribution: lower limit A must"], ... [" be less than upper limit C."])) endif if (! (A <= B && B <= C)) error (strcat (["TriangularDistribution: mode B must be within"], ... [" lower limit A and upper limit C."])) endif endfunction ## Test output %!shared pd, t %! pd = TriangularDistribution (0, 3, 5); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.0667, 0.2667, 0.6000, 0.9000, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.5263, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.1500, 0.2667, 0.6, 0.9, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.5263, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 1.7321, 2.4495, 3, 3.5858, 5], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.4290, 2.7928, 3.1203, 3.4945, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4495, 3, 3.5858, 5, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.7928, 3.1203, 3.4945, 4, NaN], 1e-4); %!assert (iqr (pd), 1.4824, 1e-4); %!assert (iqr (t), 0.8678, 1e-4); %!assert (mean (pd), 2.6667, 1e-4); %!assert (mean (t), 2.9649, 1e-4); %!assert (median (pd), 2.7386, 1e-4); %!assert (median (t), 2.9580, 1e-4); %!assert (pdf (pd, [0:5]), [0, 0.1333, 0.2667, 0.4, 0.2, 0], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.4211, 0.6316, 0.3158, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0.2, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.0274, 1e-4); %!assert (std (t), 0.5369, 1e-4); %!assert (var (pd), 1.0556, 1e-4); %!assert (var (t), 0.2882, 1e-4); ## Test input validation ## 'TriangularDistribution' constructor %!error ... %! TriangularDistribution (i, 1, 2) %!error ... %! TriangularDistribution (Inf, 1, 2) %!error ... %! TriangularDistribution ([1, 2], 1, 2) %!error ... %! TriangularDistribution ("a", 1, 2) %!error ... %! TriangularDistribution (NaN, 1, 2) %!error ... %! TriangularDistribution (1, i, 2) %!error ... %! TriangularDistribution (1, Inf, 2) %!error ... %! TriangularDistribution (1, [1, 2], 2) %!error ... %! TriangularDistribution (1, "a", 2) %!error ... %! TriangularDistribution (1, NaN, 2) %!error ... %! TriangularDistribution (1, 2, i) %!error ... %! TriangularDistribution (1, 2, Inf) %!error ... %! TriangularDistribution (1, 2, [1, 2]) %!error ... %! TriangularDistribution (1, 2, "a") %!error ... %! TriangularDistribution (1, 2, NaN) %!error ... %! TriangularDistribution (1, 1, 1) %!error ... %! TriangularDistribution (1, 0.5, 2) ## 'cdf' method %!error ... %! cdf (TriangularDistribution, 2, "uper") %!error ... %! cdf (TriangularDistribution, 2, 3) ## 'plot' method %!error ... %! plot (TriangularDistribution, "Parent") %!error ... %! plot (TriangularDistribution, "PlotType", 12) %!error ... %! plot (TriangularDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (TriangularDistribution, "PlotType", "pdfcdf") %!error ... %! plot (TriangularDistribution, "Discrete", "pdfcdf") %!error ... %! plot (TriangularDistribution, "Discrete", [1, 0]) %!error ... %! plot (TriangularDistribution, "Discrete", {true}) %!error ... %! plot (TriangularDistribution, "Parent", 12) %!error ... %! plot (TriangularDistribution, "Parent", "hax") %!error ... %! plot (TriangularDistribution, "invalidNAME", "pdf") %!error <'probability' PlotType is not supported for 'TriangularDistribution'.> ... %! plot (TriangularDistribution, "PlotType", "probability") ## 'truncate' method %!error ... %! truncate (TriangularDistribution) %!error ... %! truncate (TriangularDistribution, 2) %!error ... %! truncate (TriangularDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = TriangularDistribution (0, 1, 2); %! pd(2) = TriangularDistribution (0, 1, 2); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error pdf (pd, 1) %!error plot (pd) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/UniformDistribution.m000066400000000000000000000533271475240274700243270ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef UniformDistribution ## -*- texinfo -*- ## @deftypefn {statistics} UniformDistribution ## ## Continuous uniform probability distribution object. ## ## A @code{UniformDistribution} object consists of parameters, a model ## description, and sample data for a uniform probability distribution. ## ## The uniform distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{Lower} @tab Lower limit @tab @math{-Inf < Lower < Upper} ## @item @qcode{Upper} @tab Upper limit @tab @math{Lower < Upper < Inf} ## @end multitable ## ## There are several ways to create a @code{UniformDistribution} object. ## ## @itemize ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{UniformDistribution (@var{Lower}, ## @var{Upper})} to create a uniform distribution with specified parameter ## values. ## @end itemize ## ## It is highly recommended to use @code{makedist} function to create ## probability distribution objects, instead of the constructor. ## ## A @code{UniformDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{Truncation} @tab @qcode{IsTruncated} ## @end multitable ## ## A @code{UniformDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{pdf}, @code{plot}, @code{random}, @code{std}, @code{truncate}, ## @code{var}. ## ## Further information about the continuous uniform distribution can be found ## at @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{makedist, unifcdf, unifinv, unifpdf, unifrnd, unifit, unifstat} ## @end deftypefn properties (Dependent = true) Lower Upper endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = false; DistributionName = "UniformDistribution"; DistributionCode = "unif"; NumParameters = 2; ParameterNames = {"Lower", "Upper"}; ParameterDescription = {"Lower limit", "Upper limit"}; endproperties properties (GetAccess = public , SetAccess = protected) ParameterValues Truncation IsTruncated endproperties methods (Hidden) function this = UniformDistribution (Lower, Upper) if (nargin == 0) Lower = 0; Upper = 1; endif checkparams (Lower, Upper); this.IsTruncated = false; this.ParameterValues = [Lower, Upper]; endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Uniform distribution (continuous)"); endfunction function disp (this) __disp__ (this, "Uniform distribution (continuous)"); endfunction function this = set.Lower (this, Lower) checkparams (Lower, this.Upper); this.ParameterValues(1) = Lower; endfunction function Lower = get.Lower (this) Lower = this.ParameterValues(1); endfunction function this = set.Upper (this, Upper) checkparams (this.Lower, Upper); this.ParameterValues(2) = Upper; endfunction function Upper = get.Upper (this) Upper = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {UniformDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = unifcdf (x, this.Lower, this.Upper); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= unifcdf (lx, this.Lower, this.Upper); p(! (lb | ub)) /= diff (unifcdf ([lx, ux], this.Lower, this.Upper)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = unifcdf (this.Truncation(1), this.Lower, this.Upper); up = unifcdf (this.Truncation(2), this.Lower, this.Upper); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = unifinv (np, this.Lower, this.Upper); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = unifinv (p, this.Lower, this.Upper); endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = unifstat (this.Lower, this.Upper); endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = unifcdf ([lx, ux], this.Lower, this.Upper); m = unifinv (sum (Fa_b) / 2, this.Lower, this.Upper); else m = unifstat (this.Lower, this.Upper); endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = unifpdf (x, this.Lower, this.Upper); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (unifcdf ([lx, ux], this.Lower, this.Upper)); endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {} plot (@var{pd}) ## @deftypefnx {UniformDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {UniformDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {UniformDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {UniformDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {UniformDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) r = unifrnd (this.Truncation(1), this.Truncation(2), varargin{:}); else r = unifrnd (this.Lower, this.Upper, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif ## Check boundaries and constrain within support [Lower, Upper] lower(lower < this.Lower) = this.Lower; upper(upper > this.Upper) = this.Upper; this.Truncation = [lower, upper]; this.IsTruncated = true; endfunction ## -*- texinfo -*- ## @deftypefn {UniformDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = unifstat (this.Lower, this.Upper); endif endfunction endmethods endclassdef function checkparams (Lower, Upper) if (! (isscalar (Lower) && isnumeric (Lower) && isreal (Lower) && isfinite (Lower))) error ("UniformDistribution: LOWER must be a real scalar.") endif if (! (isscalar (Upper) && isnumeric (Upper) && isreal (Upper) && isfinite (Upper))) error ("UniformDistribution: UPPER must be a real scalar.") endif if (! (Lower < Upper)) error ("UniformDistribution: LOWER must be less than UPPER.") endif endfunction ## Test output %!shared pd, t %! pd = UniformDistribution (0, 5); %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.2, 0.4, 0.6, 0.8, 1], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.5, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.3, 0.4, 0.6, 0.8, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.5, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 1, 2, 3, 4, 5], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.4, 2.8, 3.2, 3.6, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 2, 3, 4, 5, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.8, 3.2, 3.6, 4, NaN], 1e-4); %!assert (iqr (pd), 2.5, 1e-14); %!assert (iqr (t), 1, 1e-14); %!assert (mean (pd), 2.5, 1e-14); %!assert (mean (t), 3, 1e-14); %!assert (median (pd), 2.5, 1e-14); %!assert (median (t), 3, 1e-14); %!assert (pdf (pd, [0:5]), [0.2, 0.2, 0.2, 0.2, 0.2, 0.2], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 0.5, 0.5, 0.5, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0.2, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.4434, 1e-4); %!assert (std (t), 0.5774, 1e-4); %!assert (var (pd), 2.0833, 1e-4); %!assert (var (t), 0.3333, 1e-4); ## Test input validation ## 'UniformDistribution' constructor %!error ... %! UniformDistribution (i, 1) %!error ... %! UniformDistribution (Inf, 1) %!error ... %! UniformDistribution ([1, 2], 1) %!error ... %! UniformDistribution ("a", 1) %!error ... %! UniformDistribution (NaN, 1) %!error ... %! UniformDistribution (1, i) %!error ... %! UniformDistribution (1, Inf) %!error ... %! UniformDistribution (1, [1, 2]) %!error ... %! UniformDistribution (1, "a") %!error ... %! UniformDistribution (1, NaN) %!error ... %! UniformDistribution (2, 1) ## 'cdf' method %!error ... %! cdf (UniformDistribution, 2, "uper") %!error ... %! cdf (UniformDistribution, 2, 3) ## 'plot' method %!error ... %! plot (UniformDistribution, "Parent") %!error ... %! plot (UniformDistribution, "PlotType", 12) %!error ... %! plot (UniformDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (UniformDistribution, "PlotType", "pdfcdf") %!error ... %! plot (UniformDistribution, "Discrete", "pdfcdf") %!error ... %! plot (UniformDistribution, "Discrete", [1, 0]) %!error ... %! plot (UniformDistribution, "Discrete", {true}) %!error ... %! plot (UniformDistribution, "Parent", 12) %!error ... %! plot (UniformDistribution, "Parent", "hax") %!error ... %! plot (UniformDistribution, "invalidNAME", "pdf") %!error ... %! plot (UniformDistribution, "PlotType", "probability") ## 'truncate' method %!error ... %! truncate (UniformDistribution) %!error ... %! truncate (UniformDistribution, 2) %!error ... %! truncate (UniformDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = UniformDistribution (0, 1); %! pd(2) = UniformDistribution (0, 2); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error pdf (pd, 1) %!error plot (pd) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/WeibullDistribution.m000066400000000000000000001031361475240274700243050ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef WeibullDistribution ## -*- texinfo -*- ## @deftypefn {statistics} WeibullDistribution ## ## Weibull probability distribution object. ## ## A @code{WeibullDistribution} object consists of parameters, a model ## description, and sample data for a Weibull probability distribution. ## ## The Weibull distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{lambda} @tab Scale parameter @tab @math{lambda > 0} ## @item @qcode{k} @tab Scale parameter @tab @math{k > 0} ## @end multitable ## ## There are several ways to create a @code{WeibullDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{WeibullDistribution (@var{lambda}, ## @var{k})} to create a Weibull distribution with specified parameter values. ## @item Use the static method @qcode{WeibullDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{WeibullDistribution} object contains the following properties, ## which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{WeibullDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{fitdist, makedist, wblcdf, wblinv, wblpdf, wblrnd, wblfit, ## wbllike, wblstat, wblplot} ## @end deftypefn properties (Dependent = true) lambda k endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "WeibullDistribution"; DistributionCode = "wbl"; NumParameters = 2; ParameterNames = {"lambda", "k"}; ParameterDescription = {"Scale", "Shape"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [realmin, realmin; Inf, Inf]; ParameterLogCI = [true, true]; endproperties properties (GetAccess = public, SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = WeibullDistribution (lambda, k) if (nargin == 0) lambda = 1; k = 1; endif checkparams (lambda, k); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [lambda, k]; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "Weibull distribution"); endfunction function disp (this) __disp__ (this, "Weibull distribution"); endfunction function this = set.lambda (this, lambda) checkparams (lambda, this.k); this.InputData = []; this.ParameterValues(1) = lambda; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function lambda = get.lambda (this) lambda = this.ParameterValues(1); endfunction function this = set.k (this, k) checkparams (this.lambda, k); this.InputData = []; this.ParameterValues(2) = k; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function k = get.k (this) k = this.ParameterValues(2); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {WeibullDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = wblcdf (x, this.lambda, this.k); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= wblcdf (lx, this.lambda, this.k); p(! (lb | ub)) /= diff (wblcdf ([lx, ux], this.lambda, this.k)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = wblcdf (this.Truncation(1), this.lambda, this.k); up = wblcdf (this.Truncation(2), this.lambda, this.k); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = wblinv (np, this.lambda, this.k); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = wblinv (p, this.lambda, this.k); endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = wblstat (this.lambda, this.k); endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = wblcdf ([lx, ux], this.lambda, this.k); m = wblinv (sum (Fa_b) / 2, this.lambda, this.k); else m = wblinv (0.5, this.lambda, this.k); endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - wbllike ([this.lambda, this.k], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {WeibullDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = wblpdf (x, this.lambda, this.k); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (wblcdf ([lx, ux], this.lambda, this.k)); endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {} plot (@var{pd}) ## @deftypefnx {WeibullDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {WeibullDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {WeibullDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {WeibullDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {WeibullDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the Weibull distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{lambda} and @qcode{@var{pnum} = 2} selects the parameter ## @var{k}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {WeibullDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {WeibullDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {WeibullDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (wblcdf ([lx, ux], this.lambda, this.k)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = wblrnd (this.lambda, this.k, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = wblrnd (this.lambda, this.k, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {WeibullDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = wblstat (this.lambda, this.k); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = wblfit (x, alpha, censor, freq, options); [~, acov] = wbllike (phat, x, censor, freq); ## Create fitted distribution object pd = WeibullDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) lambda = phat(1); k = phat(2); pd = WeibullDistribution (lambda, k); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (lambda, k) if (! (isscalar (lambda) && isnumeric (lambda) && isreal (lambda) && isfinite (lambda) && lambda > 0 )) error ("WeibullDistribution: LAMBDA must be a positive real scalar.") endif if (! (isscalar (k) && isnumeric (k) && isreal (k) && isfinite (k) && k > 0)) error ("WeibullDistribution: K must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = WeibullDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0, 0.6321, 0.8647, 0.9502, 0.9817, 0.9933], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7311, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.7769, 0.8647, 0.9502, 0.9817, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.7311, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [0, 0.2231, 0.5108, 0.9163, 1.6094, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1899, 2.4244, 2.7315, 3.1768, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, 0.5108, 0.9163, 1.6094, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.4244, 2.7315, 3.1768, 4, NaN], 1e-4); %!assert (iqr (pd), 1.0986, 1e-4); %!assert (iqr (t), 0.8020, 1e-4); %!assert (mean (pd), 1, 1e-14); %!assert (mean (t), 2.6870, 1e-4); %!assert (median (pd), 0.6931, 1e-4); %!assert (median (t), 2.5662, 1e-4); %!assert (pdf (pd, [0:5]), [1, 0.3679, 0.1353, 0.0498, 0.0183, 0.0067], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.1565, 0.4255, 0.1565, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0, 0.2231, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1, 1e-14); %!assert (std (t), 0.5253, 1e-4); %!assert (var (pd), 1, 1e-14); %!assert (var (t), 0.2759, 1e-4); ## Test input validation ## 'WeibullDistribution' constructor %!error ... %! WeibullDistribution(0, 1) %!error ... %! WeibullDistribution(-1, 1) %!error ... %! WeibullDistribution(Inf, 1) %!error ... %! WeibullDistribution(i, 1) %!error ... %! WeibullDistribution("a", 1) %!error ... %! WeibullDistribution([1, 2], 1) %!error ... %! WeibullDistribution(NaN, 1) %!error ... %! WeibullDistribution(1, 0) %!error ... %! WeibullDistribution(1, -1) %!error ... %! WeibullDistribution(1, Inf) %!error ... %! WeibullDistribution(1, i) %!error ... %! WeibullDistribution(1, "a") %!error ... %! WeibullDistribution(1, [1, 2]) %!error ... %! WeibullDistribution(1, NaN) ## 'cdf' method %!error ... %! cdf (WeibullDistribution, 2, "uper") %!error ... %! cdf (WeibullDistribution, 2, 3) ## 'paramci' method %!shared x %! x = wblrnd (1, 1, [1, 100]); %!error ... %! paramci (WeibullDistribution.fit (x), "alpha") %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", 0) %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", 1) %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", "") %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (WeibullDistribution.fit (x), "parameter", "k", "alpha", {0.05}) %!error ... %! paramci (WeibullDistribution.fit (x), "parameter", {"lambda", "k", "param"}) %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"lambda", "k", "param"}) %!error ... %! paramci (WeibullDistribution.fit (x), "parameter", "param") %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", 0.01, "parameter", "param") %!error ... %! paramci (WeibullDistribution.fit (x), "NAME", "value") %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (WeibullDistribution.fit (x), "alpha", 0.01, "parameter", "k", ... %! "NAME", "value") ## 'plot' method %!error ... %! plot (WeibullDistribution, "Parent") %!error ... %! plot (WeibullDistribution, "PlotType", 12) %!error ... %! plot (WeibullDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (WeibullDistribution, "PlotType", "pdfcdf") %!error ... %! plot (WeibullDistribution, "Discrete", "pdfcdf") %!error ... %! plot (WeibullDistribution, "Discrete", [1, 0]) %!error ... %! plot (WeibullDistribution, "Discrete", {true}) %!error ... %! plot (WeibullDistribution, "Parent", 12) %!error ... %! plot (WeibullDistribution, "Parent", "hax") %!error ... %! plot (WeibullDistribution, "invalidNAME", "pdf") %!error ... %! plot (WeibullDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (WeibullDistribution, 2) %!error ... %! proflik (WeibullDistribution.fit (x), 3) %!error ... %! proflik (WeibullDistribution.fit (x), [1, 2]) %!error ... %! proflik (WeibullDistribution.fit (x), {1}) %!error ... %! proflik (WeibullDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (WeibullDistribution.fit (x), 1, "Display") %!error ... %! proflik (WeibullDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (WeibullDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (WeibullDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (WeibullDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (WeibullDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (WeibullDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (WeibullDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (WeibullDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (WeibullDistribution) %!error ... %! truncate (WeibullDistribution, 2) %!error ... %! truncate (WeibullDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = WeibullDistribution(1, 1); %! pd(2) = WeibullDistribution(1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_obj/private/000077500000000000000000000000001475240274700215725ustar00rootroot00000000000000statistics-release-1.7.3/inst/dist_obj/private/__disp__.m000066400000000000000000000071171475240274700235110ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Private Function} __disp__ (@var{pd}, @var{distname}) ## ## Display summary of a probability distribution object. ## ## @end deftypefn function ci = __disp__ (pd, distname) if (isscalar (pd)) ## Handle special case of PiecewiseLinearDistribution if (strcmpi (pd.DistributionName, "PiecewiseLinearDistribution")) ## Print distribution header fprintf (" %s\n\n", pd.DistributionName); ## Print parameter values for i = 1:numel (pd.x) fprintf ("F(%g) = %g\n", pd.x(i), pd.Fx(i)); endfor ## Print trunctation interval if applicable if (pd.IsTruncated) fprintf (" Truncated to the interval [%g, %g]\n\n", pd.Truncation); else fprintf ("\n"); endif ## Handle special case of MultinomialDistribution elseif (strcmpi (pd.DistributionName, "MultinomialDistribution")) ## Print distribution header fprintf (" %s\n\n", pd.DistributionName); ## Print parameter values fprintf (" Probabilities:\n"); fprintf (" %0.4f", pd.Probabilities); fprintf ("\n\n"); ## Print trunctation interval if applicable if (pd.IsTruncated) fprintf (" Truncated to the interval [%g, %g]\n\n", pd.Truncation); endif ## Handle all other cases else ## Get required length for parameter values PVlen = max (arrayfun (@(x) numel (sprintf ("%g", x)), ... pd.ParameterValues)); PVstr = sprintf ("%%%dg", PVlen); ## Prepare template for fitted and not fitted distributions pat1 = [" %+7s = ", PVstr, " [%g, %g]\n"]; pat2 = [" %+7s = ", PVstr, "\n"]; ## Grad distributions that are non fittable if (any (strcmpi (pd.DistributionCode, {"unif", "tri", "logu"}))) fitted = false; ParameterIsFixed = true; elseif (all (pd.ParameterIsFixed)) fitted = false; ParameterIsFixed = pd.ParameterIsFixed; else fitted = true; ParameterIsFixed = pd.ParameterIsFixed; endif ## Print distribution header fprintf (" %s\n\n", pd.DistributionName); fprintf (" %s\n", distname); ## Print parameter values for i = 1:pd.NumParameters if (fitted && ! ParameterIsFixed(i)) fprintf (pat1, pd.ParameterNames{i}, pd.ParameterValues(i), ... pd.ParameterCI(1,i), pd.ParameterCI(2,i)); else fprintf (pat2, pd.ParameterNames{i}, pd.ParameterValues(i)); endif endfor ## Print trunctation interval if applicable if (pd.IsTruncated) fprintf (" Truncated to the interval [%g, %g]\n\n", pd.Truncation); else fprintf ("\n"); endif endif else fprintf ("%dx%d %s array\n", size (pd), class (pd)); endif endfunction statistics-release-1.7.3/inst/dist_obj/private/__paramci__.m000066400000000000000000000075771475240274700242000ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Private Function} [@var{nlogl}, @var{param}, @var{other}] = __paramci__ (@var{pd}, @var{varargin}) ## ## Compute the cofindence intervals for the selected parameters of a ## probability distribution object. ## ## @end deftypefn function ci = __paramci__ (pd, varargin) ## Get Distribution specific info distname = pd.DistributionCode; parnames = pd.ParameterNames(! pd.ParameterIsFixed); ## Add defaults and parse optional arguments alpha = 0.05; param = logical ([1:numel(parnames)]); if (mod (numel (varargin), 2) != 0) error ("paramci: optional arguments must be in NAME-VALUE pairs."); endif while (numel (varargin) > 0) switch (tolower (varargin{1})) case "alpha" alpha = varargin{2}; if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("paramci: invalid VALUE for 'Alpha' argument."); endif case "parameter" if (! isvector (varargin{2}) || ((iscellstr (varargin{2}) || isnumeric (varargin{2})) && numel (varargin{2}) > numel (parnames))) error ("paramci: invalid VALUE size for 'Parameter' argument."); endif if (iscellstr (varargin{2})) tmp = cellfun (@(x) strcmpi (x, parnames), varargin{2}, ... "UniformOutput", false); param = or (tmp{:}); elseif (isnumeric (varargin{2})) param = ismember (parnames, varargin{2}); else # assume it is a character vector param = strcmpi (varargin{2}, parnames); endif if (! any (param)) error ("paramci: unknown distribution parameter."); endif case {"type", "logflag"} printf ("paramci: '%s' argument not supported yet.", varargin{1}); otherwise error ("paramci: invalid NAME for optional argument."); endswitch varargin([1:2]) = []; endwhile ## Get confidence intervals for all parameters from selected distribution if (strcmpi (distname, "bino")) ntrials = pd.N; [~, ci] = mle (pd.InputData.data, "distribution", distname, ... "alpha", alpha, "ntrials", ntrials, ... "frequency", pd.InputData.freq); elseif (strcmpi (distname, "gp")) theta = pd.theta; [~, ci] = mle (pd.InputData.data, "distribution", distname, ... "alpha", alpha, "theta", theta, ... "frequency", pd.InputData.freq); elseif (strcmpi (distname, "hn")) mu = pd.mu; [~, ci] = mle (pd.InputData.data, "distribution", distname, ... "alpha", alpha, "mu", mu, "frequency", pd.InputData.freq); elseif (! pd.CensoringAllowed) [~, ci] = mle (pd.InputData.data, "distribution", distname, ... "alpha", alpha, "frequency", pd.InputData.freq); else [~, ci] = mle (pd.InputData.data, "distribution", distname, ... "alpha", alpha, "censoring", pd.InputData.cens, ... "frequency", pd.InputData.freq); endif ## Return ci only for requested parameters ci(:, !param) = []; endfunction statistics-release-1.7.3/inst/dist_obj/private/__plot__.m000066400000000000000000000311021475240274700235170ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Private Function} [@var{h}] = __plot__ (@var{pd}, @var{Discrete}, @var{varargin}) ## ## Plot a probability distribution object. ## ## @end deftypefn function h = __plot__ (pd, DistType, varargin) ## Add defaults (Discrete is passed by the calling method) ax = []; PlotType = "pdf"; Discrete = DistType; ## Parse optional agruments if (mod (numel (varargin), 2) != 0) error ("plot: optional arguments must be in NAME-VALUE pairs."); endif while (numel (varargin) > 0) switch (tolower (varargin{1})) case "plottype" ValidTypes = {"pdf", "cdf", "probability"}; try selected_T = strcmpi (varargin{2}, ValidTypes); catch error ("plot: invalid VALUE size for 'Parameter' argument."); end_try_catch if (! any (selected_T) || sum (selected_T) > 1) error ("plot: invalid VALUE for 'PlotType' argument."); endif PlotType = ValidTypes{selected_T}; case "discrete" if (! (islogical (varargin{2}) && isscalar (varargin{2}))) error ("plot: invalid VALUE for 'Discrete' argument."); endif ## Only for discrete distributions this can be changed by the user if (DistType) Discrete = varargin{2}; endif case "parent" if (! isaxes (varargin{2})) error ("plot: invalid VALUE for 'Parent' argument."); endif ax = varargin{2}; otherwise error ("plot: invalid NAME for optional argument."); endswitch varargin([1:2]) = []; endwhile ## Check for invalid cases of probability type bafore creating new axes if (strcmpi (PlotType, "probability")) if (! isprop (pd, "InputData")) msg = "plot: 'probability' PlotType is not supported for '%s'."; error (sprintf (msg, pd.DistributionName)); endif if (isempty (pd.InputData)) error ("plot: no fitted DATA to plot a probability plot."); endif endif ## Get current axes or create new ones if (isempty (ax)) ax = gca (); endif ## Switch to PlotType switch (PlotType) case "pdf" h = plot_pdf (pd, ax, DistType, Discrete); case "cdf" h = plot_cdf (pd, ax, DistType, Discrete); case "probability" h = plot_prob (pd, ax, DistType, Discrete); endswitch endfunction function x = expand_freq (data, freq) x = []; for i = 1:numel (freq) x = [x, repmat(data(i), 1, freq(i))]; endfor endfunction function [lb, ub, xmin, xmax] = compute_boundaries (pd) ## Compute moments to determine plot boundaries m = mean (pd); s = std (pd); lb = m - 3 * s; ub = m + 3 * s; xmin = m - 3.5 * s; xmax = m + 3.5 * s; ## Fix boundaries for specific distributions PD = {"bino", "bisa", "exp", "gam", "invg", "logl", ... "logn", "naka", "nbin", "poiss", "rayl", "rice", "wbl"}; if (strcmpi (pd.DistributionCode, "beta")) lb = xmin = 0; ub = xmax = 1; elseif (strcmpi (pd.DistributionCode, "burr")) lb = xmin = 0; ub = xmax = m + 3 * iqr (pd); elseif (any (strcmpi (pd.DistributionCode, PD))) lb = max (m - 3 * s, 0); xmin = max (m - 3.5 * s, 0); elseif (strcmpi (pd.DistributionCode, "gev")) elseif (strcmpi (pd.DistributionCode, "gp")) elseif (strcmpi (pd.DistributionCode, "hn")) lb = max (m - 3 * s, m); xmin = max (m - 3.5 * s, m); endif endfunction function h = plot_pdf (pd, ax, DistType, Discrete) ## Handle special case of multinomial distribution if (strcmpi (pd.DistributionCode, "mn")) y = pd.ParameterValues'; x = [1:numel(y)]'; if (Discrete) h = stem (ax, x, y, "color", "b"); else h = plot (ax, x, y, ";;b-"); endif xlim ([0.5, max(x)+0.5]); xlabel ("Data"); ylabel ("Probability"); return endif ## Handle special case of piecewise linear distribution if (strcmpi (pd.DistributionCode, "pl")) x = pd.ParameterValues(:,1); y = pd.ParameterValues(:,2); h = plot (ax, x, y, ";;b-"); xgap = (x(end) - x(1)) * 0.1; xlim ([x(1)-xgap, x(end)+xgap]); xlabel ("Data"); ylabel ("Probability"); return endif ## Handle special case of triangular distribution if (strcmpi (pd.DistributionCode, "tri")) lb = pd.A; ub = pd.C; xmin = lb - (ub - lb) * 0.1; xmax = ub + (ub - lb) * 0.1; x = [lb:(ub-lb)/100:ub]'; y = pdf (pd, x); h = plot (ax, x, y, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("PDF"); return endif ## Handle special case of log-uniform and uniform distributions if (any (strcmpi (pd.DistributionCode, {"logu", "unif"}))) lb = pd.Lower; ub = pd.Upper; xmin = lb - (ub - lb) * 0.1; xmax = ub + (ub - lb) * 0.1; x = [lb:(ub-lb)/100:ub]'; y = pdf (pd, x); h = plot (ax, x, y, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("PDF"); return endif ## Check for fitted distribution if (isempty (pd.InputData)) # fixed parameters, no data ## Compute plot boundaries [lb, ub, xmin, xmax] = compute_boundaries (pd); ## Compute stem or line for PDF if (DistType) x = [floor(lb):ceil(ub)]'; y = pdf (pd, x); else x = [lb:(ub-lb)/100:ub]'; y = pdf (pd, x); endif ## Plot if (Discrete) h = stem (ax, x, y, "color", "r"); xlim ([min(y)-0.5, max(y)+0.5]); xlabel ("Data"); ylabel ("Probability"); else h = plot (ax, x, y, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("PDF"); endif else # fitted distribution, data available ## Expand frequency vector (if necessary) if (any (pd.InputData.freq != 1)) x = expand_freq (pd.InputData.data, pd.InputData.freq); else x = pd.InputData.data; endif ## Keep data within plotting boundaries [lb, ub, xmin, xmax] = compute_boundaries (pd); x(x < lb | x > ub) = []; ## Compute the patch or histogram for data xsize = numel (x); if (DistType) binwidth = 1; xmin = min (x) - 1; xmax = max (x) + 1; [binsize, bincenter] = hist (x, [xmin:xmax]); else nbins = ceil (sqrt (xsize)); [binsize, bincenter] = hist (x, nbins); binwidth = max (diff (bincenter)); xmin = min (x) - binwidth / 2; xmax = max (x) + binwidth / 2; endif ## Compute stem or line for PDF if (Discrete) x = [min(x):max(x)]'; y = pdf (pd, x); else x = [xmin:(xmax-xmin)/100:xmax]'; y = pdf (pd, x); endif ## Normalize density line y = xsize * y * binwidth; ## Plot if (DistType) h(2) = patch (ax, bincenter, binsize, 1, "facecolor", "b"); hold on; if (Discrete) h(1) = stem (ax, x, y, "color", "r"); else h(1) = plot (ax, x, y, ";;r-"); endif xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("Probability"); hold off; else h(2) = bar (ax, bincenter, binsize, 1, "facecolor", "b"); hold on; h(1) = plot (ax, x, y, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("PDF"); hold off; endif endif endfunction function h = plot_cdf (pd, ax, DistType, Discrete) ## Handle special case of multinomial distribution if (strcmpi (pd.DistributionCode, "mn")) y = pd.ParameterValues'; x = [1:numel(y)]'; if (Discrete) h = stem (ax, x, y, "color", "b"); else h = plot (ax, x, y, ";;b-"); endif xlim ([0.5, max(x)+0.5]); xlabel ("Data"); ylabel ("Probability"); return endif ## Handle special case of piecewise linear distribution if (strcmpi (pd.DistributionCode, "pl")) x = pd.ParameterValues(:,1); y = pd.ParameterValues(:,2); h = plot (ax, x, y, ";;b-"); xgap = (x(end) - x(1)) * 0.1; xlim ([x(1)-xgap, x(end)+xgap]); xlabel ("Data"); ylabel ("Probability"); return endif ## Handle special case of triangular distribution if (strcmpi (pd.DistributionCode, "tri")) lb = pd.A; ub = pd.C; xmin = lb - (ub - lb) * 0.1; xmax = ub + (ub - lb) * 0.1; x = [lb:(ub-lb)/100:ub]'; y = pdf (pd, x); h = plot (ax, x, y, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("PDF"); return endif ## Handle special case of log-uniform and uniform distributions if (any (strcmpi (pd.DistributionCode, {"logu", "unif"}))) lb = pd.Lower; ub = pd.Upper; xmin = lb - (ub - lb) * 0.1; xmax = ub + (ub - lb) * 0.1; x = [lb:(ub-lb)/100:ub]'; y = pdf (pd, x); h = plot (ax, x, y, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); xlabel ("Data"); ylabel ("PDF"); return endif ## Compute plot boundaries [lb, ub, xmin, xmax] = compute_boundaries (pd); ## Check for fitted distribution if (isempty (pd.InputData)) # fixed parameters, no data ## Compute stem or line for PDF if (DistType) x = [floor(lb):ceil(ub)]'; p = cdf (pd, x); else x = [lb:(ub-lb)/100:ub]'; p = cdf (pd, x); endif ## Plot if (Discrete) h = stairs (ax, x, p, "color", "r"); xlim ([lb-0.5, ub+0.5]); ylim ([0, 1]); xlabel ("Data"); ylabel ("CDF"); else h = plot (ax, x, p, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); ylim ([0, 1]); xlabel ("Data"); ylabel ("CDF"); endif else # fitted distribution, data available ## Expand frequency vector (if necessary) if (any (pd.InputData.freq != 1)) x = expand_freq (pd.InputData.data, pd.InputData.freq); else x = pd.InputData.data; endif ## Compute the stairs for data [yy, xx, ~, ~, eid] = cdfcalc (x); n = length (xx); ## Create vectors for plotting nidx = reshape (repmat (1:n, 2, 1), 2*n, 1); xCDF = [-Inf; xx(nidx); Inf]; yCDF = [0; 0; yy(1+nidx)]; ## Compute stairs or line for CDF if (DistType) x = [min(x):max(x)]'; p = cdf (pd, x); else x = [xmin:(xmax-xmin)/100:xmax]'; p = cdf (pd, x); endif ## Plot if (DistType) h(2) = plot (ax, xCDF, yCDF, ";;b-"); hold on; if (Discrete) h(1) = stem (ax, x, p, "color", "r"); else h(1) = plot (ax, x, p, ";;r-"); endif xlim ([xmin, xmax]); ylim ([0, 1]); xlabel ("Data"); ylabel ("CDF"); hold off; else h(2) = plot (ax, xCDF, yCDF, ";;b-"); hold on; h(1) = plot (ax, x, p, ";;r-", "linewidth", 2); xlim ([xmin, xmax]); ylim ([0, 1]); xlabel ("Data"); ylabel ("CDF"); hold off; endif endif endfunction function h = plot_prob (pd, ax, DistType, Discrete) ## Expand frequency vector (if necessary) if (any (pd.InputData.freq != 1)) x = expand_freq (pd.InputData.data, pd.InputData.freq); else x = pd.InputData.data; endif ## Compute the probabilities for data n = rows (x); y = icdf (pd, ([1:n]' - 0.5) / n); x = sort (x); ## Plot reference line X = Y = [x(1); x(end)]; h(2) = line (ax, X, Y, "LineStyle", "-.", "Marker", "none", "color", "red"); hold on; h(1) = plot (ax, x, y, "LineStyle", "none", "Marker", "+", "color", "blue"); ## Plot labels ylabel "Probability" xlabel "Data" ## Plot grid p = [0.001, 0.005, 0.01, 0.02, 0.05, 0.10, 0.25, 0.5, ... 0.75, 0.90, 0.95, 0.98, 0.99, 0.995, 0.999]; label = {"0.001", "0.005", "0.01", "0.02", "0.05", "0.10", "0.25", "0.50", ... "0.75", "0.90", "0.95", "0.98", "0.99", "0.995", "0.999"}; tick = icdf (pd, p); set (ax, "ytick", tick, "yticklabel", label); ## Compute plot boundaries [~, ~, xmin, xmax] = compute_boundaries (pd); ## Set view range with a bit of space around data ymin = icdf (pd, 0.25 ./ n); ymax = icdf (pd, (n - 0.25) ./ n); set (ax, "ylim", [ymin, ymax], "xlim", [xmin, xmax]); grid (ax, "on"); box (ax, "off"); hold off; endfunction statistics-release-1.7.3/inst/dist_obj/private/__proflik__.m000066400000000000000000000111421475240274700242110ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Private Function} [@var{nlogl}, @var{param}, @var{other}] = __proflik__ (@var{pd}, @var{pnum}, @var{varargin}) ## ## Compute the negative log likelihood for a range of the selected parameter of ## a probability distribution object. ## ## @end deftypefn function [nlogl, param] = __proflik__ (pd, pnum, varargin) ## Check for non-fixed pnum npvec = find (pd.ParameterIsFixed == false); if (! (isnumeric (pnum) && isscalar (pnum) && ismember (pnum, npvec))) error (strcat (["proflik: PNUM must be a scalar number"], ... [" indexing a non-fixed parameter."])); endif ## Add defaults and parse optional arguments param = []; Display = false; while (numel (varargin) > 0) if (isnumeric (varargin{1})) if (! isvector (varargin{1})) error ("proflik: SETPARAM must be a numeric vector."); endif param = varargin{1}; varargin(1) = []; elseif (ischar (varargin{1})) if (strcmpi (varargin{1}, "display")) if (numel (varargin) < 2) error ("proflik: missing VALUE for 'Display' argument."); endif if (! ischar (varargin{2})) error ("proflik: invalid VALUE type for 'Display' argument."); endif if (size (varargin{2}, 1) != 1) error ("proflik: invalid VALUE size for 'Display' argument."); endif if (strcmpi (varargin{2}, "off")) Display = false; elseif (strcmpi (varargin{2}, "on")) Display = true; else error ("proflik: invalid VALUE for 'Display' argument."); endif varargin([1:2]) = []; else error ("proflik: invalid NAME for optional arguments."); endif else error ("proflik: invalid optional argument."); endif endwhile ## Get fitted parameter CI and restrict for non-fixed range pname = pd.ParameterNames{pnum}; lower = pd.ParameterCI(1, pnum); if (lower < pd.ParameterRange(1,pnum)) lower = pd.ParameterRange(1,pnum); endif upper = pd.ParameterCI(2, pnum); if (upper > pd.ParameterRange(2,pnum)) upper = pd.ParameterRange(2,pnum); endif ## Create parameter vector if (isempty (param)) param = [lower:(upper-lower)/100:upper]; else ## Restrict user defined parameter range within non-fixed range param(param < pd.ParameterRange(1,pnum)) = []; param(param > pd.ParameterRange(2,pnum)) = []; endif ## Get nlogl estimate and optimal parameter values optnll = negloglik (pd); optpar = pd.ParameterValues; ## Compute negative log likelihood for the given parameter range by ## calling the appropriate likelihood function params = pd.ParameterValues; if (pd.CensoringAllowed) for i = 1:numel (param) params(pnum) = param(i); fname = sprintf ("%slike", pd.DistributionCode); nlogl(i) = - feval (fname, params, pd.InputData.data, ... pd.InputData.cens, pd.InputData.freq); endfor else for i = 1:numel (param) params(pnum) = param(i); fname = sprintf ("%slike", pd.DistributionCode); nlogl(i) = - feval (fname, params, pd.InputData.data, pd.InputData.freq); endfor endif ## Plot the loglikelihood values against the range of the selected parameter if (Display) ## Get 95% confidence params(pnum) = lower; if (pd.CensoringAllowed) nll_conf = - feval (fname, params, pd.InputData.data, ... pd.InputData.cens, pd.InputData.freq); else nll_conf = - feval (fname, params, pd.InputData.data, pd.InputData.freq); endif plot (optpar(pnum), optnll, "ok;Estimate;", ... param, nlogl, "-r;Exact log likelihood;", ... param, repmat (nll_conf, size (param)), ":b;95% confidence;"); xlabel (pname); ylabel ("log likelihood"); xlim ([param(1), param(end)]); endif endfunction statistics-release-1.7.3/inst/dist_obj/tLocationScaleDistribution.m000066400000000000000000001101021475240274700255750ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef tLocationScaleDistribution ## -*- texinfo -*- ## @deftypefn {statistics} tLocationScaleDistribution ## ## Weibull probability distribution object. ## ## A @code{tLocationScaleDistribution} object consists of parameters, a model ## description, and sample data for a Weibull probability distribution. ## ## The Weibull distribution uses the following parameters. ## ## @multitable @columnfractions 0.25 0.48 0.27 ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} ## ## @item @qcode{mu} @tab Location parameter @tab @math{-Inf < mu < Inf} ## @item @qcode{sigma} @tab Scale parameter @tab @math{sigma > 0} ## @item @qcode{nu} @tab Degrees of Freedom @tab @math{nu > 0} ## @end multitable ## ## There are several ways to create a @code{tLocationScaleDistribution} object. ## ## @itemize ## @item Fit a distribution to data using the @code{fitdist} function. ## @item Create a distribution with specified parameter values using the ## @code{makedist} function. ## @item Use the constructor @qcode{tLocationScaleDistribution (@var{lambda}, ## @var{mu})} to create a Weibull distribution with specified parameter values. ## @item Use the static method @qcode{tLocationScaleDistribution.fit (@var{x}, ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. ## @end itemize ## ## It is highly recommended to use @code{fitdist} and @code{makedist} ## functions to create probability distribution objects, instead of the ## constructor and the aforementioned static method. ## ## A @code{tLocationScaleDistribution} object contains the following ## properties, which can be accessed using dot notation. ## ## @multitable @columnfractions 0.25 0.25 0.25 0.25 ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab ## @qcode{NumParameters} @tab @qcode{ParameterNames} ## @item @qcode{ParameterDescription} @tab @qcode{ParameterValues} @tab ## @qcode{ParameterValues} @tab @qcode{ParameterCI} ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab ## @qcode{IsTruncated} @tab @qcode{InputData} ## @end multitable ## ## A @code{tLocationScaleDistribution} object contains the following methods: ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, ## @code{random}, @code{std}, @code{truncate}, @code{var}. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{fitdist, makedist, tlscdf, tlsinv, tlspdf, tlsrnd, tlsfit, ## tlslike, tlsstat} ## @end deftypefn properties (Dependent = true) mu sigma nu endproperties properties (GetAccess = public, Constant = true) CensoringAllowed = true; DistributionName = "tLocationScaleDistribution"; DistributionCode = "tls"; NumParameters = 3; ParameterNames = {"mu", "sigma", "nu"}; ParameterDescription = {"Location", "Scale", "Degrees of Freedom"}; endproperties properties (GetAccess = public, Constant = true) ParameterRange = [-Inf, realmin, realmin; Inf, Inf, Inf]; ParameterLogCI = [false, true, true]; endproperties properties (GetAccess = public, SetAccess = protected) ParameterValues ParameterCI ParameterCovariance ParameterIsFixed Truncation IsTruncated InputData endproperties methods (Hidden) function this = tLocationScaleDistribution (mu, sigma, nu) if (nargin == 0) mu = 0; sigma = 1; nu = 5; endif checkparams (mu, sigma, nu); this.InputData = []; this.IsTruncated = false; this.ParameterValues = [mu, sigma, nu]; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function display (this) fprintf ("%s =\n", inputname(1)); __disp__ (this, "t Location-Scale distribution"); endfunction function disp (this) __disp__ (this, "t Location-Scale distribution"); endfunction function this = set.mu (this, mu) checkparams (mu, this.sigma, this.nu); this.InputData = []; this.ParameterValues(1) = mu; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function mu = get.mu (this) mu = this.ParameterValues(1); endfunction function this = set.sigma (this, sigma) checkparams (this.mu, sigma, this.nu); this.InputData = []; this.ParameterValues(2) = sigma; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function sigma = get.sigma (this) sigma = this.ParameterValues(2); endfunction function this = set.nu (this, nu) checkparams (this.mu, this.sigma, nu); this.InputData = []; this.ParameterValues(3) = nu; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction function nu = get.nu (this) nu = this.ParameterValues(3); endfunction endmethods methods (Access = public) ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) ## @deftypefnx {tLocationScaleDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) ## ## Compute the cumulative distribution function (CDF). ## ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of ## the CDF of the probability distribution object, @var{pd}, evaluated at ## the values in @var{x}. ## ## @end deftypefn function p = cdf (this, x, uflag) if (! isscalar (this)) error ("cdf: requires a scalar probability distribution."); endif ## Check for "upper" flag if (nargin > 2 && strcmpi (uflag, "upper")) utail = true; elseif (nargin > 2 && ! strcmpi (uflag, "upper")) error ("cdf: invalid argument for upper tail."); else utail = false; endif ## Do the computations p = tlscdf (x, this.mu, this.sigma, this.nu); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; p(lb) = 0; p(ub) = 1; p(! (lb | ub)) -= tlscdf (lx, this.mu, this.sigma, this.nu); p(! (lb | ub)) /= diff (tlscdf ([lx, ux], this.mu, this.sigma, this.nu)); endif ## Apply uflag if (utail) p = 1 - p; endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) ## ## Compute the inverse cumulative distribution function (iCDF). ## ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the ## inverse of the CDF) of the probability distribution object, @var{pd}, ## evaluated at the values in @var{x}. ## ## @end deftypefn function x = icdf (this, p) if (! isscalar (this)) error ("icdf: requires a scalar probability distribution."); endif if (this.IsTruncated) lp = tlscdf (this.Truncation(1), this.mu, this.sigma, this.nu); up = tlscdf (this.Truncation(2), this.mu, this.sigma, this.nu); ## Adjust p values within range of p @ lower limit and p @ upper limit is_nan = p < 0 | p > 1; p(is_nan) = NaN; np = lp + (up - lp) .* p; x = tlsinv (np, this.mu, this.sigma, this.nu); x(x < this.Truncation(1)) = this.Truncation(1); x(x > this.Truncation(2)) = this.Truncation(2); else x = tlsinv (p, this.mu, this.sigma, this.nu); endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{r} =} iqr (@var{pd}) ## ## Compute the interquartile range of a probability distribution. ## ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function r = iqr (this) if (! isscalar (this)) error ("iqr: requires a scalar probability distribution."); endif r = diff (icdf (this, [0.25, 0.75])); endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{m} =} mean (@var{pd}) ## ## Compute the mean of a probability distribution. ## ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = mean (this) if (! isscalar (this)) error ("mean: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); else m = tlsstat (this.mu, this.sigma, this.nu); endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{m} =} median (@var{pd}) ## ## Compute the median of a probability distribution. ## ## @code{@var{m} = median (@var{pd})} computes the median of the probability ## distribution object, @var{pd}. ## ## @end deftypefn function m = median (this) if (! isscalar (this)) error ("median: requires a scalar probability distribution."); endif if (this.IsTruncated) lx = this.Truncation(1); ux = this.Truncation(2); Fa_b = tlscdf ([lx, ux], this.mu, this.sigma, this.nu); m = tlsinv (sum (Fa_b) / 2, this.mu, this.sigma, this.nu); else m = tlsstat (this.mu, this.sigma, this.nu); endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{nlogL} =} negloglik (@var{pd}) ## ## Compute the negative loglikelihood of a probability distribution. ## ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood ## of the probability distribution object, @var{pd}. ## ## @end deftypefn function nlogL = negloglik (this) if (! isscalar (this)) error ("negloglik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) nlogL = []; return endif nlogL = - tlslike ([this.mu, this.sigma, this.nu], this.InputData.data, ... this.InputData.cens, this.InputData.freq); endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{ci} =} paramci (@var{pd}) ## @deftypefnx {tLocationScaleDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) ## ## Compute the confidence intervals for probability distribution parameters. ## ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper ## boundaries of the 95% confidence interval for each parameter of the ## probability distribution object, @var{pd}. ## ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the ## confidence intervals with additional options specified specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} ## specifying the significance level for the confidence interval. The ## default value 0.05 corresponds to a 95% confidence interval. ## ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of ## character vectors specifying the parameter names for which to compute ## confidence intervals. By default, @code{paramci} computes confidence ## intervals for all distribution parameters. ## @end multitable ## ## @code{paramci} is meaningful only when @var{pd} is fitted to data, ## otherwise an empty array, @qcode{[]}, is returned. ## ## @end deftypefn function ci = paramci (this, varargin) if (! isscalar (this)) error ("paramci: requires a scalar probability distribution."); endif if (isempty (this.InputData)) ci = [this.ParameterValues; this.ParameterValues]; else ci = __paramci__ (this, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) ## ## Compute the probability distribution function (PDF). ## ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the ## probability distribution object, @var{pd}, evaluated at the values in ## @var{x}. ## ## @end deftypefn function y = pdf (this, x) if (! isscalar (this)) error ("pdf: requires a scalar probability distribution."); endif y = tlspdf (x, this.mu, this.sigma, this.nu); if (this.IsTruncated) lx = this.Truncation(1); lb = x < lx; ux = this.Truncation(2); ub = x > ux; y(lb | ub) = 0; y(! (lb | ub)) /= diff (tlscdf ([lx, ux], this.mu, this.sigma, this.nu)); endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {} plot (@var{pd}) ## @deftypefnx {tLocationScaleDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) ## @deftypefnx {tLocationScaleDistribution} {@var{h} =} plot (@dots{}) ## ## Plot a probability distribution object. ## ## @code{plot (@var{pd}} plots a probability density function (PDF) of the ## probability distribution object @var{pd}. If @var{pd} contains data, ## which have been fitted by @code{fitdist}, the PDF is superimposed over a ## histogram of the data. ## ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional ## options with the @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot ## type. @qcode{"pdf"} plots the probability density function (PDF). When ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. ## @qcode{"probability"} plots a probability plot using a CDF of the data ## and a CDF of the fitted probability distribution. This option is ## available only when @var{pd} is fitted to data. ## ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to ## plot the PDF or CDF of a discrete distribution object as a line plot or a ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By ## default, it is @qcode{true} for discrete distributions and @qcode{false} ## for continuous distributions. When @var{pd} is a continuous distribution ## object, option is ignored. ## ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If ## not specified, the @code{plot} function plots into the current axes or ## creates a new axes object if one does not exist. ## @end multitable ## ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted ## objects. ## ## @end deftypefn function [varargout] = plot (this, varargin) if (! isscalar (this)) error ("plot: requires a scalar probability distribution."); endif h = __plot__ (this, false, varargin{:}); if (nargout > 0) varargout{1} = h; endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) ## @deftypefnx {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) ## @deftypefnx {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) ## @deftypefnx {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) ## ## Profile likelihood function for a probability distribution object. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} ## returns a vector @var{nlogL} of negative loglikelihood values and a ## vector @var{param} of corresponding parameter values for the parameter in ## the position indicated by @var{pnum}. By default, @code{proflik} uses ## the lower and upper bounds of the 95% confidence interval and computes ## 100 equispaced values for the selected parameter. @var{pd} must be ## fitted to data. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood ## against the default range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam})} defines a user-defined range of the selected parameter. ## ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile ## likelihood against the user-defined range of the selected parameter. ## ## For the location-scale T distribution, @qcode{@var{pnum} = 1} selects the ## parameter @qcode{mu}, @qcode{@var{pnum} = 2} selects the parameter ## @qcode{sigma}, and @qcode{@var{pnum} = 3} selects the parameter @var{nu}. ## ## When opted to display the profile likelihood plot, @code{proflik} also ## plots the baseline loglikelihood computed at the lower bound of the 95% ## confidence interval and estimated maximum likelihood. The latter might ## not be observable if it is outside of the used-defined range of parameter ## values. ## ## @end deftypefn function [varargout] = proflik (this, pnum, varargin) if (! isscalar (this)) error ("proflik: requires a scalar probability distribution."); endif if (isempty (this.InputData)) error ("proflik: no fitted data available."); endif [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{y} =} random (@var{pd}) ## @deftypefnx {tLocationScaleDistribution} {@var{y} =} random (@var{pd}, @var{rows}) ## @deftypefnx {tLocationScaleDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {tLocationScaleDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) ## ## Generate random arrays from the probability distribution object. ## ## @code{@var{r} = random (@var{pd})} returns a random number from the ## distribution object @var{pd}. ## ## When called with a single size argument, @code{betarnd} returns a square ## matrix with the dimension specified. When called with more than one ## scalar argument, the first two arguments are taken as the number of rows ## and columns and any further arguments specify additional matrix ## dimensions. The size may also be specified with a row vector of ## dimensions, @var{sz}. ## ## @end deftypefn function r = random (this, varargin) if (! isscalar (this)) error ("random: requires a scalar probability distribution."); endif if (this.IsTruncated) sz = [varargin{:}]; ps = prod (sz); ## Get an estimate of how many more random numbers we need to randomly ## pick the appropriate size from lx = this.Truncation(1); ux = this.Truncation(2); ratio = 1 / diff (tlscdf ([lx, ux], this.mu, this.sigma, this.nu)); nsize = fix (2 * ratio * ps); # times 2 to be on the safe side ## Generate the numbers and remove out-of-bound random samples r = tlsrnd (this.mu, this.sigma, this.nu, nsize, 1); r(r < lx | r > ux) = []; ## Randomly select the required size and reshape to requested dimensions idx = randperm (numel (r), ps); r = reshape (r(idx), sz); else r = tlsrnd (this.mu, this.sigma, this.nu, varargin{:}); endif endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{s} =} std (@var{pd}) ## ## Compute the standard deviation of a probability distribution. ## ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function s = std (this) if (! isscalar (this)) error ("std: requires a scalar probability distribution."); endif v = var (this); s = sqrt (v); endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) ## ## Truncate a probability distribution. ## ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution ## @var{t}, which is the probability distribution @var{pd} truncated to the ## specified interval with lower limit, @var{lower}, and upper limit, ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the ## returned probability distribution @var{t} is not fitted, does not contain ## any data or estimated values, and it is as it has been created with the ## @var{makedist} function, but it includes the truncation interval. ## ## @end deftypefn function this = truncate (this, lower, upper) if (! isscalar (this)) error ("truncate: requires a scalar probability distribution."); endif if (nargin < 3) error ("truncate: missing input argument."); elseif (lower >= upper) error ("truncate: invalid lower upper limits."); endif this.Truncation = [lower, upper]; this.IsTruncated = true; this.InputData = []; this.ParameterIsFixed = [true, true, true]; this.ParameterCovariance = zeros (this.NumParameters); endfunction ## -*- texinfo -*- ## @deftypefn {tLocationScaleDistribution} {@var{v} =} var (@var{pd}) ## ## Compute the variance of a probability distribution. ## ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the ## probability distribution object, @var{pd}. ## ## @end deftypefn function v = var (this) if (! isscalar (this)) error ("var: requires a scalar probability distribution."); endif if (this.IsTruncated) fm = @(x) x .* pdf (this, x); m = integral (fm, this.Truncation(1), this.Truncation(2)); fv = @(x) ((x - m) .^ 2) .* pdf (this, x); v = integral (fv, this.Truncation(1), this.Truncation(2)); else [~, v] = tlsstat (this.mu, this.sigma, this.nu); endif endfunction endmethods methods (Static, Hidden) function pd = fit (x, varargin) ## Check input arguments if (nargin < 2) alpha = 0.05; else alpha = varargin{1}; endif if (nargin < 3) censor = []; else censor = varargin{2}; endif if (nargin < 4) freq = []; else freq = varargin{3}; endif if (nargin < 5) options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; else options = varargin{4}; endif ## Fit data [phat, pci] = tlsfit (x, alpha, censor, freq, options); [~, acov] = tlslike (phat, x, censor, freq); ## Create fitted distribution object pd = tLocationScaleDistribution.makeFitted ... (phat, pci, acov, x, censor, freq); endfunction function pd = makeFitted (phat, pci, acov, x, censor, freq) mu = phat(1); sigma = phat(2); nu = phat(3); pd = tLocationScaleDistribution (mu, sigma, nu); pd.ParameterCI = pci; pd.ParameterIsFixed = [false, false, false]; pd.ParameterCovariance = acov; pd.InputData = struct ("data", x, "cens", censor, "freq", freq); endfunction endmethods endclassdef function checkparams (mu, sigma, nu) if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) error ("tLocationScaleDistribution: MU must be a real scalar.") endif if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) && isfinite (sigma) && sigma > 0)) error ("tLocationScaleDistribution: SIGMA must be a positive real scalar.") endif if (! (isscalar (nu) && isnumeric (nu) && isreal (nu) && isfinite (nu) && nu > 0)) error ("tLocationScaleDistribution: NU must be a positive real scalar.") endif endfunction ## Test output %!shared pd, t %! pd = tLocationScaleDistribution; %! t = truncate (pd, 2, 4); %!assert (cdf (pd, [0:5]), [0.5, 0.8184, 0.9490, 0.9850, 0.9948, 0.9979], 1e-4); %!assert (cdf (t, [0:5]), [0, 0, 0, 0.7841, 1, 1], 1e-4); %!assert (cdf (pd, [1.5, 2, 3, 4, NaN]), [0.9030, 0.9490, 0.9850, 0.9948, NaN], 1e-4); %!assert (cdf (t, [1.5, 2, 3, 4, NaN]), [0, 0, 0.7841, 1, NaN], 1e-4); %!assert (icdf (pd, [0:0.2:1]), [-Inf, -0.9195, -0.2672, 0.2672, 0.9195, Inf], 1e-4); %!assert (icdf (t, [0:0.2:1]), [2, 2.1559, 2.3533, 2.6223, 3.0432, 4], 1e-4); %!assert (icdf (pd, [-1, 0.4:0.2:1, NaN]), [NaN, -0.2672, 0.2672, 0.9195, Inf, NaN], 1e-4); %!assert (icdf (t, [-1, 0.4:0.2:1, NaN]), [NaN, 2.3533, 2.6223, 3.0432, 4, NaN], 1e-4); %!assert (iqr (pd), 1.4534, 1e-4); %!assert (iqr (t), 0.7139, 1e-4); %!assert (mean (pd), 0, eps); %!assert (mean (t), 2.6099, 1e-4); %!assert (median (pd), 0, eps); %!assert (median (t), 2.4758, 1e-4); %!assert (pdf (pd, [0:5]), [0.3796, 0.2197, 0.0651, 0.0173, 0.0051, 0.0018], 1e-4); %!assert (pdf (t, [0:5]), [0, 0, 1.4209, 0.3775, 0.1119, 0], 1e-4); %!assert (pdf (pd, [-1, 1.5, NaN]), [0.2197, 0.1245, NaN], 1e-4); %!assert (pdf (t, [-1, 1.5, NaN]), [0, 0, NaN], 1e-4); %!assert (isequal (size (random (pd, 100, 50)), [100, 50])) %!assert (any (random (t, 1000, 1) < 2), false); %!assert (any (random (t, 1000, 1) > 4), false); %!assert (std (pd), 1.2910, 1e-4); %!assert (std (t), 0.4989, 1e-4); %!assert (var (pd), 1.6667, 1e-4); %!assert (var (t), 0.2489, 1e-4); ## Test input validation ## 'tLocationScaleDistribution' constructor %!error ... %! tLocationScaleDistribution(i, 1, 1) %!error ... %! tLocationScaleDistribution(Inf, 1, 1) %!error ... %! tLocationScaleDistribution([1, 2], 1, 1) %!error ... %! tLocationScaleDistribution("a", 1, 1) %!error ... %! tLocationScaleDistribution(NaN, 1, 1) %!error ... %! tLocationScaleDistribution(0, 0, 1) %!error ... %! tLocationScaleDistribution(0, -1, 1) %!error ... %! tLocationScaleDistribution(0, Inf, 1) %!error ... %! tLocationScaleDistribution(0, i, 1) %!error ... %! tLocationScaleDistribution(0, "a", 1) %!error ... %! tLocationScaleDistribution(0, [1, 2], 1) %!error ... %! tLocationScaleDistribution(0, NaN, 1) %!error ... %! tLocationScaleDistribution(0, 1, 0) %!error ... %! tLocationScaleDistribution(0, 1, -1) %!error ... %! tLocationScaleDistribution(0, 1, Inf) %!error ... %! tLocationScaleDistribution(0, 1, i) %!error ... %! tLocationScaleDistribution(0, 1, "a") %!error ... %! tLocationScaleDistribution(0, 1, [1, 2]) %!error ... %! tLocationScaleDistribution(0, 1, NaN) ## 'cdf' method %!error ... %! cdf (tLocationScaleDistribution, 2, "uper") %!error ... %! cdf (tLocationScaleDistribution, 2, 3) ## 'paramci' method %!shared x %! x = tlsrnd (0, 1, 1, [1, 100]); %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha") %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", 0) %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", 1) %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", [0.5 2]) %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", "") %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", {0.05}) %!error ... %! paramci (tLocationScaleDistribution.fit (x), "parameter", "mu", ... %! "alpha", {0.05}) %!error ... %! paramci (tLocationScaleDistribution.fit (x), ... %! "parameter", {"mu", "sigma", "nu", "param"}) %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", 0.01, ... %! "parameter", {"mu", "sigma", "nu", "param"}) %!error ... %! paramci (tLocationScaleDistribution.fit (x), "parameter", "param") %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "param") %!error ... %! paramci (tLocationScaleDistribution.fit (x), "NAME", "value") %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", 0.01, "NAME", "value") %!error ... %! paramci (tLocationScaleDistribution.fit (x), "alpha", 0.01, ... %! "parameter", "mu", "NAME", "value") ## 'plot' method %!error ... %! plot (tLocationScaleDistribution, "Parent") %!error ... %! plot (tLocationScaleDistribution, "PlotType", 12) %!error ... %! plot (tLocationScaleDistribution, "PlotType", {"pdf", "cdf"}) %!error ... %! plot (tLocationScaleDistribution, "PlotType", "pdfcdf") %!error ... %! plot (tLocationScaleDistribution, "Discrete", "pdfcdf") %!error ... %! plot (tLocationScaleDistribution, "Discrete", [1, 0]) %!error ... %! plot (tLocationScaleDistribution, "Discrete", {true}) %!error ... %! plot (tLocationScaleDistribution, "Parent", 12) %!error ... %! plot (tLocationScaleDistribution, "Parent", "hax") %!error ... %! plot (tLocationScaleDistribution, "invalidNAME", "pdf") %!error ... %! plot (tLocationScaleDistribution, "PlotType", "probability") ## 'proflik' method %!error ... %! proflik (tLocationScaleDistribution, 2) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 4) %!error ... %! proflik (tLocationScaleDistribution.fit (x), [1, 2]) %!error ... %! proflik (tLocationScaleDistribution.fit (x), {1}) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, ones (2)) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "Display") %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "Display", 1) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "Display", {1}) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "Display", {"on"}) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "Display", ["on"; "on"]) %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "Display", "onnn") %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, "NAME", "on") %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, {"NAME"}, "on") %!error ... %! proflik (tLocationScaleDistribution.fit (x), 1, {[1 2 3 4]}, "Display", "on") ## 'truncate' method %!error ... %! truncate (tLocationScaleDistribution) %!error ... %! truncate (tLocationScaleDistribution, 2) %!error ... %! truncate (tLocationScaleDistribution, 4, 2) ## Catch errors when using array of probability objects with available methods %!shared pd %! pd = tLocationScaleDistribution (0, 1, 1); %! pd(2) = tLocationScaleDistribution (0, 1, 3); %!error cdf (pd, 1) %!error icdf (pd, 0.5) %!error iqr (pd) %!error mean (pd) %!error median (pd) %!error negloglik (pd) %!error paramci (pd) %!error pdf (pd, 1) %!error plot (pd) %!error proflik (pd, 2) %!error random (pd) %!error std (pd) %!error ... %! truncate (pd, 2, 4) %!error var (pd) statistics-release-1.7.3/inst/dist_stat/000077500000000000000000000000001475240274700203215ustar00rootroot00000000000000statistics-release-1.7.3/inst/dist_stat/betastat.m000066400000000000000000000101011475240274700222770ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} betastat (@var{a}, @var{b}) ## ## Compute statistics of the Beta distribution. ## ## @code{[@var{m}, @var{v}] = betastat (@var{a}, @var{b})} returns the mean ## and variance of the Beta distribution with shape parameters @var{a} and ## @var{b}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the Beta distribution can be found at ## @url{https://en.wikipedia.org/wiki/Beta_distribution} ## ## @seealso{betacdf, betainv, betapdf, betarnd, betafit, betalike} ## @end deftypefn function [m, v] = betastat (a, b) ## Check for valid number of input arguments if (nargin < 2) error ("betastat: function called with too few input arguments."); endif ## Check for A and B being numeric if (! (isnumeric (a) && isnumeric (b))) error ("betastat: A and B must be numeric."); endif ## Check for A and B being real if (iscomplex (a) || iscomplex (b)) error ("betastat: A and B must not be complex."); endif ## Check for common size of A and B if (! isscalar (a) || ! isscalar (b)) [retval, a, b] = common_size (a, b); if (retval > 0) error ("betastat: A and B must be of common size or scalars."); endif endif ## Catch invalid parameters k = find (! (a > 0 & b > 0)); ## Calculate moments a_b = a + b; m = a ./ (a_b); m(k) = NaN; if (nargout > 1) v = (a .* b) ./ ((a_b .^ 2) .* (a_b + 1)); v(k) = NaN; endif endfunction ## Input validation tests %!error betastat () %!error betastat (1) %!error betastat ({}, 2) %!error betastat (1, "") %!error betastat (i, 2) %!error betastat (1, i) %!error ... %! betastat (ones (3), ones (2)) %!error ... %! betastat (ones (2), ones (3)) ## Output validation tests %!test %! a = -2:6; %! b = 0.4:0.2:2; %! [m, v] = betastat (a, b); %! expected_m = [NaN NaN NaN 1/2 2/3.2 3/4.4 4/5.6 5/6.8 6/8]; %! expected_v = [NaN NaN NaN 0.0833, 0.0558, 0.0402, 0.0309, 0.0250, 0.0208]; %! assert (m, expected_m, eps*100); %! assert (v, expected_v, 0.001); %!test %! a = -2:1:6; %! [m, v] = betastat (a, 1.5); %! expected_m = [NaN NaN NaN 1/2.5 2/3.5 3/4.5 4/5.5 5/6.5 6/7.5]; %! expected_v = [NaN NaN NaN 0.0686, 0.0544, 0.0404, 0.0305, 0.0237, 0.0188]; %! assert (m, expected_m); %! assert (v, expected_v, 0.001); %!test %! a = [14 Inf 10 NaN 10]; %! b = [12 9 NaN Inf 12]; %! [m, v] = betastat (a, b); %! expected_m = [14/26 NaN NaN NaN 10/22]; %! expected_v = [168/18252 NaN NaN NaN 120/11132]; %! assert (m, expected_m); %! assert (v, expected_v); %!assert (nthargout (1:2, @betastat, 5, []), {[], []}) %!assert (nthargout (1:2, @betastat, [], 5), {[], []}) %!assert (size (betastat (rand (10, 5, 4), rand (10, 5, 4))), [10 5 4]) %!assert (size (betastat (rand (10, 5, 4), 7)), [10 5 4]) statistics-release-1.7.3/inst/dist_stat/binostat.m000066400000000000000000000103021475240274700223160ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2015 Carnë Draug ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} binostat (@var{n}, @var{ps}) ## ## Compute statistics of the binomial distribution. ## ## @code{[@var{m}, @var{v}] = binostat (@var{n}, @var{ps})} returns the mean and ## variance of the binomial distribution with parameters @var{n} and @var{ps}, ## where @var{n} is the number of trials and @var{ps} is the probability of ## success. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Binomial_distribution} ## ## @seealso{binocdf, binoinv, binopdf, binornd, binofit, binolike, binotest} ## @end deftypefn function [m, v] = binostat (n, ps) ## Check for valid number of input arguments if (nargin < 2) error ("binostat: function called with too few input arguments."); endif ## Check for N and PS being numeric if (! (isnumeric (n) && isnumeric (ps))) error ("binostat: N and PS must be numeric."); endif ## Check for N and PS being real if (iscomplex (n) || iscomplex (ps)) error ("binostat: N and PS must not be complex."); endif ## Check for common size of N and PS if (! isscalar (n) || ! isscalar (ps)) [retval, n, ps] = common_size (n, ps); if (retval > 0) error ("binostat: N and PS must be of common size or scalars."); endif endif ## Catch invalid parameters k = find (! (n > 0 & fix (n) == n & ps >= 0 & ps <= 1)); ## Calculate moments m = n .* ps; m(k) = NaN; if (nargout > 1) v = m .* (1 - ps); v(k) = NaN; endif endfunction ## Input validation tests %!error binostat () %!error binostat (1) %!error binostat ({}, 2) %!error binostat (1, "") %!error binostat (i, 2) %!error binostat (1, i) %!error ... %! binostat (ones (3), ones (2)) %!error ... %! binostat (ones (2), ones (3)) ## Output validation tests %!test %! n = 1:6; %! ps = 0:0.2:1; %! [m, v] = binostat (n, ps); %! expected_m = [0.00, 0.40, 1.20, 2.40, 4.00, 6.00]; %! expected_v = [0.00, 0.32, 0.72, 0.96, 0.80, 0.00]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! n = 1:6; %! [m, v] = binostat (n, 0.5); %! expected_m = [0.50, 1.00, 1.50, 2.00, 2.50, 3.00]; %! expected_v = [0.25, 0.50, 0.75, 1.00, 1.25, 1.50]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! n = [-Inf -3 5 0.5 3 NaN 100, Inf]; %! [m, v] = binostat (n, 0.5); %! assert (isnan (m), [true true false true false true false false]) %! assert (isnan (v), [true true false true false true false false]) %! assert (m(end), Inf); %! assert (v(end), Inf); %!assert (nthargout (1:2, @binostat, 5, []), {[], []}) %!assert (nthargout (1:2, @binostat, [], 5), {[], []}) %!assert (size (binostat (randi (100, 10, 5, 4), rand (10, 5, 4))), [10 5 4]) %!assert (size (binostat (randi (100, 10, 5, 4), 7)), [10 5 4]) statistics-release-1.7.3/inst/dist_stat/bisastat.m000066400000000000000000000074671475240274700223270ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} bisastat (@var{beta}, @var{gamma}) ## ## Compute statistics of the Birnbaum-Saunders distribution. ## ## @code{[@var{m}, @var{v}] = bisastat (@var{beta}, @var{gamma})} returns the ## mean and variance of the Birnbaum-Saunders distribution with scale parameter ## @var{beta} and shape parameter @var{gamma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the Birnbaum-Saunders distribution can be found at ## @url{https://en.wikipedia.org/wiki/Birnbaum%E2%80%93Saunders_distribution} ## ## @seealso{bisacdf, bisainv, bisapdf, bisarnd, bisafit, bisalike} ## @end deftypefn function [m, v] = bisastat (beta, gamma) ## Check for valid number of input arguments if (nargin < 2) error ("bisastat: function called with too few input arguments."); endif ## Check for BETA and GAMMA being numeric if (! (isnumeric (beta) && isnumeric (gamma))) error ("bisastat: BETA and GAMMA must be numeric."); endif ## Check for BETA and GAMMA being real if (iscomplex (beta) || iscomplex (gamma)) error ("bisastat: BETA and GAMMA must not be complex."); endif ## Check for common size of BETA and GAMMA if (! isscalar (beta) || ! isscalar (gamma)) [retval, beta, gamma] = common_size (beta, gamma); if (retval > 0) error ("bisastat: BETA and GAMMA must be of common size or scalars."); endif endif ## Calculate moments m = beta .* (1 + ((gamma .^ 2) ./ 2)); v = ((beta .* gamma) .^ 2) .* (1 + ((5 .* (gamma .^ 2)) ./ 4)); ## Continue argument check beta = find (! (beta > 0) | ! (beta < Inf) | ! (gamma > 0) | ! (gamma < Inf)); if (any (beta)) m(beta) = NaN; v(beta) = NaN; endif endfunction ## Input validation tests %!error bisastat () %!error bisastat (1) %!error bisastat ({}, 2) %!error bisastat (1, "") %!error bisastat (i, 2) %!error bisastat (1, i) %!error ... %! bisastat (ones (3), ones (2)) %!error ... %! bisastat (ones (2), ones (3)) ## Output validation tests %!test %! beta = 1:6; %! gamma = 1:0.2:2; %! [m, v] = bisastat (beta, gamma); %! expected_m = [1.50, 3.44, 5.94, 9.12, 13.10, 18]; %! expected_v = [2.25, 16.128, 60.858, 172.032, 409.050, 864]; %! assert (m, expected_m, 1e-2); %! assert (v, expected_v, 1e-3); %!test %! beta = 1:6; %! [m, v] = bisastat (beta, 1.5); %! expected_m = [2.125, 4.25, 6.375, 8.5, 10.625, 12.75]; %! expected_v = [8.5781, 34.3125, 77.2031, 137.2500, 214.4531, 308.8125]; %! assert (m, expected_m, 1e-3); %! assert (v, expected_v, 1e-4); statistics-release-1.7.3/inst/dist_stat/burrstat.m000066400000000000000000000105151475240274700223470ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} burrstat (@var{lambda}, @var{c}, @var{k}) ## ## Compute statistics of the Burr type XII distribution. ## ## @code{[@var{m}, @var{v}] = burrstat (@var{lambda}, @var{c}, @var{k})} returns ## the mean and variance of the Burr type XII distribution with scale parameter ## @var{lambda}, first shape parameter @var{c}, and second shape parameter ## @var{k}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## Further information about the Burr distribution can be found at ## @url{https://en.wikipedia.org/wiki/Burr_distribution} ## ## @seealso{gevcdf, gevinv, gevpdf, gevrnd, gevfit, gevlike} ## @end deftypefn function [m, v] = burrstat (lambda, c, k) ## Check for is_val number of input arguments if (nargin < 3) error ("burrstat: function called with too few input arguments."); endif ## Check for LAMBDA, C, and K being numeric if (! (isnumeric (lambda) && isnumeric (c) && isnumeric (k))) error ("burrstat: LAMBDA, C, and K must be numeric."); endif ## Check for LAMBDA, C, and K being real if (iscomplex (lambda) || iscomplex (c) || iscomplex (k)) error ("burrstat: LAMBDA, C, and K must not be complex."); endif ## Check for common size of LAMBDA, C, and K if (! isscalar (lambda) || ! isscalar (c) || ! isscalar (k)) [retval, lambda, c, k] = common_size (lambda, c, k); if (retval > 0) error ("burrstat: LAMBDA, C, and K must be of common size or scalars."); endif endif ## Preallocate mean annd variance m = v = nan (size (lambda)); ## Precalculate some values c_i = 1 ./ c; l_c = lambda .* c_i; kci = k - c_i; ## Find valid vases c_k = c .* k; is_val = lambda > 0 & c > 0 & k > 0; ## Calculate 1st moment is_inf = is_val & c_k <= 1; m(is_inf) = Inf; no_inf = ! is_inf; m(no_inf) = l_c(no_inf) .* beta (c_i(no_inf), kci(no_inf)); ## Calculate 2nd moment is_inf = is_val & c_k <= 2; v(is_inf) = Inf; no_inf = ! is_inf; v(no_inf) = 2 * lambda(no_inf) .* l_c(no_inf) .* ... beta (c_i(no_inf) .* 2, kci(no_inf) - c_i(no_inf)) ... - m(no_inf) .^ 2; endfunction ## Input validation tests %!error burrstat () %!error burrstat (1) %!error burrstat (1, 2) %!error burrstat ({}, 2, 3) %!error burrstat (1, "", 3) %!error burrstat (1, 2, "") %!error burrstat (i, 2, 3) %!error burrstat (1, i, 3) %!error burrstat (1, 2, i) %!error ... %! burrstat (ones (3), ones (2), 3) %!error ... %! burrstat (ones (2), 2, ones (3)) %!error ... %! burrstat (1, ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = burrstat (1, 2, 5); %! assert (m, 0.4295, 1e-4); %! assert (v, 0.0655, 1e-4); %!test %! [m, v] = burrstat (1, 1, 1); %! assert (m, Inf); %! assert (v, Inf); %!test %! [m, v] = burrstat (2, 4, 1); %! assert (m, 2.2214, 1e-4); %! assert (v, 1.3484, 1e-4); statistics-release-1.7.3/inst/dist_stat/chi2stat.m000066400000000000000000000046041475240274700222240ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} chi2stat (@var{df}) ## ## Compute statistics of the chi-squared distribution. ## ## @code{[@var{m}, @var{v}] = chi2stat (@var{df})} returns the mean and ## variance of the chi-squared distribution with @var{df} degrees of freedom. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## Further information about the chi-squared distribution can be found at ## @url{https://en.wikipedia.org/wiki/Chi-squared_distribution} ## ## @seealso{chi2cdf, chi2inv, chi2pdf, chi2rnd} ## @end deftypefn function [m, v] = chi2stat (df) ## Check for valid number of input arguments if (nargin < 1) error ("chi2stat: function called with too few input arguments."); endif ## Check for DF being numeric if (! isnumeric (df)) error ("chi2stat: DF must be numeric."); endif ## Check for DF being real if (iscomplex (df)) error ("chi2stat: DF must not be complex."); endif ## Calculate moments m = df; v = 2 .* df; ## Continue argument check k = find (! (df > 0) | ! (df < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error chi2stat () %!error chi2stat ({}) %!error chi2stat ("") %!error chi2stat (i) ## Output validation tests %!test %! df = 1:6; %! [m, v] = chi2stat (df); %! assert (m, df); %! assert (v, [2, 4, 6, 8, 10, 12], 0.001); statistics-release-1.7.3/inst/dist_stat/evstat.m000066400000000000000000000073641475240274700220170ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} evstat (@var{mu}, @var{sigma}) ## ## Compute statistics of the extreme value distribution. ## ## @code{[@var{m}, @var{v}] = evstat (@var{mu}, @var{sigma})} returns the mean ## and variance of the extreme value distribution (also known as the Gumbel ## or the type I generalized extreme value distribution) with location parameter ## @var{mu} and scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## The type 1 extreme value distribution is also known as the Gumbel ## distribution. This version is suitable for modeling minima. The mirror image ## of this distribution can be used to model maxima by negating @var{x}. If ## @var{y} has a Weibull distribution, then @code{@var{x} = log (@var{y})} has ## the type 1 extreme value distribution. ## ## Further information about the Gumbel distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gumbel_distribution} ## ## @seealso{evcdf, evinv, evpdf, evrnd, evfit, evlike} ## @end deftypefn function [m, v] = evstat (mu, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("evstat: function called with too few input arguments."); endif ## Check for MU and SIGMA being numeric if (! (isnumeric (mu) && isnumeric (sigma))) error ("evstat: MU and SIGMA must be numeric."); endif ## Check for MU and SIGMA being real if (iscomplex (mu) || iscomplex (sigma)) error ("evstat: MU and SIGMA must not be complex."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("evstat: MU and SIGMA must be of common size or scalars."); endif endif ## Return NaNs for out of range values of SIGMA sigma(sigma <= 0) = NaN; ## Calculate mean and variance m = mu + psi(1) .* sigma; v = (pi .* sigma) .^ 2 ./ 6; endfunction ## Input validation tests %!error evstat () %!error evstat (1) %!error evstat ({}, 2) %!error evstat (1, "") %!error evstat (i, 2) %!error evstat (1, i) %!error ... %! evstat (ones (3), ones (2)) %!error ... %! evstat (ones (2), ones (3)) ## Output validation tests %!shared x, y0, y1 %! x = [-5, 0, 1, 2, 3]; %! y0 = [NaN, NaN, 0.4228, 0.8456, 1.2684]; %! y1 = [-5.5772, -3.4633, -3.0405, -2.6177, -2.1949]; %!assert (evstat (x, x), y0, 1e-4) %!assert (evstat (x, x+6), y1, 1e-4) %!assert (evstat (x, x-6), NaN (1,5)) statistics-release-1.7.3/inst/dist_stat/expstat.m000066400000000000000000000053371475240274700221770ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} expstat (@var{mu}) ## ## Compute statistics of the exponential distribution. ## ## @code{[@var{m}, @var{v}] = expstat (@var{mu})} returns the mean and ## variance of the exponential distribution with mean parameter @var{mu}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## A common alternative parameterization of the exponential distribution is to ## use the parameter @math{λ} defined as the mean number of events in an ## interval as opposed to the parameter @math{μ}, which is the mean wait time ## for an event to occur. @math{λ} and @math{μ} are reciprocals, ## i.e. @math{μ = 1 / λ}. ## ## Further information about the exponential distribution can be found at ## @url{https://en.wikipedia.org/wiki/Exponential_distribution} ## ## @seealso{expcdf, expinv, exppdf, exprnd, expfit, explike} ## @end deftypefn function [m, v] = expstat (mu) ## Check for valid number of input arguments if (nargin < 1) error ("expstat: function called with too few input arguments."); endif ## Check for MU being numeric if (! isnumeric (mu)) error ("expstat: MU must be numeric."); endif ## Check for MU being real if (iscomplex (mu)) error ("expstat: MU must not be complex."); endif ## Calculate moments m = mu; v = m .^ 2; ## Continue argument check k = find (! (mu > 0) | ! (mu < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error expstat () %!error expstat ({}) %!error expstat ("") %!error expstat (i) ## Output validation tests %!test %! mu = 1:6; %! [m, v] = expstat (mu); %! assert (m, [1, 2, 3, 4, 5, 6], 0.001); %! assert (v, [1, 4, 9, 16, 25, 36], 0.001); statistics-release-1.7.3/inst/dist_stat/fstat.m000066400000000000000000000073231475240274700216250ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} fstat (@var{df1}, @var{df2}) ## ## Compute statistics of the @math{F}-distribution. ## ## @code{[@var{m}, @var{v}] = fstat (@var{df1}, @var{df2})} returns the mean and ## variance of the @math{F}-distribution with @var{df1} and @var{df2} degrees ## of freedom. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the @math{F}-distribution can be found at ## @url{https://en.wikipedia.org/wiki/F-distribution} ## ## @seealso{fcdf, finv, fpdf, frnd} ## @end deftypefn function [m, v] = fstat (df1, df2) ## Check for valid number of input arguments if (nargin < 2) error ("fstat: function called with too few input arguments."); endif ## Check for DF1 and DF2 being numeric if (! (isnumeric (df1) && isnumeric (df2))) error ("fstat: DF1 and DF2 must be numeric."); endif ## Check for DF1 and DF2 being real if (iscomplex (df1) || iscomplex (df2)) error ("fstat: DF1 and DF2 must not be complex."); endif ## Check for common size of DF1 and DF2 if (! isscalar (df1) || ! isscalar (df2)) [retval, df1, df2] = common_size (df1, df2); if (retval > 0) error ("fstat: DF1 and DF2 must be of common size or scalars."); endif endif ## Calculate moments m = df2 ./ (df2 - 2); v = (2 .* (df2 .^ 2) .* (df1 + df2 - 2)) ./ ... (df1 .* ((df2 - 2) .^ 2) .* (df2 - 4)); ## Continue argument check k = find (! (df1 > 0) | ! (df1 < Inf) | ! (df2 > 2) | ! (df2 < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif k = find (! (df2 > 4)); if (any (k)) v(k) = NaN; endif endfunction ## Input validation tests %!error fstat () %!error fstat (1) %!error fstat ({}, 2) %!error fstat (1, "") %!error fstat (i, 2) %!error fstat (1, i) %!error ... %! fstat (ones (3), ones (2)) %!error ... %! fstat (ones (2), ones (3)) ## Output validation tests %!test %! df1 = 1:6; %! df2 = 5:10; %! [m, v] = fstat (df1, df2); %! expected_mn = [1.6667, 1.5000, 1.4000, 1.3333, 1.2857, 1.2500]; %! expected_v = [22.2222, 6.7500, 3.4844, 2.2222, 1.5869, 1.2153]; %! assert (m, expected_mn, 0.001); %! assert (v, expected_v, 0.001); %!test %! df1 = 1:6; %! [m, v] = fstat (df1, 5); %! expected_mn = [1.6667, 1.6667, 1.6667, 1.6667, 1.6667, 1.6667]; %! expected_v = [22.2222, 13.8889, 11.1111, 9.7222, 8.8889, 8.3333]; %! assert (m, expected_mn, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/gamstat.m000066400000000000000000000077301475240274700221460ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} gamstat (@var{a}, @var{b}) ## ## Compute statistics of the Gamma distribution. ## ## @code{[@var{m}, @var{v}] = gamstat (@var{a}, @var{b})} returns the mean ## and variance of the Gamma distribution with shape parameter @var{a} and ## scale parameter @var{b}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## OCTAVE/MATLAB use the alternative parameterization given by the pair ## @math{α, β}, i.e. shape @var{a} and scale @var{b}. In Wikipedia, the two ## common parameterizations use the pairs @math{k, θ}, as shape and scale, and ## @math{α, β}, as shape and rate, respectively. The parameter names @var{a} ## and @var{b} used here (for MATLAB compatibility) correspond to the parameter ## notation @math{k, θ} instead of the @math{α, β} as reported in Wikipedia. ## ## Further information about the Gamma distribution can be found at ## @url{https://en.wikipedia.org/wiki/Gamma_distribution} ## ## @seealso{gamcdf, gaminv, gampdf, gamrnd, gamfit, gamlike} ## @end deftypefn function [m, v] = gamstat (a, b) ## Check for valid number of input arguments if (nargin < 2) error ("gamstat: function called with too few input arguments."); endif ## Check for A and B being numeric if (! (isnumeric (a) && isnumeric (b))) error ("gamstat: A and B must be numeric."); endif ## Check for A and B being real if (iscomplex (a) || iscomplex (b)) error ("gamstat: A and B must not be complex."); endif ## Check for common size of A and B if (! isscalar (a) || ! isscalar (b)) [retval, a, b] = common_size (a, b); if (retval > 0) error ("gamstat: A and B must be of common size or scalars."); endif endif ## Calculate moments m = a .* b; v = a .* (b .^ 2); ## Continue argument check a = find (! (a > 0) | ! (a < Inf) | ! (b > 0) | ! (b < Inf)); if (any (a)) m(a) = NaN; v(a) = NaN; endif endfunction ## Input validation tests %!error gamstat () %!error gamstat (1) %!error gamstat ({}, 2) %!error gamstat (1, "") %!error gamstat (i, 2) %!error gamstat (1, i) %!error ... %! gamstat (ones (3), ones (2)) %!error ... %! gamstat (ones (2), ones (3)) ## Output validation tests %!test %! a = 1:6; %! b = 1:0.2:2; %! [m, v] = gamstat (a, b); %! expected_m = [1.00, 2.40, 4.20, 6.40, 9.00, 12.00]; %! expected_v = [1.00, 2.88, 5.88, 10.24, 16.20, 24.00]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! a = 1:6; %! [m, v] = gamstat (a, 1.5); %! expected_m = [1.50, 3.00, 4.50, 6.00, 7.50, 9.00]; %! expected_v = [2.25, 4.50, 6.75, 9.00, 11.25, 13.50]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/geostat.m000066400000000000000000000046731475240274700221570ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} geostat (@var{ps}) ## ## Compute statistics of the geometric distribution. ## ## @code{[@var{m}, @var{v}] = geostat (@var{ps})} returns the mean and ## variance of the geometric distribution with probability of success parameter ## @var{ps}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## Further information about the geometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Geometric_distribution} ## ## @seealso{geocdf, geoinv, geopdf, geornd, geofit} ## @end deftypefn function [m, v] = geostat (ps) ## Check for valid number of input arguments if (nargin < 1) error ("geostat: function called with too few input arguments."); endif ## Check for PS being numeric if (! isnumeric (ps)) error ("geostat: PS must be numeric."); endif ## Check for PS being real if (iscomplex (ps)) error ("geostat: PS must not be complex."); endif ## Calculate moments q = 1 - ps; m = q ./ ps; v = q ./ (ps .^ 2); ## Continue argument check k = find (! (ps >= 0) | ! (ps <= 1)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error geostat () %!error geostat ({}) %!error geostat ("") %!error geostat (i) ## Output validation tests %!test %! ps = 1 ./ (1:6); %! [m, v] = geostat (ps); %! assert (m, [0, 1, 2, 3, 4, 5], 0.001); %! assert (v, [0, 2, 6, 12, 20, 30], 0.001); statistics-release-1.7.3/inst/dist_stat/gevstat.m000066400000000000000000000113111475240274700221510ustar00rootroot00000000000000## Copyright (C) 2012 Nir Krakauer ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} gevstat (@var{k}, @var{sigma}, @var{mu}) ## ## Compute statistics of the generalized extreme value distribution. ## ## @code{[@var{m}, @var{v}] = gevstat (@var{k}, @var{sigma}, @var{mu})} returns ## the mean and variance of the generalized extreme value distribution with ## shape parameter @var{k}, scale parameter @var{sigma}, and location parameter ## @var{mu}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## The mean of the GEV distribution is not finite when @qcode{@var{k} >= 1}, and ## the variance is not finite when @qcode{@var{k} >= 1/2}. The GEV distribution ## has positive density only for values of @var{x} such that ## @qcode{@var{k} * (@var{x} - @var{mu}) / @var{sigma} > -1}. ## ## Further information about the generalized extreme value distribution can be ## found at ## @url{https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution} ## ## @seealso{gevcdf, gevinv, gevpdf, gevrnd, gevfit, gevlike} ## @end deftypefn function [m, v] = gevstat (k, sigma, mu) ## Check for valid number of input arguments if (nargin < 3) error ("gevstat: function called with too few input arguments."); endif ## Check for K, SIGMA, and MU being numeric if (! (isnumeric (k) && isnumeric (sigma) && isnumeric (mu))) error ("gevstat: K, SIGMA, and MU must be numeric."); endif ## Check for K, SIGMA, and MU being real if (iscomplex (k) || iscomplex (sigma) || iscomplex (mu)) error ("gevstat: K, SIGMA, and MU must not be complex."); endif ## Check for common size of K, SIGMA, and MU if (! isscalar (k) || ! isscalar (sigma) || ! isscalar (mu)) [retval, k, sigma, mu] = common_size (k, sigma, mu); if (retval > 0) error ("gevstat: K, SIGMA, and MU must be of common size or scalars."); endif endif ## Euler-Mascheroni constant eg = 0.57721566490153286; m = v = k; ## Find the mean m(k >= 1) = Inf; m(k == 0) = mu(k == 0) + eg*sigma(k == 0); m(k < 1 & k != 0) = mu(k < 1 & k != 0) + sigma(k < 1 & k != 0) .* ... (gamma(1-k(k < 1 & k != 0)) - 1) ./ k(k < 1 & k != 0); ## Find the variance v(k >= 0.5) = Inf; v(k == 0) = (pi^2 / 6) * sigma(k == 0) .^ 2; v(k < 0.5 & k != 0) = (gamma(1-2*k(k < 0.5 & k != 0)) - ... gamma(1-k(k < 0.5 & k != 0)).^2) .* ... (sigma(k < 0.5 & k != 0) ./ k(k < 0.5 & k != 0)) .^ 2; endfunction ## Input validation tests %!error gevstat () %!error gevstat (1) %!error gevstat (1, 2) %!error gevstat ({}, 2, 3) %!error gevstat (1, "", 3) %!error gevstat (1, 2, "") %!error gevstat (i, 2, 3) %!error gevstat (1, i, 3) %!error gevstat (1, 2, i) %!error ... %! gevstat (ones (3), ones (2), 3) %!error ... %! gevstat (ones (2), 2, ones (3)) %!error ... %! gevstat (1, ones (2), ones (3)) ## Output validation tests %!test %! k = [-1, -0.5, 0, 0.2, 0.4, 0.5, 1]; %! sigma = 2; %! mu = 1; %! [m, v] = gevstat (k, sigma, mu); %! expected_m = [1, 1.4551, 2.1544, 2.6423, 3.4460, 4.0898, Inf]; %! expected_v = [4, 3.4336, 6.5797, 13.3761, 59.3288, Inf, Inf]; %! assert (m, expected_m, -0.001); %! assert (v, expected_v, -0.001); statistics-release-1.7.3/inst/dist_stat/gpstat.m000066400000000000000000000130551475240274700220050ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} gpstat (@var{k}, @var{sigma}, @var{theta}) ## ## Compute statistics of the generalized Pareto distribution. ## ## @code{[@var{m}, @var{v}] = gpstat (@var{k}, @var{sigma}, @var{theta})} ## returns the mean and variance of the generalized Pareto distribution with ## shape parameter @var{k}, scale parameter @var{sigma}, and location parameter ## @var{theta}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## When @var{k} = 0 and @var{theta} = 0, the generalized Pareto distribution is ## equivalent to the exponential distribution. When @code{@var{k} > 0} and ## @code{@var{theta} = @var{sigma} / @var{k}}, the generalized Pareto ## distribution is equivalent to the Pareto distribution. The mean of the ## generalized Pareto distribution is not finite when @code{@var{k} >= 1}, and ## the variance is not finite when @code{@var{k} >= 1/2}. When ## @code{@var{k} >= 0}, the generalized Pareto distribution has positive density ## for @code{@var{x} > @var{theta}}, or, when @code{@var{k} < 0}, for ## @code{0 <= (@var{x} - @var{theta}) / @var{sigma} <= -1 / @var{k}}. ## ## Further information about the generalized Pareto distribution can be found at ## @url{https://en.wikipedia.org/wiki/Generalized_Pareto_distribution} ## ## @seealso{gpcdf, gpinv, gppdf, gprnd, gpfit, gplike} ## @end deftypefn function [m, v] = gpstat (k, sigma, theta) ## Check for valid number of input arguments if (nargin < 3) error ("gpstat: function called with too few input arguments."); endif ## Check for K, SIGMA, and MU being numeric if (! (isnumeric (k) && isnumeric (sigma) && isnumeric (theta))) error ("gpstat: K, SIGMA, and MU must be numeric."); endif ## Check for K, SIGMA, and MU being real if (iscomplex (k) || iscomplex (sigma) || iscomplex (theta)) error ("gpstat: K, SIGMA, and MU must not be complex."); endif ## Check for common size of K, SIGMA, and MU if (! isscalar (k) || ! isscalar (sigma) || ! isscalar (theta)) [retval, k, sigma, theta] = common_size (k, sigma, theta); if (retval > 0) error ("gpstat: K, SIGMA, and MU must be of common size or scalars."); endif endif ## Return NaNs for out of range SCALE parameters. sigma(sigma <= 0) = NaN; ## Check for appropriate class if (isa (k, "single") || isa (sigma, "single") || isa (theta, "single")); is_class = "single"; else is_class = "double"; endif ## Prepare output m = NaN (size (k), is_class); v = NaN (size (k), is_class); ## Compute cases for SHAPE == 0 knot0 = (abs (k) < eps (is_class)); m(knot0) = 1; v(knot0) = 1; ## Compute cases for SHAPE != 0 knot0 = ! knot0; ## SHAPE < 1 kless = knot0 & (k < 1); m(kless) = 1 ./ (1 - k(kless)); ## SHAPE > 1 m(k >= 1) = Inf; ## SHAPE < 1/2 ## Find the k~=0 cases and fill in the variance. kless = knot0 & (k < 1/2); v(kless) = 1 ./ ((1-k(kless)).^2 .* (1-2.*k(kless))); ## SHAPE > 1/2 v(k >= 1/2) = Inf; ## Compute mean and variance m = theta + sigma .* m; v = sigma .^ 2 .* v; endfunction ## Input validation tests %!error gpstat () %!error gpstat (1) %!error gpstat (1, 2) %!error gpstat ({}, 2, 3) %!error gpstat (1, "", 3) %!error gpstat (1, 2, "") %!error gpstat (i, 2, 3) %!error gpstat (1, i, 3) %!error gpstat (1, 2, i) %!error ... %! gpstat (ones (3), ones (2), 3) %!error ... %! gpstat (ones (2), 2, ones (3)) %!error ... %! gpstat (1, ones (2), ones (3)) ## Output validation tests %!shared x, y %! x = [-Inf, -1, 0, 1/2, 1, Inf]; %! y = [0, 0.5, 1, 2, Inf, Inf]; %!assert (gpstat (x, ones (1,6), zeros (1,6)), y, eps) ## Test class of input preserved %!assert (gpstat (single (x), 1, 0), single (y), eps("single")) %!assert (gpstat (x, single (1), 0), single (y), eps("single")) %!assert (gpstat (x, 1, single (0)), single (y), eps("single")) %!assert (gpstat (single ([x, NaN]), 1, 0), single ([y, NaN]), eps("single")) %!assert (gpstat ([x, NaN], single (1), 0), single ([y, NaN]), eps("single")) %!assert (gpstat ([x, NaN], 1, single (0)), single ([y, NaN]), eps("single")) statistics-release-1.7.3/inst/dist_stat/hnstat.m000066400000000000000000000072731475240274700220110ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} hnstat (@var{mu}, @var{sigma}) ## ## Compute statistics of the half-normal distribution. ## ## @code{[@var{m}, @var{v}] = hnstat (@var{mu}, @var{sigma})} returns the mean ## and variance of the half-normal distribution with non-centrality (distance) ## parameter @var{mu} and scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the half-normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Half-normal_distribution} ## ## @seealso{norminv, norminv, normpdf, normrnd, normfit, normlike} ## @end deftypefn function [m, v] = hnstat (mu, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("hnstat: function called with too few input arguments."); endif ## Check for MU and SIGMA being numeric if (! (isnumeric (mu) && isnumeric (sigma))) error ("hnstat: MU and SIGMA must be numeric."); endif ## Check for MU and SIGMA being real if (iscomplex (mu) || iscomplex (sigma)) error ("hnstat: MU and SIGMA must not be complex."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("hnstat: MU and SIGMA must be of common size or scalars."); endif endif ## Calculate moments m = mu + (sigma .* sqrt (2)) ./ sqrt (pi); v = sigma .^ 2 .* (1 - 2 / pi); ## Continue argument check k = find (! (sigma > 0) | ! (sigma < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error hnstat () %!error hnstat (1) %!error hnstat ({}, 2) %!error hnstat (1, "") %!error hnstat (i, 2) %!error hnstat (1, i) %!error ... %! hnstat (ones (3), ones (2)) %!error ... %! hnstat (ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = hnstat (0, 1); %! assert (m, 0.7979, 1e-4); %! assert (v, 0.3634, 1e-4); %!test %! [m, v] = hnstat (2, 1); %! assert (m, 2.7979, 1e-4); %! assert (v, 0.3634, 1e-4); %!test %! [m, v] = hnstat (2, 2); %! assert (m, 3.5958, 1e-4); %! assert (v, 1.4535, 1e-4); %!test %! [m, v] = hnstat (2, 2.5); %! assert (m, 3.9947, 1e-4); %! assert (v, 2.2711, 1e-4); %!test %! [m, v] = hnstat (1.5, 0.5); %! assert (m, 1.8989, 1e-4); %! assert (v, 0.0908, 1e-4); %!test %! [m, v] = hnstat (-1.5, 0.5); %! assert (m, -1.1011, 1e-4); %! assert (v, 0.0908, 1e-4); statistics-release-1.7.3/inst/dist_stat/hygestat.m000066400000000000000000000113261475240274700223320ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{mn}, @var{v}] =} hygestat (@var{m}, @var{k}, @var{n}) ## ## Compute statistics of the hypergeometric distribution. ## ## @code{[@var{mn}, @var{v}] = hygestat (@var{m}, @var{k}, @var{n})} returns the ## mean and variance of the hypergeometric distribution parameters @var{m}, ## @var{k}, and @var{n}. ## ## @itemize ## @item ## @var{m} is the total size of the population of the hypergeometric ## distribution. The elements of @var{m} must be positive natural numbers. ## ## @item ## @var{k} is the number of marked items of the hypergeometric distribution. ## The elements of @var{k} must be natural numbers. ## ## @item ## @var{n} is the size of the drawn sample of the hypergeometric ## distribution. The elements of @var{n} must be positive natural numbers. ## @end itemize ## ## The size of @var{mn} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the hypergeometric distribution can be found at ## @url{https://en.wikipedia.org/wiki/Hypergeometric_distribution} ## ## @seealso{hygecdf, hygeinv, hygepdf, hygernd} ## @end deftypefn function [mn, v] = hygestat (m, k, n) ## Check for valid number of input arguments if (nargin < 3) error ("hygestat: function called with too few input arguments."); endif ## Check for M, K, and N being numeric if (! (isnumeric (m) && isnumeric (k) && isnumeric (n))) error ("hygestat: M, K, and N must be numeric."); endif ## Check for M, K, and N being real if (iscomplex (m) || iscomplex (k) || iscomplex (n)) error ("hygestat: M, K, and N must not be complex."); endif ## Check for common size of M, K, and N if (! isscalar (m) || ! isscalar (k) || ! isscalar (n)) [retval, m, k, n] = common_size (m, k, n); if (retval > 0) error ("hygestat: M, K, and N must be of common size or scalars."); endif endif ## Calculate moments mn = (n .* k) ./ m; v = (n .* (k ./ m) .* (1 - k ./ m) .* (m - n)) ./ (m - 1); ## Continue argument check is_nan = find (! (m >= 0) | ! (k >= 0) | ! (n > 0) | ! (m == round (m)) | ... ! (k == round (k)) | ! (n == round (n)) | ! (k <= m) | ... ! (n <= m)); if (any (is_nan)) mn(is_nan) = NaN; v(is_nan) = NaN; endif endfunction ## Input validation tests %!error hygestat () %!error hygestat (1) %!error hygestat (1, 2) %!error hygestat ({}, 2, 3) %!error hygestat (1, "", 3) %!error hygestat (1, 2, "") %!error hygestat (i, 2, 3) %!error hygestat (1, i, 3) %!error hygestat (1, 2, i) %!error ... %! hygestat (ones (3), ones (2), 3) %!error ... %! hygestat (ones (2), 2, ones (3)) %!error ... %! hygestat (1, ones (2), ones (3)) ## Output validation tests %!test %! m = 4:9; %! k = 0:5; %! n = 1:6; %! [mn, v] = hygestat (m, k, n); %! expected_mn = [0.0000, 0.4000, 1.0000, 1.7143, 2.5000, 3.3333]; %! expected_v = [0.0000, 0.2400, 0.4000, 0.4898, 0.5357, 0.5556]; %! assert (mn, expected_mn, 0.001); %! assert (v, expected_v, 0.001); %!test %! m = 4:9; %! k = 0:5; %! [mn, v] = hygestat (m, k, 2); %! expected_mn = [0.0000, 0.4000, 0.6667, 0.8571, 1.0000, 1.1111]; %! expected_v = [0.0000, 0.2400, 0.3556, 0.4082, 0.4286, 0.4321]; %! assert (mn, expected_mn, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/invgstat.m000066400000000000000000000067631475240274700223520ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} invgstat (@var{mu}, @var{lambda}) ## ## Compute statistics of the inverse Gaussian distribution. ## ## @code{[@var{m}, @var{v}] = invgstat (@var{mu}, @var{lambda})} returns the ## mean and variance of the inverse Gaussian distribution with mean parameter ## @var{mu} and shape parameter @var{lambda}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the inverse Gaussian distribution can be found at ## @url{https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution} ## ## @seealso{invgcdf, invginv, invgpdf, invgrnd, invgfit, invglike} ## @end deftypefn function [m, v] = invgstat (mu, lambda) ## Check for valid number of input arguments if (nargin < 2) error ("invgstat: function called with too few input arguments."); endif ## Check for MU and LAMBDA being numeric if (! (isnumeric (mu) && isnumeric (lambda))) error ("invgstat: MU and LAMBDA must be numeric."); endif ## Check for MU and LAMBDA being real if (iscomplex (mu) || iscomplex (lambda)) error ("invgstat: MU and LAMBDA must not be complex."); endif ## Check for common size of MU and LAMBDA if (! isscalar (mu) || ! isscalar (lambda)) [retval, mu, lambda] = common_size (mu, lambda); if (retval > 0) error ("invgstat: MU and LAMBDA must be of common size or scalars."); endif endif ## Calculate moments m = mu; v = (mu .^ 3) ./ lambda; ## Continue argument check m(lambda <= 0 | mu <= 0) = NaN; v(lambda <= 0 | mu <= 0) = NaN; endfunction ## Input validation tests %!error invgstat () %!error invgstat (1) %!error invgstat ({}, 2) %!error invgstat (1, "") %!error invgstat (i, 2) %!error invgstat (1, i) %!error ... %! invgstat (ones (3), ones (2)) %!error ... %! invgstat (ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = invgstat (1, 1); %! assert (m, 1); %! assert (v, 1); %!test %! [m, v] = invgstat (2, 1); %! assert (m, 2); %! assert (v, 8); %!test %! [m, v] = invgstat (2, 2); %! assert (m, 2); %! assert (v, 4); %!test %! [m, v] = invgstat (2, 2.5); %! assert (m, 2); %! assert (v, 3.2); %!test %! [m, v] = invgstat (1.5, 0.5); %! assert (m, 1.5); %! assert (v, 6.75); statistics-release-1.7.3/inst/dist_stat/logistat.m000066400000000000000000000067521475240274700223370ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} logistat (@var{mu}, @var{sigma}) ## ## Compute statistics of the logistic distribution. ## ## @code{[@var{m}, @var{v}] = logistat (@var{mu}, @var{sigma})} returns the mean ## and variance of the logistic distribution with mean parameter @var{mu} and ## scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the logistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Logistic_distribution} ## ## @seealso{logicdf, logiinv, logipdf, logirnd, logifit, logilike} ## @end deftypefn function [m, v] = logistat (mu, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("logistat: function called with too few input arguments."); endif ## Check for MU and SIGMA being numeric if (! (isnumeric (mu) && isnumeric (sigma))) error ("logistat: MU and SIGMA must be numeric."); endif ## Check for MU and SIGMA being real if (iscomplex (mu) || iscomplex (sigma)) error ("logistat: MU and SIGMA must not be complex."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("logistat: MU and SIGMA must be of common size or scalars."); endif endif ## Calculate moments m = mu; v = (sigma .^ 2 .* pi .^ 2) ./ 3; ## Continue argument check m(sigma <= 0) = NaN; v(sigma <= 0) = NaN; endfunction ## Input validation tests %!error logistat () %!error logistat (1) %!error logistat ({}, 2) %!error logistat (1, "") %!error logistat (i, 2) %!error logistat (1, i) %!error ... %! logistat (ones (3), ones (2)) %!error ... %! logistat (ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = logistat (0, 1); %! assert (m, 0); %! assert (v, 3.2899, 0.001); %!test %! [m, v] = logistat (0, 0.8); %! assert (m, 0); %! assert (v, 2.1055, 0.001); %!test %! [m, v] = logistat (1, 0.6); %! assert (m, 1); %! assert (v, 1.1844, 0.001); %!test %! [m, v] = logistat (0, 0.4); %! assert (m, 0); %! assert (v, 0.5264, 0.001); %!test %! [m, v] = logistat (-1, 0.2); %! assert (m, -1); %! assert (v, 0.1316, 0.001); statistics-release-1.7.3/inst/dist_stat/loglstat.m000066400000000000000000000100721475240274700223300ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} loglstat (@var{mu}, @var{sigma}) ## ## Compute statistics of the loglogistic distribution. ## ## @code{[@var{m}, @var{v}] = loglstat (@var{mu}, @var{sigma})} returns the mean ## and variance of the loglogistic distribution with mean parameter @var{mu} and ## scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the loglogistic distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-logistic_distribution} ## ## OCTAVE/MATLAB use an alternative parameterization given by the pair ## @math{μ, σ}, i.e. @var{mu} and @var{sigma}, in analogy with the logistic ## distribution. Their relation to the @math{α} and @math{b} parameters used ## in Wikipedia are given below: ## ## @itemize ## @item @qcode{@var{mu} = log (@var{a})} ## @item @qcode{@var{sigma} = 1 / @var{a}} ## @end itemize ## ## @seealso{logncdf, logninv, lognpdf, lognrnd, lognfit, lognlike} ## @end deftypefn function [m, v] = loglstat (mu, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("loglstat: function called with too few input arguments."); endif ## Check for MU and SIGMA being numeric if (! (isnumeric (mu) && isnumeric (sigma))) error ("loglstat: MU and SIGMA must be numeric."); endif ## Check for MU and SIGMA being real if (iscomplex (mu) || iscomplex (sigma)) error ("loglstat: MU and SIGMA must not be complex."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("loglstat: MU and SIGMA must be of common size or scalars."); endif endif ## Calculate moments pib = pi .* sigma; m = (exp (mu) .* pib) ./ sin (pib); pib2 = 2 * pib; v = exp (mu) .^ 2 .* (pib2 ./ sin (pib2) - pib .^ 2 ./ sin (pib) .^ 2); ## Continue argument check m(sigma >= 1) = Inf; v(sigma >= 0.5) = Inf; m(sigma <= 0) = NaN; v(sigma <= 0) = NaN; endfunction ## Input validation tests %!error loglstat () %!error loglstat (1) %!error loglstat ({}, 2) %!error loglstat (1, "") %!error loglstat (i, 2) %!error loglstat (1, i) %!error ... %! loglstat (ones (3), ones (2)) %!error ... %! loglstat (ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = loglstat (0, 1); %! assert (m, Inf, 0.001); %! assert (v, Inf, 0.001); %!test %! [m, v] = loglstat (0, 0.8); %! assert (m, 4.2758, 0.001); %! assert (v, Inf, 0.001); %!test %! [m, v] = loglstat (0, 0.6); %! assert (m, 1.9820, 0.001); %! assert (v, Inf, 0.001); %!test %! [m, v] = loglstat (0, 0.4); %! assert (m, 1.3213, 0.001); %! assert (v, 2.5300, 0.001); %!test %! [m, v] = loglstat (0, 0.2); %! assert (m, 1.0690, 0.001); %! assert (v, 0.1786, 0.001); statistics-release-1.7.3/inst/dist_stat/lognstat.m000066400000000000000000000075321475240274700223410ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} lognstat (@var{mu}, @var{sigma}) ## ## Compute statistics of the lognormal distribution. ## ## @code{[@var{m}, @var{v}] = lognstat (@var{mu}, @var{sigma})} returns the mean ## and variance of the lognormal distribution with mean parameter @var{mu} and ## standard deviation parameter @var{sigma}, each corresponding to the ## associated normal distribution. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the lognormal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Log-normal_distribution} ## ## @seealso{logncdf, logninv, lognpdf, lognrnd, lognfit, lognlike} ## @end deftypefn function [m, v] = lognstat (mu, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("lognstat: function called with too few input arguments."); endif ## Check for MU and SIGMA being numeric if (! (isnumeric (mu) && isnumeric (sigma))) error ("lognstat: MU and SIGMA must be numeric."); endif ## Check for MU and SIGMA being real if (iscomplex (mu) || iscomplex (sigma)) error ("lognstat: MU and SIGMA must not be complex."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("lognstat: MU and SIGMA must be of common size or scalars."); endif endif ## Calculate moments m = exp (mu + (sigma .^ 2) ./ 2); v = (exp (sigma .^ 2) - 1) .* exp (2 .* mu + sigma .^ 2); ## Continue argument check k = find (! (sigma >= 0) | ! (sigma < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error lognstat () %!error lognstat (1) %!error lognstat ({}, 2) %!error lognstat (1, "") %!error lognstat (i, 2) %!error lognstat (1, i) %!error ... %! lognstat (ones (3), ones (2)) %!error ... %! lognstat (ones (2), ones (3)) ## Output validation tests %!test %! mu = 0:0.2:1; %! sigma = 0.2:0.2:1.2; %! [m, v] = lognstat (mu, sigma); %! expected_m = [1.0202, 1.3231, 1.7860, 2.5093, 3.6693, 5.5845]; %! expected_v = [0.0425, 0.3038, 1.3823, 5.6447, 23.1345, 100.4437]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! sigma = 0.2:0.2:1.2; %! [m, v] = lognstat (0, sigma); %! expected_m = [1.0202, 1.0833, 1.1972, 1.3771, 1.6487, 2.0544]; %! expected_v = [0.0425, 0.2036, 0.6211, 1.7002, 4.6708, 13.5936]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/nakastat.m000066400000000000000000000067351475240274700223200ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} nakastat (@var{mu}, @var{omega}) ## ## Compute statistics of the Nakagami distribution. ## ## @code{[@var{m}, @var{v}] = nakastat (@var{mu}, @var{omega})} returns the mean ## and variance of the Nakagami distribution with shape parameter @var{mu} and ## spread parameter @var{omega}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the Nakagami distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{nakacdf, nakainv, nakapdf, nakarnd, nakafit, nakalike} ## @end deftypefn function [m, v] = nakastat (mu, omega) ## Check for valid number of input arguments if (nargin < 2) error ("nakastat: function called with too few input arguments."); endif ## Check for MU and OMEGA being numeric if (! (isnumeric (mu) && isnumeric (omega))) error ("nakastat: MU and OMEGA must be numeric."); endif ## Check for MU and OMEGA being real if (iscomplex (mu) || iscomplex (omega)) error ("nakastat: MU and OMEGA must not be complex."); endif ## Check for common size of MU and OMEGA if (! isscalar (mu) || ! isscalar (omega)) [retval, mu, omega] = common_size (mu, omega); if (retval > 0) error ("nakastat: MU and OMEGA must be of common size or scalars."); endif endif ## Calculate moments g = gamma (mu + 0.5) ./ gamma (mu); m = g .* sqrt (omega ./ mu); v = omega .* (1 - ((1 ./ mu) .* (g .^ 2))); ## Continue argument check knan = mu < 0.5 | omega <= 0; m(knan) = NaN; v(knan) = NaN; endfunction ## Input validation tests %!error nakastat () %!error nakastat (1) %!error nakastat ({}, 2) %!error nakastat (1, "") %!error nakastat (i, 2) %!error nakastat (1, i) %!error ... %! nakastat (ones (3), ones (2)) %!error ... %! nakastat (ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = nakastat (1, 1); %! assert (m, 0.8862269254, 1e-10); %! assert (v, 0.2146018366, 1e-10); %!test %! [m, v] = nakastat (1, 2); %! assert (m, 1.25331413731, 1e-10); %! assert (v, 0.42920367321, 1e-10); %!test %! [m, v] = nakastat (2, 1); %! assert (m, 0.93998560299, 1e-10); %! assert (v, 0.11642706618, 1e-10); statistics-release-1.7.3/inst/dist_stat/nbinstat.m000066400000000000000000000073441475240274700223310ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} nbinstat (@var{r}, @var{ps}) ## ## Compute statistics of the negative binomial distribution. ## ## @code{[@var{m}, @var{v}] = nbinstat (@var{r}, @var{ps})} returns the mean ## and variance of the negative binomial distribution with parameters @var{r} ## and @var{ps}, where @var{r} is the number of successes until the experiment ## is stopped and @var{ps} is the probability of success in each experiment, ## given the number of failures in @var{x}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the negative binomial distribution can be found at ## @url{https://en.wikipedia.org/wiki/Negative_binomial_distribution} ## ## @seealso{nbincdf, nbininv, nbininv, nbinrnd, nbinfit, nbinlike} ## @end deftypefn function [m, v] = nbinstat (r, ps) ## Check for valid number of input arguments if (nargin < 2) error ("nbinstat: function called with too few input arguments."); endif ## Check for R and PS being numeric if (! (isnumeric (r) && isnumeric (ps))) error ("nbinstat: R and PS must be numeric."); endif ## Check for R and PS being real if (iscomplex (r) || iscomplex (ps)) error ("nbinstat: R and PS must not be complex."); endif ## Check for common size of R and PS if (! isscalar (r) || ! isscalar (ps)) [retval, r, ps] = common_size (r, ps); if (retval > 0) error ("nbinstat: R and PS must be of common size or scalars."); endif endif ## Calculate moments q = 1 - ps; m = r .* q ./ ps; v = r .* q ./ (ps .^ 2); ## Continue argument check k = find (! (r > 0) | ! (r < Inf) | ! (ps > 0) | ! (ps < 1)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error nbinstat () %!error nbinstat (1) %!error nbinstat ({}, 2) %!error nbinstat (1, "") %!error nbinstat (i, 2) %!error nbinstat (1, i) %!error ... %! nbinstat (ones (3), ones (2)) %!error ... %! nbinstat (ones (2), ones (3)) ## Output validation tests %!test %! r = 1:4; %! ps = 0.2:0.2:0.8; %! [m, v] = nbinstat (r, ps); %! expected_m = [ 4.0000, 3.0000, 2.0000, 1.0000]; %! expected_v = [20.0000, 7.5000, 3.3333, 1.2500]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! r = 1:4; %! [m, v] = nbinstat (r, 0.5); %! expected_m = [1, 2, 3, 4]; %! expected_v = [2, 4, 6, 8]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/ncfstat.m000066400000000000000000000117351475240274700221500ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} ncfstat (@var{df1}, @var{df1}, @var{lambda}) ## ## Compute statistics for the noncentral @math{F}-distribution. ## ## @code{[@var{m}, @var{v}] = ncfstat (@var{df1}, @var{df1}, @var{lambda})} ## returns the mean and variance of the noncentral @math{F}-distribution with ## @var{df1} and @var{df2} degrees of freedom and noncentrality parameter ## @var{lambda}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## Further information about the noncentral @math{F}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_F-distribution} ## ## @seealso{ncfcdf, ncfinv, ncfpdf, ncfrnd, fstat} ## @end deftypefn function [m, v] = ncfstat (df1, df2, lambda) ## Check for valid number of input arguments if (nargin < 3) error ("ncfstat: function called with too few input arguments."); endif ## Check for DF1, DF2, and LAMBDA being numeric if (! (isnumeric (df1) && isnumeric (df2) && isnumeric (lambda))) error ("ncfstat: DF1, DF2, and LAMBDA must be numeric."); endif ## Check for DF1, DF2, and LAMBDA being reals if (iscomplex (df1) || iscomplex (df2) || iscomplex (lambda)) error ("ncfstat: DF1, DF2, and LAMBDA must not be complex."); endif ## Check for common size of DF1, DF2, and LAMBDA if (! isscalar (df1) || ! isscalar (df2) || ! isscalar (lambda)) [retval, df1, df2, lambda] = common_size (df1, df2, lambda); if (retval > 0) error ("ncfstat: DF1, DF2, and LAMBDA must be of common size or scalars."); endif endif ## Initialize mean and variance if (isa (df1, "single") || isa (df2, "single") || isa (lambda, "single")) m = zeros (size (df1), "single"); v = m; else m = zeros (size (df1)); v = m; endif ## Return NaNs for invalid df2 parameters m(df2 <= 2) = NaN; v(df2 <= 4) = NaN; ## Compute mean and variance for valid parameter values. k = (df2 > 2); if (any (k(:))) m(k) = df2(k) .* (df1(k) + lambda(k)) ./ (df1(k) .* (df2(k) - 2)); endif k = (df2 > 4); if (any (k(:))) df1_idx = df1(k) + lambda(k); df2_idx = df2(k) - 2; df1_df2 = (df2(k) ./ df1(k)) .^ 2; v(k) = 2 * df1_df2 .* (df1_idx .^ 2 + (df1_idx + lambda(k)) .* ... df2_idx) ./ ((df2(k) - 4) .* df2_idx .^ 2); endif endfunction ## Input validation tests %!error ncfstat () %!error ncfstat (1) %!error ncfstat (1, 2) %!error ncfstat ({}, 2, 3) %!error ncfstat (1, "", 3) %!error ncfstat (1, 2, "") %!error ncfstat (i, 2, 3) %!error ncfstat (1, i, 3) %!error ncfstat (1, 2, i) %!error ... %! ncfstat (ones (3), ones (2), 3) %!error ... %! ncfstat (ones (2), 2, ones (3)) %!error ... %! ncfstat (1, ones (2), ones (3)) ## Output validation tests %!shared df1, df2, lambda %! df1 = [2, 0, -1, 1, 4, 5]; %! df2 = [2, 4, -1, 5, 6, 7]; %! lambda = [1, NaN, 3, 0, 2, -1]; %!assert (ncfstat (df1, df2, lambda), [NaN, NaN, NaN, 1.6667, 2.25, 1.12], 1e-4); %!assert (ncfstat (df1(4:6), df2(4:6), 1), [3.3333, 1.8750, 1.6800], 1e-4); %!assert (ncfstat (df1(4:6), df2(4:6), 2), [5.0000, 2.2500, 1.9600], 1e-4); %!assert (ncfstat (df1(4:6), df2(4:6), 3), [6.6667, 2.6250, 2.2400], 1e-4); %!assert (ncfstat (2, [df2(1), df2(4:6)], 5), [NaN,5.8333,5.2500,4.9000], 1e-4); %!assert (ncfstat (0, [df2(1), df2(4:6)], 5), [NaN, Inf, Inf, Inf]); %!assert (ncfstat (1, [df2(1), df2(4:6)], 5), [NaN, 10, 9, 8.4], 1e-14); %!assert (ncfstat (4, [df2(1), df2(4:6)], 5), [NaN, 3.75, 3.375, 3.15], 1e-14); statistics-release-1.7.3/inst/dist_stat/nctstat.m000066400000000000000000000101731475240274700221610ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} nctstat (@var{df}, @var{mu}) ## ## Compute statistics for the noncentral @math{t}-distribution. ## ## @code{[@var{m}, @var{v}] = nctstat (@var{df}, @var{mu})} returns the mean ## and variance of the noncentral @math{t}-distribution with @var{df} degrees ## of freedom and noncentrality parameter @var{mu}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the same ## size as the other inputs. ## ## Further information about the noncentral @math{t}-distribution can be found ## at @url{https://en.wikipedia.org/wiki/Noncentral_t-distribution} ## ## @seealso{nctcdf, nctinv, nctpdf, nctrnd, tstat} ## @end deftypefn function [m, v] = nctstat (df, mu) ## Check for valid number of input arguments if (nargin < 2) error ("nctstat: function called with too few input arguments."); endif ## Check for DF and MU being numeric if (! (isnumeric (df) && isnumeric (mu))) error ("nctstat: DF and MU must be numeric."); endif ## Check for DF and MU being real if (iscomplex (df) || iscomplex (mu)) error ("nctstat: DF and MU must not be complex."); endif ## Check for common size of DF and MU if (! isscalar (df) || ! isscalar (mu)) [retval, df, mu] = common_size (df, mu); if (retval > 0) error ("nctstat: DF and MU must be of common size or scalars."); endif endif ## Initialize mean and variance if (isa (df, "single") || isa (mu, "single")) m = NaN (size (df), "single"); v = m; else m = NaN (size (df)); v = m; endif ## Compute mean and variance for valid parameter values. mk = df > 1; if (any (mk(:))) m(mk) = mu(mk) .* sqrt ((df(mk) / 2)) .* ... gamma ((df(mk) - 1) / 2) ./ gamma (df(mk) / 2); endif vk = df > 2; if (any (vk(:))) v(vk) = (df(vk) ./ (df(vk) - 2)) .* ... (1 + mu(vk) .^2) - 0.5 * (df(vk) .* mu(vk) .^ 2) .* ... exp (2 * (gammaln ((df(vk) - 1) / 2) - gammaln (df(vk) / 2))); endif endfunction ## Input validation tests %!error nctstat () %!error nctstat (1) %!error nctstat ({}, 2) %!error nctstat (1, "") %!error nctstat (i, 2) %!error nctstat (1, i) %!error ... %! nctstat (ones (3), ones (2)) %!error ... %! nctstat (ones (2), ones (3)) ## Output validation tests %!shared df, mu %! df = [2, 0, -1, 1, 4]; %! mu = [1, NaN, 3, -1, 2]; %!assert (nctstat (df, mu), [1.7725, NaN, NaN, NaN, 2.5066], 1e-4); %!assert (nctstat ([df(1:2), df(4:5)], 1), [1.7725, NaN, NaN, 1.2533], 1e-4); %!assert (nctstat ([df(1:2), df(4:5)], 3), [5.3174, NaN, NaN, 3.7599], 1e-4); %!assert (nctstat ([df(1:2), df(4:5)], 2), [3.5449, NaN, NaN, 2.5066], 1e-4); %!assert (nctstat (2, [mu(1), mu(3:5)]), [1.7725,5.3174,-1.7725,3.5449], 1e-4); %!assert (nctstat (0, [mu(1), mu(3:5)]), [NaN, NaN, NaN, NaN]); %!assert (nctstat (1, [mu(1), mu(3:5)]), [NaN, NaN, NaN, NaN]); %!assert (nctstat (4, [mu(1), mu(3:5)]), [1.2533,3.7599,-1.2533,2.5066], 1e-4); statistics-release-1.7.3/inst/dist_stat/ncx2stat.m000066400000000000000000000076161475240274700222570ustar00rootroot00000000000000## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} ncx2stat (@var{df}, @var{lambda}) ## ## Compute statistics for the noncentral chi-squared distribution. ## ## @code{[@var{m}, @var{v}] = ncx2stat (@var{df}, @var{lambda})} returns the ## mean and variance of the noncentral chi-squared distribution with @var{df} ## degrees of freedom and noncentrality parameter @var{lambda}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the noncentral chi-squared distribution can be ## found at @url{https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution} ## ## @seealso{ncx2cdf, ncx2inv, ncx2pdf, ncx2rnd} ## @end deftypefn function [m, v] = ncx2stat (df, lambda) ## Check for valid number of input arguments if (nargin < 2) error ("ncx2stat: function called with too few input arguments."); endif ## Check for DF and LAMBDA being numeric if (! (isnumeric (df) && isnumeric (lambda))) error ("ncx2stat: DF and LAMBDA must be numeric."); endif ## Check for DF and LAMBDA being real if (iscomplex (df) || iscomplex (lambda)) error ("ncx2stat: DF and LAMBDA must not be complex."); endif ## Check for common size of DF and LAMBDA if (! isscalar (df) || ! isscalar (lambda)) [retval, df, lambda] = common_size (df, lambda); if (retval > 0) error ("ncx2stat: DF and LAMBDA must be of common size or scalars."); endif endif ## Initialize mean and variance if (isa (df, "single") || isa (lambda, "single")) m = NaN (size (df), "single"); v = m; else m = NaN (size (df)); v = m; endif ## Compute mean and variance for valid parameter values. k = (df > 0 & lambda >= 0); if (any (k(:))) m(k) = lambda(k) + df(k); v(k) = 2 * (df(k) + 2 * (lambda(k))); endif endfunction ## Input validation tests %!error ncx2stat () %!error ncx2stat (1) %!error ncx2stat ({}, 2) %!error ncx2stat (1, "") %!error ncx2stat (i, 2) %!error ncx2stat (1, i) %!error ... %! ncx2stat (ones (3), ones (2)) %!error ... %! ncx2stat (ones (2), ones (3)) ## Output validation tests %!shared df, d1 %! df = [2, 0, -1, 1, 4]; %! d1 = [1, NaN, 3, -1, 2]; %!assert (ncx2stat (df, d1), [3, NaN, NaN, NaN, 6]); %!assert (ncx2stat ([df(1:2), df(4:5)], 1), [3, NaN, 2, 5]); %!assert (ncx2stat ([df(1:2), df(4:5)], 3), [5, NaN, 4, 7]); %!assert (ncx2stat ([df(1:2), df(4:5)], 2), [4, NaN, 3, 6]); %!assert (ncx2stat (2, [d1(1), d1(3:5)]), [3, 5, NaN, 4]); %!assert (ncx2stat (0, [d1(1), d1(3:5)]), [NaN, NaN, NaN, NaN]); %!assert (ncx2stat (1, [d1(1), d1(3:5)]), [2, 4, NaN, 3]); %!assert (ncx2stat (4, [d1(1), d1(3:5)]), [5, 7, NaN, 6]); statistics-release-1.7.3/inst/dist_stat/normstat.m000066400000000000000000000071361475240274700223550ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} normstat (@var{mu}, @var{sigma}) ## ## Compute statistics of the normal distribution. ## ## @code{[@var{m}, @var{v}] = normstat (@var{mu}, @var{sigma})} returns the mean ## and variance of the normal distribution with non-centrality (distance) ## parameter @var{mu} and scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the normal distribution can be found at ## @url{https://en.wikipedia.org/wiki/Normal_distribution} ## ## @seealso{norminv, norminv, normpdf, normrnd, normfit, normlike} ## @end deftypefn function [m, v] = normstat (mu, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("normstat: function called with too few input arguments."); endif ## Check for MU and SIGMA being numeric if (! (isnumeric (mu) && isnumeric (sigma))) error ("normstat: MU and SIGMA must be numeric."); endif ## Check for MU and SIGMA being real if (iscomplex (mu) || iscomplex (sigma)) error ("normstat: MU and SIGMA must not be complex."); endif ## Check for common size of MU and SIGMA if (! isscalar (mu) || ! isscalar (sigma)) [retval, mu, sigma] = common_size (mu, sigma); if (retval > 0) error ("normstat: MU and SIGMA must be of common size or scalars."); endif endif ## Calculate moments m = mu; v = sigma .* sigma; ## Continue argument check k = find (! (sigma > 0) | ! (sigma < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error normstat () %!error normstat (1) %!error normstat ({}, 2) %!error normstat (1, "") %!error normstat (i, 2) %!error normstat (1, i) %!error ... %! normstat (ones (3), ones (2)) %!error ... %! normstat (ones (2), ones (3)) ## Output validation tests %!test %! mu = 1:6; %! sigma = 0.2:0.2:1.2; %! [m, v] = normstat (mu, sigma); %! expected_v = [0.0400, 0.1600, 0.3600, 0.6400, 1.0000, 1.4400]; %! assert (m, mu); %! assert (v, expected_v, 0.001); %!test %! sigma = 0.2:0.2:1.2; %! [m, v] = normstat (0, sigma); %! expected_mn = [0, 0, 0, 0, 0, 0]; %! expected_v = [0.0400, 0.1600, 0.3600, 0.6400, 1.0000, 1.4400]; %! assert (m, expected_mn, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/plstat.m000066400000000000000000000065651475240274700220220ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} plstat (@var{x}, @var{Fx}) ## ## Compute statistics of the piecewise linear distribution. ## ## @code{[@var{m}, @var{v}] = plstat (@var{x}, @var{Fx})} returns the mean, ## @var{m}, and variance, @var{v}, of the piecewise linear distribution with a ## vector of @var{x} values at which the CDF changes slope and a vector of CDF ## values @var{Fx} that correspond to each value in @var{x}. Both @var{x} and ## @var{Fx} must be vectors of the same size and at least 2-elements long. ## ## Further information about the piecewise linear distribution can be found at ## @url{https://en.wikipedia.org/wiki/Piecewise_linear_function} ## ## @seealso{plcdf, plinv, plpdf, plrnd} ## @end deftypefn function [m, v] = plstat (x, Fx) ## Check for valid number of input arguments if (nargin < 2) error ("plstat: function called with too few input arguments."); endif ## Check for common size of X and FX if (! isvector (x) || ! isvector (Fx) || ! isequal (size (x), size (Fx))) error ("plstat: X and FX must be vectors of equal size."); endif ## Check for X and FX being at least 2-elements long if (length (x) < 2 || length (Fx) < 2) error ("plstat: X and FX must be at least two-elements long."); endif ## Check for Fx being bounded in [0, 1] if (any (Fx < 0) || any (Fx > 1)) error ("plstat: FX must be bounded in the range [0, 1]."); endif ## Check for X and FX being reals if (iscomplex (x) || iscomplex (Fx)) error ("plstat: X and FX must not be complex."); endif ## Compute the mean and variance x_m = (x(1:end-1) + x(2:end)) / 2; dFx = diff (Fx); m = dot (dFx, x_m); x_v = diff(x) .^ 2 / 12; v = dot (dFx, x_v + (x_m - m) .^ 2); endfunction ## Test output %!shared x, Fx %! x = [0, 1, 3, 4, 7, 10]; %! Fx = [0, 0.2, 0.5, 0.6, 0.7, 1]; %!assert (plstat (x, Fx), 4.15) %!test %! [m, v] = plstat (x, Fx); %! assert (v, 10.3775, 1e-14) ## Test input validation %!error plstat () %!error plstat (1) %!error ... %! plstat ([0, 1, 2], [0, 1]) %!error ... %! plstat ([0], [1]) %!error ... %! plstat ([0, 1, 2], [0, 1, 1.5]) %!error ... %! plstat ([0, 1, 2], [0, i, 1]) %!error ... %! plstat ([0, i, 2], [0, 0.5, 1]) %!error ... %! plstat ([0, i, 2], [0, 0.5i, 1]) statistics-release-1.7.3/inst/dist_stat/poisstat.m000066400000000000000000000046711475240274700223550ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} poisstat (@var{lambda}) ## ## Compute statistics of the Poisson distribution. ## ## @code{[@var{m}, @var{v}] = poisstat (@var{lambda})} returns the mean and ## variance of the Poisson distribution with rate parameter @var{lambda}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## Further information about the Poisson distribution can be found at ## @url{https://en.wikipedia.org/wiki/Poisson_distribution} ## ## @seealso{poisscdf, poissinv, poisspdf, poissrnd, poissfit, poisslike} ## @end deftypefn function [m, v] = poisstat (lambda) ## Check for valid number of input arguments if (nargin < 1) error ("poisstat: function called with too few input arguments."); endif ## Check for SIGMA being numeric if (! isnumeric (lambda)) error ("poisstat: SIGMA must be numeric."); endif ## Check for SIGMA being real if (iscomplex (lambda)) error ("poisstat: SIGMA must not be complex."); endif ## Set moments m = lambda; v = lambda; ## Continue argument check k = find (! (lambda > 0) | ! (lambda < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error poisstat () %!error poisstat ({}) %!error poisstat ("") %!error poisstat (i) ## Output validation tests %!test %! lambda = 1 ./ (1:6); %! [m, v] = poisstat (lambda); %! assert (m, lambda); %! assert (v, lambda); statistics-release-1.7.3/inst/dist_stat/raylstat.m000066400000000000000000000051351475240274700223460ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} raylstat (@var{sigma}) ## ## Compute statistics of the Rayleigh distribution. ## ## @code{[@var{m}, @var{v}] = raylstat (@var{sigma})} returns the mean and ## variance of the Rayleigh distribution with scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## Further information about the Rayleigh distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rayleigh_distribution} ## ## @seealso{raylcdf, raylinv, raylpdf, raylrnd, raylfit, rayllike} ## @end deftypefn function [m, v] = raylstat (sigma) ## Check for valid number of input arguments if (nargin < 1) error ("raylstat: function called with too few input arguments."); endif ## Check for SIGMA being numeric if (! isnumeric (sigma)) error ("raylstat: SIGMA must be numeric."); endif ## Check for SIGMA being real if (iscomplex (sigma)) error ("raylstat: SIGMA must not be complex."); endif ## Calculate moments m = sigma .* sqrt (pi ./ 2); v = (2 - pi ./ 2) .* sigma .^ 2; ## Continue argument check k = find (! (sigma > 0)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error raylstat () %!error raylstat ({}) %!error raylstat ("") %!error raylstat (i) ## Output validation tests %!test %! sigma = 1:6; %! [m, v] = raylstat (sigma); %! expected_m = [1.2533, 2.5066, 3.7599, 5.0133, 6.2666, 7.5199]; %! expected_v = [0.4292, 1.7168, 3.8628, 6.8673, 10.7301, 15.4513]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/ricestat.m000066400000000000000000000104121475240274700223130ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} ricestat (@var{s}, @var{sigma}) ## ## Compute statistics of the Rician distribution. ## ## @code{[@var{m}, @var{v}] = ricestat (@var{s}, @var{sigma})} returns the mean ## and variance of the Rician distribution with non-centrality (distance) ## parameter @var{s} and scale parameter @var{sigma}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the Rician distribution can be found at ## @url{https://en.wikipedia.org/wiki/Rice_distribution} ## ## @seealso{ricecdf, riceinv, ricepdf, ricernd, ricefit, ricelike} ## @end deftypefn function [m, v] = ricestat (s, sigma) ## Check for valid number of input arguments if (nargin < 2) error ("ricestat: function called with too few input arguments."); endif ## Check for S and SIGMA being numeric if (! (isnumeric (s) && isnumeric (sigma))) error ("ricestat: S and SIGMA must be numeric."); endif ## Check for S and SIGMA being real if (iscomplex (s) || iscomplex (sigma)) error ("ricestat: S and SIGMA must not be complex."); endif ## Check for common size of S and SIGMA if (! isscalar (s) || ! isscalar (sigma)) [retval, s, sigma] = common_size (s, sigma); if (retval > 0) error ("ricestat: S and SIGMA must be of common size or scalars."); endif endif ## Initialize mean and variance if (isa (s, "single") || isa (sigma, "single")) m = NaN (size (s), "single"); v = m; else m = NaN (size (s)); v = m; endif ## Compute mean and variance for valid parameter values. k = (s >= 0 & sigma > 0); if (any (k(:))) thetasq = (s(k) .^ 2) ./ (sigma(k) .^ 2); L = Laguerre_half (-0.5 .* thetasq); m(k) = sigma(k) .* sqrt (pi / 2) .* L; v(k) = 2 * (sigma(k) .^ 2) + s(k) .^ 2 - ... (0.5 .* pi .* sigma(k) .^ 2) .* L .^ 2; endif endfunction function L = Laguerre_half(x) L = exp (x ./ 2) .* ((1 - x) .* besseli (0, -x./2) - x .* besseli (1, -x./2)); endfunction ## Input validation tests %!error ricestat () %!error ricestat (1) %!error ricestat ({}, 2) %!error ricestat (1, "") %!error ricestat (i, 2) %!error ricestat (1, i) %!error ... %! ricestat (ones (3), ones (2)) %!error ... %! ricestat (ones (2), ones (3)) ## Output validation tests %!shared s, sigma %! s = [2, 0, -1, 1, 4]; %! sigma = [1, NaN, 3, -1, 2]; %!assert (ricestat (s, sigma), [2.2724, NaN, NaN, NaN, 4.5448], 1e-4); %!assert (ricestat ([s(1:2), s(4:5)], 1), [2.2724, 1.2533, 1.5486, 4.1272], 1e-4); %!assert (ricestat ([s(1:2), s(4:5)], 3), [4.1665, 3.7599, 3.8637, 5.2695], 1e-4); %!assert (ricestat ([s(1:2), s(4:5)], 2), [3.0971, 2.5066, 2.6609, 4.5448], 1e-4); %!assert (ricestat (2, [sigma(1), sigma(3:5)]), [2.2724, 4.1665, NaN, 3.0971], 1e-4); %!assert (ricestat (0, [sigma(1), sigma(3:5)]), [1.2533, 3.7599, NaN, 2.5066], 1e-4); %!assert (ricestat (1, [sigma(1), sigma(3:5)]), [1.5486, 3.8637, NaN, 2.6609], 1e-4); %!assert (ricestat (4, [sigma(1), sigma(3:5)]), [4.1272, 5.2695, NaN, 4.5448], 1e-4); statistics-release-1.7.3/inst/dist_stat/tlsstat.m000066400000000000000000000117511475240274700222020ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} tlsstat (@var{mu}, @var{sigma}, @var{nu}) ## ## Compute statistics of the location-scale Student's T distribution. ## ## @code{[@var{m}, @var{v}] = tlsstat (@var{mu}, @var{sigma}, @var{nu})} returns ## the mean and variance of the location-scale Student's T distribution with ## location parameter @var{mu}, scale parameter @var{sigma}, and @var{nu} ## degrees of freedom. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the location-scale Student's T distribution can be ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} ## ## @seealso{tlscdf, tlsinv, tlspdf, tlsrnd, tlsfit, tlslike} ## @end deftypefn function [m, v] = tlsstat (mu, sigma, nu) ## Check for valid number of input arguments if (nargin < 3) error ("tlsstat: function called with too few input arguments."); endif ## Check for MU, SIGMA, and NU being numeric if (! (isnumeric (mu) && isnumeric (sigma) && isnumeric (nu))) error ("tlsstat: MU, SIGMA, and NU must be numeric."); endif ## Check for MU, SIGMA, and NU being real if (iscomplex (mu) || iscomplex (sigma) || iscomplex (nu)) error ("tlsstat: MU, SIGMA, and NU must not be complex."); endif ## Check for common size of MU, SIGMA, and NU if (! isscalar (mu) || ! isscalar (sigma) || ! isscalar (nu)) [retval, mu, sigma, nu] = common_size (mu, sigma, nu); if (retval > 0) error ("tlsstat: MU, SIGMA, and NU must be of common size or scalars."); endif endif ## Calculate moments m = zeros (size (nu)) + mu; v = sigma .* (nu ./ (nu - 2)); ## Continue argument check k = find (! (nu > 1) | ! (nu < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif k = find (! (nu > 2) & (nu < Inf)); if (any (k)) v(k) = NaN; endif endfunction ## Input validation tests %!error tlsstat () %!error tlsstat (1) %!error tlsstat (1, 2) %!error tlsstat ({}, 2, 3) %!error tlsstat (1, "", 3) %!error tlsstat (1, 2, ["d"]) %!error tlsstat (i, 2, 3) %!error tlsstat (1, i, 3) %!error tlsstat (1, 2, i) %!error ... %! tlsstat (ones (3), ones (2), 1) %!error ... %! tlsstat (ones (2), 1, ones (3)) %!error ... %! tlsstat (1, ones (2), ones (3)) ## Output validation tests %!test %! [m, v] = tlsstat (0, 1, 0); %! assert (m, NaN); %! assert (v, NaN); %!test %! [m, v] = tlsstat (0, 1, 1); %! assert (m, NaN); %! assert (v, NaN); %!test %! [m, v] = tlsstat (2, 1, 1); %! assert (m, NaN); %! assert (v, NaN); %!test %! [m, v] = tlsstat (-2, 1, 1); %! assert (m, NaN); %! assert (v, NaN); %!test %! [m, v] = tlsstat (0, 1, 2); %! assert (m, 0); %! assert (v, NaN); %!test %! [m, v] = tlsstat (2, 1, 2); %! assert (m, 2); %! assert (v, NaN); %!test %! [m, v] = tlsstat (-2, 1, 2); %! assert (m, -2); %! assert (v, NaN); %!test %! [m, v] = tlsstat (0, 2, 2); %! assert (m, 0); %! assert (v, NaN); %!test %! [m, v] = tlsstat (2, 2, 2); %! assert (m, 2); %! assert (v, NaN); %!test %! [m, v] = tlsstat (-2, 2, 2); %! assert (m, -2); %! assert (v, NaN); %!test %! [m, v] = tlsstat (0, 1, 3); %! assert (m, 0); %! assert (v, 3); %!test %! [m, v] = tlsstat (0, 2, 3); %! assert (m, 0); %! assert (v, 6); %!test %! [m, v] = tlsstat (2, 1, 3); %! assert (m, 2); %! assert (v, 3); %!test %! [m, v] = tlsstat (2, 2, 3); %! assert (m, 2); %! assert (v, 6); %!test %! [m, v] = tlsstat (-2, 1, 3); %! assert (m, -2); %! assert (v, 3); %!test %! [m, v] = tlsstat (-2, 2, 3); %! assert (m, -2); %! assert (v, 6); statistics-release-1.7.3/inst/dist_stat/tristat.m000066400000000000000000000071431475240274700221760ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} tristat (@var{a}, @var{b}, @var{c}) ## ## Compute statistics of the Triangular distribution. ## ## @code{[@var{m}, @var{v}] = tristat (@var{a}, @var{b}, @var{c})} returns the ## mean and variance of the Triangular distribution with lower limit parameter ## @var{a}, peak location (mode) parameter @var{b}, and upper limit parameter ## @var{c}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Note that the order of the parameter input arguments has been changed after ## statistics version 1.6.3 in order to be MATLAB compatible with the parameters ## used in the TriangularDistribution probability distribution object. More ## specifically, the positions of the parameters @var{b} and @var{c} have been ## swapped. As a result, the naming conventions no longer coinside with those ## used in Wikipedia, in which @math{b} denotes the upper limit and @math{c} ## denotes the mode or peak parameter. ## ## Further information about the triangular distribution can be found at ## @url{https://en.wikipedia.org/wiki/Triangular_distribution} ## ## @seealso{tcdf, tinv, tpdf, trnd} ## @end deftypefn function [m, v] = tristat (a, b, c) ## Check for valid number of input arguments if (nargin < 3) error ("tristat: function called with too few input arguments."); endif ## Check for A, B, and C being numeric if (! (isnumeric (a) && isnumeric (b) && isnumeric (c))) error ("tristat: A, B, and C must be numeric."); endif ## Check for A, B, and C being real if (! (isreal (a) && isreal (b) && isreal (c))) error ("tristat: A, B, and C must be real."); endif ## Calculate moments m = (a + b + c) ./ 3; v = (a .^ 2 + b .^ 2 + c .^ 2 - a .* b - a .* c - b .* c) ./ 18; ## Continue argument check k = find (! (a < c) | ! (a <= b & b <= c)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error tristat () %!error tristat (1) %!error tristat (1, 2) %!error tristat ("i", 2, 1) %!error tristat (0, "d", 1) %!error tristat (0, 3, {}) %!error tristat (i, 2, 1) %!error tristat (0, i, 1) %!error tristat (0, 3, i) ## Output validation tests %!test %! a = 1:5; %! b = 3:7; %! c = 5:9; %! [m, v] = tristat (a, b, c); %! expected_m = [3, 4, 5, 6, 7]; %! assert (m, expected_m); %! assert (v, ones (1, 5) * (2/3)); statistics-release-1.7.3/inst/dist_stat/tstat.m000066400000000000000000000050241475240274700216370ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} tstat (@var{df}) ## ## Compute statistics of the Student's T distribution. ## ## @code{[@var{m}, @var{v}] = tstat (@var{df})} returns the mean and variance of ## the Student's T distribution with @var{df} degrees of freedom. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## Further information about the Student's T distribution can be found at ## @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution} ## ## @seealso{tcdf, tinv, tpdf, trnd} ## @end deftypefn function [m, v] = tstat (df) ## Check for valid number of input arguments if (nargin < 1) error ("tstat: function called with too few input arguments."); endif ## Check for DF being numeric if (! isnumeric (df)) error ("tstat: DF must be numeric."); endif ## Check for DF being real if (iscomplex (df)) error ("tstat: DF must not be complex."); endif ## Calculate moments m = zeros (size (df)); v = df ./ (df - 2); ## Continue argument check k = find (! (df > 1) | ! (df < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif k = find (! (df > 2) & (df < Inf)); if (any (k)) v(k) = Inf; endif endfunction ## Input validation tests %!error tstat () %!error tstat ({}) %!error tstat ("") %!error tstat (i) ## Output validation tests %!test %! df = 3:8; %! [m, v] = tstat (df); %! expected_m = [0, 0, 0, 0, 0, 0]; %! expected_v = [3.0000, 2.0000, 1.6667, 1.5000, 1.4000, 1.3333]; %! assert (m, expected_m); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/unidstat.m000066400000000000000000000052631475240274700223400ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} unidstat (@var{df}) ## ## Compute statistics of the discrete uniform cumulative distribution. ## ## @code{[@var{m}, @var{v}] = unidstat (@var{df})} returns the mean and variance ## of the discrete uniform cumulative distribution with parameter @var{N}, which ## corresponds to the maximum observable value and must be a positive natural ## number. ## ## The size of @var{m} (mean) and @var{v} (variance) is the same size of the ## input argument. ## ## Further information about the discrete uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Discrete_uniform_distribution} ## ## @seealso{unidcdf, unidinv, unidpdf, unidrnd, unidfit} ## @end deftypefn function [m, v] = unidstat (N) ## Check for valid number of input arguments if (nargin < 1) error ("unidstat: function called with too few input arguments."); endif ## Check for N being numeric if (! isnumeric (N)) error ("unidstat: N must be numeric."); endif ## Check for N being real if (iscomplex (N)) error ("unidstat: N must not be complex."); endif ## Calculate moments m = (N + 1) ./ 2; v = ((N .^ 2) - 1) ./ 12; ## Continue argument check k = find (! (N > 0) | ! (N < Inf) | ! (N == round (N))); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error unidstat () %!error unidstat ({}) %!error unidstat ("") %!error unidstat (i) ## Output validation tests %!test %! N = 1:6; %! [m, v] = unidstat (N); %! expected_m = [1.0000, 1.5000, 2.0000, 2.5000, 3.0000, 3.5000]; %! expected_v = [0.0000, 0.2500, 0.6667, 1.2500, 2.0000, 2.9167]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/unifstat.m000066400000000000000000000072641475240274700223450ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} unifstat (@var{df}) ## ## Compute statistics of the continuous uniform cumulative distribution. ## ## @code{[@var{m}, @var{v}] = unifstat (@var{df})} returns the mean and variance ## of the continuous uniform cumulative distribution with parameters @var{a} and ## @var{b}, which define the lower and upper bounds of the interval ## @qcode{[@var{a}, @var{b}]}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the continuous uniform distribution can be found at ## @url{https://en.wikipedia.org/wiki/Continuous_uniform_distribution} ## ## @seealso{unifcdf, unifinv, unifpdf, unifrnd, unifit} ## @end deftypefn function [m, v] = unifstat (a, b) ## Check for valid number of input arguments if (nargin < 2) error ("unifstat: function called with too few input arguments."); endif ## Check for A and B being numeric if (! (isnumeric (a) && isnumeric (b))) error ("unifstat: A and B must be numeric."); endif ## Check for A and B being real if (iscomplex (a) || iscomplex (b)) error ("unifstat: A and B must not be complex."); endif ## Check for common size of A and B if (! isscalar (a) || ! isscalar (b)) [retval, a, b] = common_size (a, b); if (retval > 0) error ("unifstat: A and B must be of common size or scalars."); endif endif ## Calculate moments m = (a + b) ./ 2; v = ((b - a) .^ 2) ./ 12; ## Continue argument check k = find (! (-Inf < a) | ! (a < b) | ! (b < Inf)); if (any (k)) m(k) = NaN; v(k) = NaN; endif endfunction ## Input validation tests %!error unifstat () %!error unifstat (1) %!error unifstat ({}, 2) %!error unifstat (1, "") %!error unifstat (i, 2) %!error unifstat (1, i) %!error ... %! unifstat (ones (3), ones (2)) %!error ... %! unifstat (ones (2), ones (3)) ## Output validation tests %!test %! a = 1:6; %! b = 2:2:12; %! [m, v] = unifstat (a, b); %! expected_m = [1.5000, 3.0000, 4.5000, 6.0000, 7.5000, 9.0000]; %! expected_v = [0.0833, 0.3333, 0.7500, 1.3333, 2.0833, 3.0000]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! a = 1:6; %! [m, v] = unifstat (a, 10); %! expected_m = [5.5000, 6.0000, 6.5000, 7.0000, 7.5000, 8.0000]; %! expected_v = [6.7500, 5.3333, 4.0833, 3.0000, 2.0833, 1.3333]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_stat/wblstat.m000066400000000000000000000073671475240274700221740ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{m}, @var{v}] =} wblstat (@var{lambda}, @var{k}) ## ## Compute statistics of the Weibull distribution. ## ## @code{[@var{m}, @var{v}] = wblstat (@var{lambda}, @var{k})} returns the mean ## and variance of the Weibull distribution with scale parameter @var{lambda} ## and shape parameter @var{k}. ## ## The size of @var{m} (mean) and @var{v} (variance) is the common size of the ## input arguments. A scalar input functions as a constant matrix of the ## same size as the other inputs. ## ## Further information about the Weibull distribution can be found at ## @url{https://en.wikipedia.org/wiki/Weibull_distribution} ## ## @seealso{wblcdf, wblinv, wblpdf, wblrnd, wblfit, wbllike, wblplot} ## @end deftypefn function [m, v] = wblstat (lambda, k) ## Check for valid number of input arguments if (nargin < 2) error ("wblstat: function called with too few input arguments."); endif ## Check for LAMBDA and K being numeric if (! (isnumeric (lambda) && isnumeric (k))) error ("wblstat: LAMBDA and K must be numeric."); endif ## Check for LAMBDA and K being real if (iscomplex (lambda) || iscomplex (k)) error ("wblstat: LAMBDA and K must not be complex."); endif ## Check for common size of LAMBDA and K if (! isscalar (lambda) || ! isscalar (k)) [retval, lambda, k] = common_size (lambda, k); if (retval > 0) error ("wblstat: LAMBDA and K must be of common size or scalars."); endif endif ## Calculate moments m = lambda .* gamma (1 + 1 ./ k); v = (lambda .^ 2) .* gamma (1 + 2 ./ k) - m .^ 2; ## Continue argument check is_nan = find (! (lambda > 0) | ! (lambda < Inf) | ! (k > 0) | ! (k < Inf)); if (any (is_nan)) m(is_nan) = NaN; v(is_nan) = NaN; endif endfunction ## Input validation tests %!error wblstat () %!error wblstat (1) %!error wblstat ({}, 2) %!error wblstat (1, "") %!error wblstat (i, 2) %!error wblstat (1, i) %!error ... %! wblstat (ones (3), ones (2)) %!error ... %! wblstat (ones (2), ones (3)) ## Output validation tests %!test %! lambda = 3:8; %! k = 1:6; %! [m, v] = wblstat (lambda, k); %! expected_m = [3.0000, 3.5449, 4.4649, 5.4384, 6.4272, 7.4218]; %! expected_v = [9.0000, 3.4336, 2.6333, 2.3278, 2.1673, 2.0682]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); %!test %! k = 1:6; %! [m, v] = wblstat (6, k); %! expected_m = [ 6.0000, 5.3174, 5.3579, 5.4384, 5.5090, 5.5663]; %! expected_v = [36.0000, 7.7257, 3.7920, 2.3278, 1.5923, 1.1634]; %! assert (m, expected_m, 0.001); %! assert (v, expected_v, 0.001); statistics-release-1.7.3/inst/dist_wrap/000077500000000000000000000000001475240274700203175ustar00rootroot00000000000000statistics-release-1.7.3/inst/dist_wrap/cdf.m000066400000000000000000000372301475240274700212360ustar00rootroot00000000000000## Copyright (C) 2013 Pantxo Diribarne ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} cdf (@var{name}, @var{x}, @var{A}) ## @deftypefnx {statistics} {@var{p} =} cdf (@var{name}, @var{x}, @var{A}, @var{B}) ## @deftypefnx {statistics} {@var{p} =} cdf (@var{name}, @var{x}, @var{A}, @var{B}, @var{C}) ## @deftypefnx {statistics} {@var{p} =} cdf (@dots{}, @qcode{"upper"}) ## ## Return the CDF of a univariate distribution evaluated at @var{x}. ## ## @code{cdf} is a wrapper for the univariate cumulative distribution functions ## available in the statistics package. See the corresponding functions' help ## to learn the signification of the parameters after @var{x}. ## ## @code{@var{p} = cdf (@var{name}, @var{x}, @var{A})} returns the CDF for the ## one-parameter distribution family specified by @var{name} and the ## distribution parameter @var{A}, evaluated at the values in @var{x}. ## ## @code{@var{p} = cdf (@var{name}, @var{x}, @var{A}, @var{B})} returns the CDF ## for the two-parameter distribution family specified by @var{name} and the ## distribution parameters @var{A} and @var{B}, evaluated at the values in ## @var{x}. ## ## @code{@var{p} = cdf (@var{name}, @var{x}, @var{A}, @var{B}, @var{C})} returns ## the CDF for the three-parameter distribution family specified by @var{name} ## and the distribution parameters @var{A}, @var{B}, and @var{C}, evaluated at ## the values in @var{x}. ## ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of the ## CDF using an algorithm that more accurately computes the extreme upper-tail ## probabilities. @qcode{"upper"} can follow any of the input arguments in the ## previous syntaxes. ## ## @var{name} must be a char string of the name or the abbreviation of the ## desired cumulative distribution function as listed in the followng table. ## The last column shows the number of required parameters that should be parsed ## after @var{x} to the desired CDF. The optional input argument ## @qcode{"upper"} does not count in the required number of parameters. ## ## @multitable @columnfractions 0.4 0.05 0.2 0.05 0.3 ## @headitem Distribution Name @tab @tab Abbreviation @tab @tab Input Parameters ## @item @qcode{"Beta"} @tab @tab @qcode{"beta"} @tab @tab 2 ## @item @qcode{"Binomial"} @tab @tab @qcode{"bino"} @tab @tab 2 ## @item @qcode{"Birnbaum-Saunders"} @tab @tab @qcode{"bisa"} @tab @tab 2 ## @item @qcode{"Burr"} @tab @tab @qcode{"burr"} @tab @tab 3 ## @item @qcode{"Cauchy"} @tab @tab @qcode{"cauchy"} @tab @tab 2 ## @item @qcode{"Chi-squared"} @tab @tab @qcode{"chi2"} @tab @tab 1 ## @item @qcode{"Extreme Value"} @tab @tab @qcode{"ev"} @tab @tab 2 ## @item @qcode{"Exponential"} @tab @tab @qcode{"exp"} @tab @tab 1 ## @item @qcode{"F-Distribution"} @tab @tab @qcode{"f"} @tab @tab 2 ## @item @qcode{"Gamma"} @tab @tab @qcode{"gam"} @tab @tab 2 ## @item @qcode{"Geometric"} @tab @tab @qcode{"geo"} @tab @tab 1 ## @item @qcode{"Generalized Extreme Value"} @tab @tab @qcode{"gev"} @tab @tab 3 ## @item @qcode{"Generalized Pareto"} @tab @tab @qcode{"gp"} @tab @tab 3 ## @item @qcode{"Gumbel"} @tab @tab @qcode{"gumbel"} @tab @tab 2 ## @item @qcode{"Half-normal"} @tab @tab @qcode{"hn"} @tab @tab 2 ## @item @qcode{"Hypergeometric"} @tab @tab @qcode{"hyge"} @tab @tab 3 ## @item @qcode{"Inverse Gaussian"} @tab @tab @qcode{"invg"} @tab @tab 2 ## @item @qcode{"Laplace"} @tab @tab @qcode{"laplace"} @tab @tab 2 ## @item @qcode{"Logistic"} @tab @tab @qcode{"logi"} @tab @tab 2 ## @item @qcode{"Log-Logistic"} @tab @tab @qcode{"logl"} @tab @tab 2 ## @item @qcode{"Lognormal"} @tab @tab @qcode{"logn"} @tab @tab 2 ## @item @qcode{"Nakagami"} @tab @tab @qcode{"naka"} @tab @tab 2 ## @item @qcode{"Negative Binomial"} @tab @tab @qcode{"nbin"} @tab @tab 2 ## @item @qcode{"Noncentral F-Distribution"} @tab @tab @qcode{"ncf"} @tab @tab 3 ## @item @qcode{"Noncentral Student T"} @tab @tab @qcode{"nct"} @tab @tab 2 ## @item @qcode{"Noncentral Chi-Squared"} @tab @tab @qcode{"ncx2"} @tab @tab 2 ## @item @qcode{"Normal"} @tab @tab @qcode{"norm"} @tab @tab 2 ## @item @qcode{"Poisson"} @tab @tab @qcode{"poiss"} @tab @tab 1 ## @item @qcode{"Rayleigh"} @tab @tab @qcode{"rayl"} @tab @tab 1 ## @item @qcode{"Rician"} @tab @tab @qcode{"rice"} @tab @tab 2 ## @item @qcode{"Student T"} @tab @tab @qcode{"t"} @tab @tab 1 ## @item @qcode{"location-scale T"} @tab @tab @qcode{"tls"} @tab @tab 3 ## @item @qcode{"Triangular"} @tab @tab @qcode{"tri"} @tab @tab 3 ## @item @qcode{"Discrete Uniform"} @tab @tab @qcode{"unid"} @tab @tab 1 ## @item @qcode{"Uniform"} @tab @tab @qcode{"unif"} @tab @tab 2 ## @item @qcode{"Von Mises"} @tab @tab @qcode{"vm"} @tab @tab 2 ## @item @qcode{"Weibull"} @tab @tab @qcode{"wbl"} @tab @tab 2 ## @end multitable ## ## @seealso{icdf, pdf, cdf, betacdf, binocdf, bisacdf, burrcdf, cauchycdf, ## chi2cdf, evcdf, expcdf, fcdf, gamcdf, geocdf, gevcdf, gpcdf, gumbelcdf, ## hncdf, hygecdf, invgcdf, laplacecdf, logicdf, loglcdf, logncdf, nakacdf, ## nbincdf, ncfcdf, nctcdf, ncx2cdf, normcdf, poisscdf, raylcdf, ricecdf, tcdf, ## tlscdf, tricdf, unidcdf, unifcdf, vmcdf, wblcdf} ## @end deftypefn function p = cdf (name, x, varargin) ## implemented functions persistent allDF = { ... {"beta" , "Beta"}, @betacdf, 2, ... {"bino" , "Binomial"}, @binocdf, 2, ... {"bisa" , "Birnbaum-Saunders"}, @bisacdf, 2, ... {"burr" , "Burr"}, @burrcdf, 3, ... {"cauchy" , "Cauchy"}, @cauchycdf, 2, ... {"chi2" , "Chi-squared"}, @chi2cdf, 1, ... {"ev" , "Extreme Value"}, @evcdf, 2, ... {"exp" , "Exponential"}, @expcdf, 1, ... {"f" , "F-Distribution"}, @fcdf, 2, ... {"gam" , "Gamma"}, @gamcdf, 2, ... {"geo" , "Geometric"}, @geocdf, 1, ... {"gev" , "Generalized Extreme Value"}, @gevcdf, 3, ... {"gp" , "Generalized Pareto"}, @gpcdf, 3, ... {"gumbel" , "Gumbel"}, @gumbelcdf, 2, ... {"hn" , "Half-normal"}, @hncdf, 2, ... {"hyge" , "Hypergeometric"}, @hygecdf, 3, ... {"invg" , "Inverse Gaussian"}, @invgcdf, 2, ... {"laplace" , "Laplace"}, @laplacecdf, 2, ... {"logi" , "Logistic"}, @logicdf, 2, ... {"logl" , "Log-Logistic"}, @loglcdf, 2, ... {"logn" , "Lognormal"}, @logncdf, 2, ... {"naka" , "Nakagami"}, @nakacdf, 2, ... {"nbin" , "Negative Binomial"}, @nbincdf, 2, ... {"ncf" , "Noncentral F-Distribution"}, @ncfcdf, 3, ... {"nct" , "Noncentral Student T"}, @nctcdf, 2, ... {"ncx2" , "Noncentral Chi-squared"}, @ncx2cdf, 2, ... {"norm" , "Normal"}, @normcdf, 2, ... {"poiss" , "Poisson"}, @poisscdf, 1, ... {"rayl" , "Rayleigh"}, @raylcdf, 1, ... {"rice" , "Rician"}, @ricecdf, 2, ... {"t" , "Student T"}, @tcdf, 1, ... {"tls" , "location-scale T"}, @tlscdf, 3, ... {"tri" , "Triangular"}, @tricdf, 3, ... {"unid" , "Discrete Uniform"}, @unidcdf, 1, ... {"unif" , "Uniform"}, @unifcdf, 2, ... {"vm" , "Von Mises"}, @vmcdf, 2, ... {"wbl" , "Weibull"}, @wblcdf, 2}; ## Check NAME being a char string if (! ischar (name)) error ("cdf: distribution NAME must a char string."); endif ## Check X being numeric and real if (! isnumeric (x)) error ("cdf: X must be numeric."); elseif (! isreal (x)) error ("cdf: values in X must be real."); endif ## Get number of arguments nargs = numel (varargin); ## Get available functions cdfnames = allDF(1:3:end); cdfhandl = allDF(2:3:end); cdf_args = allDF(3:3:end); ## Search for CDF function idx = cellfun (@(x)any(strcmpi (name, x)), cdfnames); if (any (idx)) if (nargs == cdf_args{idx} + 1) ## Check for "upper" option if (! strcmpi (varargin{nargs}, "upper")) error ("cdf: invalid argument for upper tail."); else ## Check that all remaining distribution parameters are numeric if (! all (cellfun (@(x)isnumeric(x), (varargin([1:nargs-1]))))) error ("cdf: distribution parameters must be numeric."); endif ## Call appropriate CDF with "upper" flag p = feval (cdfhandl{idx}, x, varargin{:}); endif elseif (nargs == cdf_args{idx}) ## Check that all distribution parameters are numeric if (! all (cellfun (@(x)isnumeric(x), (varargin)))) error ("cdf: distribution parameters must be numeric."); endif ## Call appropriate CDF without "upper" flag p = feval (cdfhandl{idx}, x, varargin{:}); else if (cdf_args{idx} == 1) error ("cdf: %s distribution requires 1 parameter.", name); else error ("cdf: %s distribution requires %d parameters.", ... name, cdf_args{idx}); endif endif else error ("cdf: %s distribution is not implemented in Statistics.", name); endif endfunction ## Test results %!shared x %! x = [1:5]; %!assert (cdf ("Beta", x, 5, 2), betacdf (x, 5, 2)) %!assert (cdf ("beta", x, 5, 2, "upper"), betacdf (x, 5, 2, "upper")) %!assert (cdf ("Binomial", x, 5, 2), binocdf (x, 5, 2)) %!assert (cdf ("bino", x, 5, 2, "upper"), binocdf (x, 5, 2, "upper")) %!assert (cdf ("Birnbaum-Saunders", x, 5, 2), bisacdf (x, 5, 2)) %!assert (cdf ("bisa", x, 5, 2, "upper"), bisacdf (x, 5, 2, "upper")) %!assert (cdf ("Burr", x, 5, 2, 2), burrcdf (x, 5, 2, 2)) %!assert (cdf ("burr", x, 5, 2, 2, "upper"), burrcdf (x, 5, 2, 2, "upper")) %!assert (cdf ("Cauchy", x, 5, 2), cauchycdf (x, 5, 2)) %!assert (cdf ("cauchy", x, 5, 2, "upper"), cauchycdf (x, 5, 2, "upper")) %!assert (cdf ("Chi-squared", x, 5), chi2cdf (x, 5)) %!assert (cdf ("chi2", x, 5, "upper"), chi2cdf (x, 5, "upper")) %!assert (cdf ("Extreme Value", x, 5, 2), evcdf (x, 5, 2)) %!assert (cdf ("ev", x, 5, 2, "upper"), evcdf (x, 5, 2, "upper")) %!assert (cdf ("Exponential", x, 5), expcdf (x, 5)) %!assert (cdf ("exp", x, 5, "upper"), expcdf (x, 5, "upper")) %!assert (cdf ("F-Distribution", x, 5, 2), fcdf (x, 5, 2)) %!assert (cdf ("f", x, 5, 2, "upper"), fcdf (x, 5, 2, "upper")) %!assert (cdf ("Gamma", x, 5, 2), gamcdf (x, 5, 2)) %!assert (cdf ("gam", x, 5, 2, "upper"), gamcdf (x, 5, 2, "upper")) %!assert (cdf ("Geometric", x, 5), geocdf (x, 5)) %!assert (cdf ("geo", x, 5, "upper"), geocdf (x, 5, "upper")) %!assert (cdf ("Generalized Extreme Value", x, 5, 2, 2), gevcdf (x, 5, 2, 2)) %!assert (cdf ("gev", x, 5, 2, 2, "upper"), gevcdf (x, 5, 2, 2, "upper")) %!assert (cdf ("Generalized Pareto", x, 5, 2, 2), gpcdf (x, 5, 2, 2)) %!assert (cdf ("gp", x, 5, 2, 2, "upper"), gpcdf (x, 5, 2, 2, "upper")) %!assert (cdf ("Gumbel", x, 5, 2), gumbelcdf (x, 5, 2)) %!assert (cdf ("gumbel", x, 5, 2, "upper"), gumbelcdf (x, 5, 2, "upper")) %!assert (cdf ("Half-normal", x, 5, 2), hncdf (x, 5, 2)) %!assert (cdf ("hn", x, 5, 2, "upper"), hncdf (x, 5, 2, "upper")) %!assert (cdf ("Hypergeometric", x, 5, 2, 2), hygecdf (x, 5, 2, 2)) %!assert (cdf ("hyge", x, 5, 2, 2, "upper"), hygecdf (x, 5, 2, 2, "upper")) %!assert (cdf ("Inverse Gaussian", x, 5, 2), invgcdf (x, 5, 2)) %!assert (cdf ("invg", x, 5, 2, "upper"), invgcdf (x, 5, 2, "upper")) %!assert (cdf ("Laplace", x, 5, 2), laplacecdf (x, 5, 2)) %!assert (cdf ("laplace", x, 5, 2, "upper"), laplacecdf (x, 5, 2, "upper")) %!assert (cdf ("Logistic", x, 5, 2), logicdf (x, 5, 2)) %!assert (cdf ("logi", x, 5, 2, "upper"), logicdf (x, 5, 2, "upper")) %!assert (cdf ("Log-Logistic", x, 5, 2), loglcdf (x, 5, 2)) %!assert (cdf ("logl", x, 5, 2, "upper"), loglcdf (x, 5, 2, "upper")) %!assert (cdf ("Lognormal", x, 5, 2), logncdf (x, 5, 2)) %!assert (cdf ("logn", x, 5, 2, "upper"), logncdf (x, 5, 2, "upper")) %!assert (cdf ("Nakagami", x, 5, 2), nakacdf (x, 5, 2)) %!assert (cdf ("naka", x, 5, 2, "upper"), nakacdf (x, 5, 2, "upper")) %!assert (cdf ("Negative Binomial", x, 5, 2), nbincdf (x, 5, 2)) %!assert (cdf ("nbin", x, 5, 2, "upper"), nbincdf (x, 5, 2, "upper")) %!assert (cdf ("Noncentral F-Distribution", x, 5, 2, 2), ncfcdf (x, 5, 2, 2)) %!assert (cdf ("ncf", x, 5, 2, 2, "upper"), ncfcdf (x, 5, 2, 2, "upper")) %!assert (cdf ("Noncentral Student T", x, 5, 2), nctcdf (x, 5, 2)) %!assert (cdf ("nct", x, 5, 2, "upper"), nctcdf (x, 5, 2, "upper")) %!assert (cdf ("Noncentral Chi-Squared", x, 5, 2), ncx2cdf (x, 5, 2)) %!assert (cdf ("ncx2", x, 5, 2, "upper"), ncx2cdf (x, 5, 2, "upper")) %!assert (cdf ("Normal", x, 5, 2), normcdf (x, 5, 2)) %!assert (cdf ("norm", x, 5, 2, "upper"), normcdf (x, 5, 2, "upper")) %!assert (cdf ("Poisson", x, 5), poisscdf (x, 5)) %!assert (cdf ("poiss", x, 5, "upper"), poisscdf (x, 5, "upper")) %!assert (cdf ("Rayleigh", x, 5), raylcdf (x, 5)) %!assert (cdf ("rayl", x, 5, "upper"), raylcdf (x, 5, "upper")) %!assert (cdf ("Rician", x, 5, 1), ricecdf (x, 5, 1)) %!assert (cdf ("rice", x, 5, 1, "upper"), ricecdf (x, 5, 1, "upper")) %!assert (cdf ("Student T", x, 5), tcdf (x, 5)) %!assert (cdf ("t", x, 5, "upper"), tcdf (x, 5, "upper")) %!assert (cdf ("location-scale T", x, 5, 1, 2), tlscdf (x, 5, 1, 2)) %!assert (cdf ("tls", x, 5, 1, 2, "upper"), tlscdf (x, 5, 1, 2, "upper")) %!assert (cdf ("Triangular", x, 5, 2, 2), tricdf (x, 5, 2, 2)) %!assert (cdf ("tri", x, 5, 2, 2, "upper"), tricdf (x, 5, 2, 2, "upper")) %!assert (cdf ("Discrete Uniform", x, 5), unidcdf (x, 5)) %!assert (cdf ("unid", x, 5, "upper"), unidcdf (x, 5, "upper")) %!assert (cdf ("Uniform", x, 5, 2), unifcdf (x, 5, 2)) %!assert (cdf ("unif", x, 5, 2, "upper"), unifcdf (x, 5, 2, "upper")) %!assert (cdf ("Von Mises", x, 5, 2), vmcdf (x, 5, 2)) %!assert (cdf ("vm", x, 5, 2, "upper"), vmcdf (x, 5, 2, "upper")) %!assert (cdf ("Weibull", x, 5, 2), wblcdf (x, 5, 2)) %!assert (cdf ("wbl", x, 5, 2, "upper"), wblcdf (x, 5, 2, "upper")) ## Test input validation %!error cdf (1) %!error cdf ({"beta"}) %!error cdf ("beta", {[1 2 3 4 5]}) %!error cdf ("beta", "text") %!error cdf ("beta", 1+i) %!error ... %! cdf ("Beta", x, "a", 2) %!error ... %! cdf ("Beta", x, 5, "") %!error ... %! cdf ("Beta", x, 5, {2}) %!error cdf ("chi2", x) %!error cdf ("Beta", x, 5) %!error cdf ("Burr", x, 5) %!error cdf ("Burr", x, 5, 2) statistics-release-1.7.3/inst/dist_wrap/fitdist.m000066400000000000000000001114251475240274700221470ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pd} =} fitdist (@var{x}, @var{distname}) ## @deftypefnx {statistics} {@var{pd} =} fitdist (@var{x}, @var{distname}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{pdca}, @var{gn}, @var{gl}] =} fitdist @ ## (@var{x}, @var{distname}, @qcode{"By"}, @var{groupvar}) ## @deftypefnx {statistics} {[@var{pdca}, @var{gn}, @var{gl}] =} fitdist @ ## (@var{x}, @var{distname}, @qcode{"By"}, @var{groupvar}, @var{Name}, @var{Value}) ## ## Create probability distribution object. ## ## @code{@var{pd} = fitdist (@var{x}, @var{distname})} creates a probability ## distribution distribution object by fitting the distribution specified by ## @var{distname} to the data in vector @var{x}. ## ## @code{@var{pd} = fitdist (@var{x}, @var{distname}, @var{Name}, @var{Value})} ## creates the probability distribution object with additional options specified ## by one or more @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"distribution"} @tab @tab A character vector specifying the ## distribution type for which to estimate parameters. ## ## @item @qcode{"Ntrials"} @tab @tab A scalar specifying the number of trials ## for the corresponding element of @var{x} for the binomial distribution. ## ## @item @qcode{"theta"} @tab @tab A scalar specifying the location parameter ## for the generalized Pareto distribution. ## ## @item @qcode{"mu"} @tab @tab A scalar specifying the location parameter ## for the half-normal distribution. ## ## @item @qcode{"censoring"} @tab @tab A vector of the same size as @var{x} ## indicating censored data in @var{x}. By default it is ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @item @qcode{"frequency"} @tab @tab A vector of nonnegative integer counts of ## the same size as @var{x} used as frequency observations. By default it is ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @item @qcode{"alpha"} @tab @tab A scalar in the range @math{(0,1)}, as the ## significance level for the confidence interval @var{pci}. By default it is ## 0.05 corresponding to 95% confidence intervals. ## ## @item @qcode{"options"} @tab @tab A structure specifying the control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. ## @end multitable ## ## @code{[@var{pdca}, @var{gn}, @var{gl}] = fitdist (@var{x}, @var{distname}, ## @qcode{"By"}, @var{groupvar})} creates probability distribution objects by ## fitting the distribution specified by @var{distname} to the data in @var{x} ## based on the grouping variable @var{groupvar}. It returns a cell array of ## fitted probability distribution object, @var{pdca}, a cell array of group ## labels, @var{gn}, and a cell array of grouping variable levels, @var{gl}. ## ## @code{[@var{pdca}, @var{gn}, @var{gl}] = fitdist (@var{x}, @var{distname}, ## @qcode{"By"}, @var{groupvar}, @var{Name}, @var{Value})} returns the same ## output arguments using additional options specified by one or more ## @qcode{Name-Value} pair arguments mentioned above. ## ## Note: calling @code{fitdist} without any input arguments will return a cell ## array of character vectors listing all supported distributions. ## ## @seealso{makedist} ## @end deftypefn function [varargout] = fitdist (varargin) ## Add list of supported probability distribution objects PDO = {'Beta'; 'Binomial'; 'BirnbaumSaunders'; 'Burr'; 'Exponential'; ... 'ExtremeValue'; 'Gamma'; 'GeneralizedExtremeValue'; ... 'GeneralizedPareto'; 'HalfNormal'; 'InverseGaussian'; ... 'Kernel'; 'Logistic'; 'Loglogistic'; 'Lognormal'; 'Nakagami'; ... 'NegativeBinomial'; 'Normal'; 'Poisson'; 'Rayleigh'; 'Rician'; ... 'Stable'; 'tLocationScale'; 'Weibull'}; ABBR = {"bisa", "ev", "gev", "gp", "hn", "invg", "nbin", "tls"}; ## Check for input arguments if (nargin == 0) varargout{1} = PDO; return elseif (nargin == 1) error ("fitdist: DISTNAME is required."); else x = varargin{1}; distname = varargin{2}; varargin([1:2]) = []; endif ## Check distribution name if (! (ischar (distname) && size (distname, 1) == 1)) error ("fitdist: DISTNAME must be a character vector."); elseif (! (any (strcmpi (distname, PDO)) || any (strcmpi (distname, ABBR)))) error ("fitdist: unrecognized distribution name."); endif ## Check data in X being a real vector if (! (isvector (x) && isnumeric (x) && isreal (x))) error ("fitdist: X must be a numeric vector of real values."); endif ## Add defaults groupvar = []; censor = zeros (size (x)); freq = ones (size (x)); alpha = 0.05; ntrials = 1; mu = 0; theta = 1; options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; ## Parse extra arguments if (mod (numel (varargin), 2) != 0) error ("fitdist: optional arguments must be in NAME-VALUE pairs."); endif while (numel (varargin) > 0) switch (tolower (varargin{1})) case "by" groupvar = varargin{2}; if (! isequal (size (x), size (groupvar)) && ! isempty (groupvar)) error (strcat (["fitdist: GROUPVAR argument must have the same"], ... [" size as the input data in X."])); endif case "censoring" censor = varargin{2}; if (! isequal (size (x), size (censor))) error (strcat (["fitdist: 'censoring' argument must have the"], ... [" same size as the input data in X."])); endif case "frequency" freq = varargin{2}; if (! isequal (size (x), size (freq))) error (strcat (["fitdist: 'frequency' argument must have the"], ... [" same size as the input data in X."])); endif if (any (freq != round (freq)) || any (freq < 0)) error (strcat (["fitdist: 'frequency' argument must contain"], ... [" non-negative integer values."])); endif case "alpha" alpha = varargin{2}; if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("fitdist: invalid value for 'alpha' argument."); endif case "ntrials" ntrials = varargin{2}; if (! (isscalar (ntrials) && isreal (ntrials) && ntrials > 0 && fix (ntrials) == ntrials)) error (strcat (["fitdist: 'ntrials' argument must be a positive"], ... [" integer scalar value."])); endif case {"mu"} mu = varargin{2}; case {"theta"} theta = varargin{2}; case "options" options = varargin{2}; if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["fitdist: 'options' argument must be a"], ... [" structure compatible for 'fminsearch'."])); endif case {"kernel", "support", "width"} warning ("fitdist: parameter not supported yet."); otherwise error ("fitdist: unknown parameter name."); endswitch varargin([1:2]) = []; endwhile ## Handle group variable if (isempty (groupvar) && nargout > 1) error ("fitdist: must define GROUPVAR for more than one output arguments."); endif if (! isempty (groupvar)) [g, gn, gl] = grp2idx (groupvar); groups = numel (gn); endif ## Switch to selected distribution switch (tolower (distname)) case "beta" if (isempty (groupvar)) varargout{1} = BetaDistribution.fit (x, alpha, freq, options); else pd = BetaDistribution.fit (x(g==1), alpha, freq(g==1), options); for i = 2:groups pd(i) = BetaDistribution.fit (x(g==i), alpha, freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "binomial" if (any (x > ntrials)) error ("fitdist: invalid NTRIALS value for Binomial distribution.") endif if (isempty (groupvar)) varargout{1} = BinomialDistribution.fit (x, ntrials, alpha, freq); else pd = BinomialDistribution.fit (x(g==1), ntrials, alpha, freq(g==1)); for i = 2:groups pd(i) = BinomialDistribution.fit (x(g==i), ntrials, alpha, freq(g==i)); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"birnbaumsaunders", "bisa"} if (isempty (groupvar)) varargout{1} = BirnbaumSaundersDistribution.fit ... (x, alpha, censor, freq, options); else pd = BirnbaumSaundersDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = BirnbaumSaundersDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "burr" if (isempty (groupvar)) varargout{1} = BurrDistribution.fit ... (x, alpha, censor, freq, options); else pd = BurrDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = BurrDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "exponential" if (isempty (groupvar)) varargout{1} = ExponentialDistribution.fit (x, alpha, censor, freq); else pd = ExponentialDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1)); for i = 2:groups pd(i) = ExponentialDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i)); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"extremevalue", "ev"} if (isempty (groupvar)) varargout{1} = ExtremeValueDistribution.fit ... (x, alpha, censor, freq, options); else pd = ExtremeValueDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = ExtremeValueDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "gamma" if (isempty (groupvar)) varargout{1} = GammaDistribution.fit (x, alpha, censor, freq, options); else pd = GammaDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = GammaDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"generalizedextremevalue", "gev"} if (isempty (groupvar)) varargout{1} = GeneralizedExtremeValueDistribution.fit ... (x, alpha, freq, options); else pd = GeneralizedExtremeValueDistribution.fit ... (x(g==1), alpha, freq(g==1), options); for i = 2:groups pd(i) = GeneralizedExtremeValueDistribution.fit ... (x(g==i), alpha, freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"generalizedpareto", "gp"} if (any (x - theta < 0)) error (strcat (["fitdist: invalid THETA value for generalized"], ... [" Pareto distribution."])); endif if (isempty (groupvar)) varargout{1} = GeneralizedParetoDistribution.fit ... (x, theta, alpha, freq, options); else pd = GeneralizedParetoDistribution.fit ... (x(g==1), theta, alpha, freq(g==1), options); for i = 2:groups pd(i) = GeneralizedParetoDistribution.fit ... (x(g==i), theta, alpha, freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"halfnormal", "hn"} if (any (x - mu < 0)) error ("fitdist: invalid MU value for half-normal distribution."); endif if (isempty (groupvar)) varargout{1} = HalfNormalDistribution.fit (x, mu, alpha, freq); else pd = HalfNormalDistribution.fit (x(g==1), mu, alpha, freq(g==1)); for i = 2:groups pd(i) = HalfNormalDistribution.fit (x(g==i), mu, alpha, freq(g==i)); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"inversegaussian", "invg"} if (isempty (groupvar)) varargout{1} = InverseGaussianDistribution.fit ... (x, alpha, censor, freq, options); else pd = InverseGaussianDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = InverseGaussianDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "kernel" warning ("fitdist: 'Kernel' distribution not supported yet."); if (isempty (groupvar)) varargout{1} = []; else varargout{1} = []; varargout{2} = gn; varargout{3} = gl; endif case "logistic" if (isempty (groupvar)) varargout{1} = LogisticDistribution.fit ... (x, alpha, censor, freq, options); else pd = LogisticDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = LogisticDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "loglogistic" if (isempty (groupvar)) varargout{1} = LoglogisticDistribution.fit ... (x, alpha, censor, freq, options); else pd = LoglogisticDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = LoglogisticDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "lognormal" if (isempty (groupvar)) varargout{1} = LognormalDistribution.fit ... (x, alpha, censor, freq, options); else pd = LognormalDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = LognormalDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "nakagami" if (isempty (groupvar)) varargout{1} = NakagamiDistribution.fit ... (x, alpha, censor, freq, options); else pd = NakagamiDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = NakagamiDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case {"negativebinomial", "nbin"} if (isempty (groupvar)) varargout{1} = NegativeBinomialDistribution.fit ... (x, alpha, freq, options); else pd = NegativeBinomialDistribution.fit ... (x(g==1), alpha, freq(g==1), options); for i = 2:groups pd(i) = NegativeBinomialDistribution.fit ... (x(g==i), alpha, freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "normal" if (isempty (groupvar)) varargout{1} = NormalDistribution.fit (x, alpha, censor, freq, options); else pd = NormalDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = NormalDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "poisson" if (isempty (groupvar)) varargout{1} = PoissonDistribution.fit (x, alpha, freq); else pd = PoissonDistribution.fit (x(g==1), alpha, freq(g==1)); for i = 2:groups pd(i) = PoissonDistribution.fit (x(g==i), alpha, freq(g==i)); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "rayleigh" if (isempty (groupvar)) varargout{1} = RayleighDistribution.fit (x, alpha, censor, freq); else pd = RayleighDistribution.fit (x(g==1), alpha, censor(g==1), freq(g==1)); for i = 2:groups pd(i) = RayleighDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i)); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "rician" if (isempty (groupvar)) varargout{1} = RicianDistribution.fit (x, alpha, censor, freq, options); else pd = RicianDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = RicianDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "stable" warning ("fitdist: 'Stable' distribution not supported yet."); if (isempty (groupvar)) varargout{1} = []; else varargout{1} = []; varargout{2} = gn; varargout{3} = gl; endif case {"tlocationscale", "tls"} if (isempty (groupvar)) varargout{1} = tLocationScaleDistribution.fit ... (x, alpha, censor, freq, options); else pd = tLocationScaleDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = tLocationScaleDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif case "weibull" if (isempty (groupvar)) varargout{1} = WeibullDistribution.fit (x, alpha, censor, freq, options); else pd = WeibullDistribution.fit ... (x(g==1), alpha, censor(g==1), freq(g==1), options); for i = 2:groups pd(i) = WeibullDistribution.fit ... (x(g==i), alpha, censor(g==i), freq(g==i), options); endfor varargout{1} = pd; varargout{2} = gn; varargout{3} = gl; endif endswitch endfunction ## Test output %!test %! x = betarnd (1, 1, 100, 1); %! pd = fitdist (x, "Beta"); %! [phat, pci] = betafit (x); %! assert ([pd.a, pd.b], phat); %! assert (paramci (pd), pci); %!test %! x1 = betarnd (1, 1, 100, 1); %! x2 = betarnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "Beta", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = betafit (x1); %! assert ([pd(1).a, pd(1).b], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = betafit (x2); %! assert ([pd(2).a, pd(2).b], phat); %! assert (paramci (pd(2)), pci); %!test %! N = 1; %! x = binornd (N, 0.5, 100, 1); %! pd = fitdist (x, "binomial"); %! [phat, pci] = binofit (sum (x), numel (x)); %! assert ([pd.N, pd.p], [N, phat]); %! assert (paramci (pd), pci); %!test %! N = 3; %! x = binornd (N, 0.4, 100, 1); %! pd = fitdist (x, "binomial", "ntrials", N); %! [phat, pci] = binofit (sum (x), numel (x) * N); %! assert ([pd.N, pd.p], [N, phat]); %! assert (paramci (pd), pci); %!test %! N = 1; %! x1 = binornd (N, 0.5, 100, 1); %! x2 = binornd (N, 0.7, 100, 1); %! pd = fitdist ([x1; x2], "binomial", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = binofit (sum (x1), numel (x1)); %! assert ([pd(1).N, pd(1).p], [N, phat]); %! assert (paramci (pd(1)), pci); %! [phat, pci] = binofit (sum (x2), numel (x2)); %! assert ([pd(2).N, pd(2).p], [N, phat]); %! assert (paramci (pd(2)), pci); %!test %! N = 5; %! x1 = binornd (N, 0.5, 100, 1); %! x2 = binornd (N, 0.8, 100, 1); %! pd = fitdist ([x1; x2], "binomial", "ntrials", N, ... %! "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = binofit (sum (x1), numel (x1) * N); %! assert ([pd(1).N, pd(1).p], [N, phat]); %! assert (paramci (pd(1)), pci); %! [phat, pci] = binofit (sum (x2), numel (x2) * N); %! assert ([pd(2).N, pd(2).p], [N, phat]); %! assert (paramci (pd(2)), pci); %!test %! x = bisarnd (1, 1, 100, 1); %! pd = fitdist (x, "BirnbaumSaunders"); %! [phat, pci] = bisafit (x); %! assert ([pd.beta, pd.gamma], phat); %! assert (paramci (pd), pci); %!test %! x1 = bisarnd (1, 1, 100, 1); %! x2 = bisarnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "bisa", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = bisafit (x1); %! assert ([pd(1).beta, pd(1).gamma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = bisafit (x2); %! assert ([pd(2).beta, pd(2).gamma], phat); %! assert (paramci (pd(2)), pci); %!test %! x = burrrnd (1, 2, 1, 100, 1); %! pd = fitdist (x, "Burr"); %! [phat, pci] = burrfit (x); %! assert ([pd.alpha, pd.c, pd.k], phat); %! assert (paramci (pd), pci); %!test %! x1 = burrrnd (1, 2, 1, 100, 1); %! x2 = burrrnd (1, 0.5, 2, 100, 1); %! pd = fitdist ([x1; x2], "burr", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = burrfit (x1); %! assert ([pd(1).alpha, pd(1).c, pd(1).k], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = burrfit (x2); %! assert ([pd(2).alpha, pd(2).c, pd(2).k], phat); %! assert (paramci (pd(2)), pci); %!test %! x = exprnd (1, 100, 1); %! pd = fitdist (x, "exponential"); %! [muhat, muci] = expfit (x); %! assert ([pd.mu], muhat); %! assert (paramci (pd), muci); %!test %! x1 = exprnd (1, 100, 1); %! x2 = exprnd (5, 100, 1); %! pd = fitdist ([x1; x2], "exponential", "By", [ones(100,1); 2*ones(100,1)]); %! [muhat, muci] = expfit (x1); %! assert ([pd(1).mu], muhat); %! assert (paramci (pd(1)), muci); %! [muhat, muci] = expfit (x2); %! assert ([pd(2).mu], muhat); %! assert (paramci (pd(2)), muci); %!test %! x = evrnd (1, 1, 100, 1); %! pd = fitdist (x, "ev"); %! [phat, pci] = evfit (x); %! assert ([pd.mu, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x1 = evrnd (1, 1, 100, 1); %! x2 = evrnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "extremevalue", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = evfit (x1); %! assert ([pd(1).mu, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = evfit (x2); %! assert ([pd(2).mu, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!test %! x = gamrnd (1, 1, 100, 1); %! pd = fitdist (x, "Gamma"); %! [phat, pci] = gamfit (x); %! assert ([pd.a, pd.b], phat); %! assert (paramci (pd), pci); %!test %! x1 = gamrnd (1, 1, 100, 1); %! x2 = gamrnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "Gamma", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = gamfit (x1); %! assert ([pd(1).a, pd(1).b], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = gamfit (x2); %! assert ([pd(2).a, pd(2).b], phat); %! assert (paramci (pd(2)), pci); %!test %! rand ("seed", 4); # for reproducibility %! x = gevrnd (-0.5, 1, 2, 1000, 1); %! pd = fitdist (x, "generalizedextremevalue"); %! [phat, pci] = gevfit (x); %! assert ([pd.k, pd.sigma, pd.mu], phat); %! assert (paramci (pd), pci); %!test %! rand ("seed", 5); # for reproducibility %! x1 = gevrnd (-0.5, 1, 2, 1000, 1); %! rand ("seed", 9); # for reproducibility %! x2 = gevrnd (0, 1, -4, 1000, 1); %! pd = fitdist ([x1; x2], "gev", "By", [ones(1000,1); 2*ones(1000,1)]); %! [phat, pci] = gevfit (x1); %! assert ([pd(1).k, pd(1).sigma, pd(1).mu], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = gevfit (x2); %! assert ([pd(2).k, pd(2).sigma, pd(2).mu], phat); %! assert (paramci (pd(2)), pci); %!test %! x = gprnd (1, 1, 1, 100, 1); %! pd = fitdist (x, "GeneralizedPareto"); %! [phat, pci] = gpfit (x, 1); %! assert ([pd.k, pd.sigma, pd.theta], phat); %! assert (paramci (pd), pci); %!test %! x = gprnd (1, 1, 2, 100, 1); %! pd = fitdist (x, "GeneralizedPareto", "theta", 2); %! [phat, pci] = gpfit (x, 2); %! assert ([pd.k, pd.sigma, pd.theta], phat); %! assert (paramci (pd), pci); %!test %! x1 = gprnd (1, 1, 1, 100, 1); %! x2 = gprnd (0, 2, 1, 100, 1); %! pd = fitdist ([x1; x2], "gp", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = gpfit (x1, 1); %! assert ([pd(1).k, pd(1).sigma, pd(1).theta], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = gpfit (x2, 1); %! assert ([pd(2).k, pd(2).sigma, pd(2).theta], phat); %! assert (paramci (pd(2)), pci); %!test %! x1 = gprnd (3, 2, 2, 100, 1); %! x2 = gprnd (2, 3, 2, 100, 1); %! pd = fitdist ([x1; x2], "GeneralizedPareto", "theta", 2, ... %! "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = gpfit (x1, 2); %! assert ([pd(1).k, pd(1).sigma, pd(1).theta], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = gpfit (x2, 2); %! assert ([pd(2).k, pd(2).sigma, pd(2).theta], phat); %! assert (paramci (pd(2)), pci); %!test %! x = hnrnd (0, 1, 100, 1); %! pd = fitdist (x, "HalfNormal"); %! [phat, pci] = hnfit (x, 0); %! assert ([pd.mu, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x = hnrnd (1, 1, 100, 1); %! pd = fitdist (x, "HalfNormal", "mu", 1); %! [phat, pci] = hnfit (x, 1); %! assert ([pd.mu, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x1 = hnrnd (0, 1, 100, 1); %! x2 = hnrnd (0, 2, 100, 1); %! pd = fitdist ([x1; x2], "HalfNormal", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = hnfit (x1, 0); %! assert ([pd(1).mu, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = hnfit (x2, 0); %! assert ([pd(2).mu, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!test %! x1 = hnrnd (2, 1, 100, 1); %! x2 = hnrnd (2, 2, 100, 1); %! pd = fitdist ([x1; x2], "HalfNormal", "mu", 2, ... %! "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = hnfit (x1, 2); %! assert ([pd(1).mu, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = hnfit (x2, 2); %! assert ([pd(2).mu, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!test %! x = invgrnd (1, 1, 100, 1); %! pd = fitdist (x, "InverseGaussian"); %! [phat, pci] = invgfit (x); %! assert ([pd.mu, pd.lambda], phat); %! assert (paramci (pd), pci); %!test %! x1 = invgrnd (1, 1, 100, 1); %! x2 = invgrnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "InverseGaussian", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = invgfit (x1); %! assert ([pd(1).mu, pd(1).lambda], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = invgfit (x2); %! assert ([pd(2).mu, pd(2).lambda], phat); %! assert (paramci (pd(2)), pci); %!test %! x = logirnd (1, 1, 100, 1); %! pd = fitdist (x, "logistic"); %! [phat, pci] = logifit (x); %! assert ([pd.mu, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x1 = logirnd (1, 1, 100, 1); %! x2 = logirnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "logistic", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = logifit (x1); %! assert ([pd(1).mu, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = logifit (x2); %! assert ([pd(2).mu, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!test %! x = loglrnd (1, 1, 100, 1); %! pd = fitdist (x, "loglogistic"); %! [phat, pci] = loglfit (x); %! assert ([pd.mu, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x1 = loglrnd (1, 1, 100, 1); %! x2 = loglrnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "loglogistic", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = loglfit (x1); %! assert ([pd(1).mu, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = loglfit (x2); %! assert ([pd(2).mu, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!test %! x = lognrnd (1, 1, 100, 1); %! pd = fitdist (x, "lognormal"); %! [phat, pci] = lognfit (x); %! assert ([pd.mu, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x1 = lognrnd (1, 1, 100, 1); %! x2 = lognrnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "lognormal", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = lognfit (x1); %! assert ([pd(1).mu, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = lognfit (x2); %! assert ([pd(2).mu, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!test %! x = nakarnd (2, 0.5, 100, 1); %! pd = fitdist (x, "Nakagami"); %! [phat, pci] = nakafit (x); %! assert ([pd.mu, pd.omega], phat); %! assert (paramci (pd), pci); %!test %! x1 = nakarnd (2, 0.5, 100, 1); %! x2 = nakarnd (5, 0.8, 100, 1); %! pd = fitdist ([x1; x2], "Nakagami", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = nakafit (x1); %! assert ([pd(1).mu, pd(1).omega], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = nakafit (x2); %! assert ([pd(2).mu, pd(2).omega], phat); %! assert (paramci (pd(2)), pci); %!test %! randp ("seed", 123); %! randg ("seed", 321); %! x = nbinrnd (2, 0.5, 100, 1); %! pd = fitdist (x, "negativebinomial"); %! [phat, pci] = nbinfit (x); %! assert ([pd.R, pd.P], phat); %! assert (paramci (pd), pci); %!test %! randp ("seed", 345); %! randg ("seed", 543); %! x1 = nbinrnd (2, 0.5, 100, 1); %! randp ("seed", 432); %! randg ("seed", 234); %! x2 = nbinrnd (5, 0.8, 100, 1); %! pd = fitdist ([x1; x2], "nbin", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = nbinfit (x1); %! assert ([pd(1).R, pd(1).P], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = nbinfit (x2); %! assert ([pd(2).R, pd(2).P], phat); %! assert (paramci (pd(2)), pci); %!test %! x = normrnd (1, 1, 100, 1); %! pd = fitdist (x, "normal"); %! [muhat, sigmahat, muci, sigmaci] = normfit (x); %! assert ([pd.mu, pd.sigma], [muhat, sigmahat]); %! assert (paramci (pd), [muci, sigmaci]); %!test %! x1 = normrnd (1, 1, 100, 1); %! x2 = normrnd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "normal", "By", [ones(100,1); 2*ones(100,1)]); %! [muhat, sigmahat, muci, sigmaci] = normfit (x1); %! assert ([pd(1).mu, pd(1).sigma], [muhat, sigmahat]); %! assert (paramci (pd(1)), [muci, sigmaci]); %! [muhat, sigmahat, muci, sigmaci] = normfit (x2); %! assert ([pd(2).mu, pd(2).sigma], [muhat, sigmahat]); %! assert (paramci (pd(2)), [muci, sigmaci]); %!test %! x = poissrnd (1, 100, 1); %! pd = fitdist (x, "poisson"); %! [phat, pci] = poissfit (x); %! assert (pd.lambda, phat); %! assert (paramci (pd), pci); %!test %! x1 = poissrnd (1, 100, 1); %! x2 = poissrnd (5, 100, 1); %! pd = fitdist ([x1; x2], "poisson", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = poissfit (x1); %! assert (pd(1).lambda, phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = poissfit (x2); %! assert (pd(2).lambda, phat); %! assert (paramci (pd(2)), pci); %!test %! x = raylrnd (1, 100, 1); %! pd = fitdist (x, "rayleigh"); %! [phat, pci] = raylfit (x); %! assert (pd.sigma, phat); %! assert (paramci (pd), pci); %!test %! x1 = raylrnd (1, 100, 1); %! x2 = raylrnd (5, 100, 1); %! pd = fitdist ([x1; x2], "rayleigh", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = raylfit (x1); %! assert ( pd(1).sigma, phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = raylfit (x2); %! assert (pd(2).sigma, phat); %! assert (paramci (pd(2)), pci); %!test %! x = ricernd (1, 1, 100, 1); %! pd = fitdist (x, "rician"); %! [phat, pci] = ricefit (x); %! assert ([pd.s, pd.sigma], phat); %! assert (paramci (pd), pci); %!test %! x1 = ricernd (1, 1, 100, 1); %! x2 = ricernd (5, 2, 100, 1); %! pd = fitdist ([x1; x2], "rician", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = ricefit (x1); %! assert ([pd(1).s, pd(1).sigma], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = ricefit (x2); %! assert ([pd(2).s, pd(2).sigma], phat); %! assert (paramci (pd(2)), pci); %!warning ... %! fitdist ([1 2 3 4 5], "Stable"); %!test %! x = tlsrnd (0, 1, 1, 100, 1); %! pd = fitdist (x, "tlocationscale"); %! [phat, pci] = tlsfit (x); %! assert ([pd.mu, pd.sigma, pd.nu], phat); %! assert (paramci (pd), pci); %!test %! x1 = tlsrnd (0, 1, 1, 100, 1); %! x2 = tlsrnd (5, 2, 1, 100, 1); %! pd = fitdist ([x1; x2], "tlocationscale", "By", [ones(100,1); 2*ones(100,1)]); %! [phat, pci] = tlsfit (x1); %! assert ([pd(1).mu, pd(1).sigma, pd(1).nu], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = tlsfit (x2); %! assert ([pd(2).mu, pd(2).sigma, pd(2).nu], phat); %! assert (paramci (pd(2)), pci); %!test %! x = [1 2 3 4 5]; %! pd = fitdist (x, "weibull"); %! [phat, pci] = wblfit (x); %! assert ([pd.lambda, pd.k], phat); %! assert (paramci (pd), pci); %!test %! x = [1 2 3 4 5 6 7 8 9 10]; %! pd = fitdist (x, "weibull", "By", [1 1 1 1 1 2 2 2 2 2]); %! [phat, pci] = wblfit (x(1:5)); %! assert ([pd(1).lambda, pd(1).k], phat); %! assert (paramci (pd(1)), pci); %! [phat, pci] = wblfit (x(6:10)); %! assert ([pd(2).lambda, pd(2).k], phat); %! assert (paramci (pd(2)), pci); ## Test input validation %!error fitdist (1) %!error fitdist (1, ["as";"sd"]) %!error fitdist (1, "some") %!error ... %! fitdist (ones (2), "normal") %!error ... %! fitdist ([i, 2, 3], "normal") %!error ... %! fitdist (["a", "s", "d"], "normal") %!error ... %! fitdist ([1, 2, 3], "normal", "By") %!error ... %! fitdist ([1, 2, 3], "normal", "By", [1, 2]) %!error ... %! fitdist ([1, 2, 3], "normal", "Censoring", [1, 2]) %!error ... %! fitdist ([1, 2, 3], "normal", "frequency", [1, 2]) %!error ... %! fitdist ([1, 2, 3], "negativebinomial", "frequency", [1, -2, 3]) %!error ... %! fitdist ([1, 2, 3], "normal", "alpha", [1, 2]) %!error ... %! fitdist ([1, 2, 3], "normal", "alpha", i) %!error ... %! fitdist ([1, 2, 3], "normal", "alpha", -0.5) %!error ... %! fitdist ([1, 2, 3], "normal", "alpha", 1.5) %!error ... %! fitdist ([1, 2, 3], "normal", "ntrials", [1, 2]) %!error ... %! fitdist ([1, 2, 3], "normal", "ntrials", 0) %!error ... %! fitdist ([1, 2, 3], "normal", "options", 0) %!error ... %! fitdist ([1, 2, 3], "normal", "options", struct ("options", 1)) %!warning fitdist ([1, 2, 3], "kernel", "kernel", "normal"); %!warning fitdist ([1, 2, 3], "kernel", "support", "positive"); %!warning fitdist ([1, 2, 3], "kernel", "width", 1); %!error ... %! fitdist ([1, 2, 3], "normal", "param", struct ("options", 1)) %!error ... %! [pdca, gn, gl] = fitdist ([1, 2, 3], "normal"); %!error ... %! fitdist ([1, 2, 3], "generalizedpareto", "theta", 2); %!error ... %! fitdist ([1, 2, 3], "halfnormal", "mu", 2); statistics-release-1.7.3/inst/dist_wrap/icdf.m000066400000000000000000000341501475240274700214050ustar00rootroot00000000000000# Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} icdf (@var{name}, @var{p}, @var{A}) ## @deftypefnx {statistics} {@var{x} =} icdf (@var{name}, @var{p}, @var{A}, @var{B}) ## @deftypefnx {statistics} {@var{x} =} icdf (@var{name}, @var{p}, @var{A}, @var{B}, @var{C}) ## ## Return the inverse CDF of a univariate distribution evaluated at @var{p}. ## ## @code{icdf} is a wrapper for the univariate quantile distribution functions ## (iCDF) available in the statistics package. See the corresponding functions' ## help to learn the signification of the parameters after @var{p}. ## ## @code{@var{x} = icdf (@var{name}, @var{p}, @var{A})} returns the iCDF for the ## one-parameter distribution family specified by @var{name} and the ## distribution parameter @var{A}, evaluated at the values in @var{p}. ## ## @code{@var{x} = icdf (@var{name}, @var{p}, @var{A}, @var{B})} returns the ## iCDF for the two-parameter distribution family specified by @var{name} and ## the distribution parameters @var{A} and @var{B}, evaluated at the values in ## @var{p}. ## ## @code{@var{x} = icdf (@var{name}, @var{p}, @var{A}, @var{B}, @var{C})} ## returns the iCDF for the three-parameter distribution family specified by ## @var{name} and the distribution parameters @var{A}, @var{B}, and @var{C}, ## evaluated at the values in @var{p}. ## ## @var{name} must be a char string of the name or the abbreviation of the ## desired quantile distribution function as listed in the followng table. ## The last column shows the number of required parameters that should be parsed ## after @var{x} to the desired iCDF. ## ## @multitable @columnfractions 0.4 0.05 0.2 0.05 0.3 ## @headitem Distribution Name @tab @tab Abbreviation @tab @tab Input Parameters ## @item @qcode{"Beta"} @tab @tab @qcode{"beta"} @tab @tab 2 ## @item @qcode{"Binomial"} @tab @tab @qcode{"bino"} @tab @tab 2 ## @item @qcode{"Birnbaum-Saunders"} @tab @tab @qcode{"bisa"} @tab @tab 2 ## @item @qcode{"Burr"} @tab @tab @qcode{"burr"} @tab @tab 3 ## @item @qcode{"Cauchy"} @tab @tab @qcode{"cauchy"} @tab @tab 2 ## @item @qcode{"Chi-squared"} @tab @tab @qcode{"chi2"} @tab @tab 1 ## @item @qcode{"Extreme Value"} @tab @tab @qcode{"ev"} @tab @tab 2 ## @item @qcode{"Exponential"} @tab @tab @qcode{"exp"} @tab @tab 1 ## @item @qcode{"F-Distribution"} @tab @tab @qcode{"f"} @tab @tab 2 ## @item @qcode{"Gamma"} @tab @tab @qcode{"gam"} @tab @tab 2 ## @item @qcode{"Geometric"} @tab @tab @qcode{"geo"} @tab @tab 1 ## @item @qcode{"Generalized Extreme Value"} @tab @tab @qcode{"gev"} @tab @tab 3 ## @item @qcode{"Generalized Pareto"} @tab @tab @qcode{"gp"} @tab @tab 3 ## @item @qcode{"Gumbel"} @tab @tab @qcode{"gumbel"} @tab @tab 2 ## @item @qcode{"Half-normal"} @tab @tab @qcode{"hn"} @tab @tab 2 ## @item @qcode{"Hypergeometric"} @tab @tab @qcode{"hyge"} @tab @tab 3 ## @item @qcode{"Inverse Gaussian"} @tab @tab @qcode{"invg"} @tab @tab 2 ## @item @qcode{"Laplace"} @tab @tab @qcode{"laplace"} @tab @tab 2 ## @item @qcode{"Logistic"} @tab @tab @qcode{"logi"} @tab @tab 2 ## @item @qcode{"Log-Logistic"} @tab @tab @qcode{"logl"} @tab @tab 2 ## @item @qcode{"Lognormal"} @tab @tab @qcode{"logn"} @tab @tab 2 ## @item @qcode{"Nakagami"} @tab @tab @qcode{"naka"} @tab @tab 2 ## @item @qcode{"Negative Binomial"} @tab @tab @qcode{"nbin"} @tab @tab 2 ## @item @qcode{"Noncentral F-Distribution"} @tab @tab @qcode{"ncf"} @tab @tab 3 ## @item @qcode{"Noncentral Student T"} @tab @tab @qcode{"nct"} @tab @tab 2 ## @item @qcode{"Noncentral Chi-Squared"} @tab @tab @qcode{"ncx2"} @tab @tab 2 ## @item @qcode{"Normal"} @tab @tab @qcode{"norm"} @tab @tab 2 ## @item @qcode{"Poisson"} @tab @tab @qcode{"poiss"} @tab @tab 1 ## @item @qcode{"Rayleigh"} @tab @tab @qcode{"rayl"} @tab @tab 1 ## @item @qcode{"Rician"} @tab @tab @qcode{"rice"} @tab @tab 2 ## @item @qcode{"Student T"} @tab @tab @qcode{"t"} @tab @tab 1 ## @item @qcode{"location-scale T"} @tab @tab @qcode{"tls"} @tab @tab 3 ## @item @qcode{"Triangular"} @tab @tab @qcode{"tri"} @tab @tab 3 ## @item @qcode{"Discrete Uniform"} @tab @tab @qcode{"unid"} @tab @tab 1 ## @item @qcode{"Uniform"} @tab @tab @qcode{"unif"} @tab @tab 2 ## @item @qcode{"Von Mises"} @tab @tab @qcode{"vm"} @tab @tab 2 ## @item @qcode{"Weibull"} @tab @tab @qcode{"wbl"} @tab @tab 2 ## @end multitable ## ## @seealso{icdf, pdf, random, betainv, binoinv, bisainv, burrinv, cauchyinv, ## chi2inv, evinv, expinv, finv, gaminv, geoinv, gevinv, gpinv, gumbelinv, ## hninv, hygeinv, invginv, laplaceinv, logiinv, loglinv, logninv, nakainv, ## nbininv, ncfinv, nctinv, ncx2inv, norminv, poissinv, raylinv, riceinv, tinv, ## tlsinv, triinv, unidinv, unifinv, vminv, wblinv} ## @end deftypefn function x = icdf (name, p, varargin) ## implemented functions persistent allDF = { ... {"beta" , "Beta"}, @betainv, 2, ... {"bino" , "Binomial"}, @binoinv, 2, ... {"bisa" , "Birnbaum-Saunders"}, @bisainv, 2, ... {"burr" , "Burr"}, @burrinv, 3, ... {"cauchy" , "Cauchy"}, @cauchyinv, 2, ... {"chi2" , "Chi-squared"}, @chi2inv, 1, ... {"ev" , "Extreme Value"}, @evinv, 2, ... {"exp" , "Exponential"}, @expinv, 1, ... {"f" , "F-Distribution"}, @finv, 2, ... {"gam" , "Gamma"}, @gaminv, 2, ... {"geo" , "Geometric"}, @geoinv, 1, ... {"gev" , "Generalized Extreme Value"}, @gevinv, 3, ... {"gp" , "Generalized Pareto"}, @gpinv, 3, ... {"gumbel" , "Gumbel"}, @gumbelinv, 2, ... {"hn" , "Half-normal"}, @hninv, 2, ... {"hyge" , "Hypergeometric"}, @hygeinv, 3, ... {"invg" , "Inverse Gaussian"}, @invginv, 2, ... {"laplace" , "Laplace"}, @laplaceinv, 2, ... {"logi" , "Logistic"}, @logiinv, 2, ... {"logl" , "Log-Logistic"}, @loglinv, 2, ... {"logn" , "Lognormal"}, @logninv, 2, ... {"naka" , "Nakagami"}, @nakainv, 2, ... {"nbin" , "Negative Binomial"}, @nbininv, 2, ... {"ncf" , "Noncentral F-Distribution"}, @ncfinv, 3, ... {"nct" , "Noncentral Student T"}, @nctinv, 2, ... {"ncx2" , "Noncentral Chi-squared"}, @ncx2inv, 2, ... {"norm" , "Normal"}, @norminv, 2, ... {"poiss" , "Poisson"}, @poissinv, 1, ... {"rayl" , "Rayleigh"}, @raylinv, 1, ... {"rice" , "Rician"}, @riceinv, 2, ... {"t" , "Student T"}, @tinv, 1, ... {"tls" , "location-scale T"}, @tlsinv, 3, ... {"tri" , "Triangular"}, @triinv, 3, ... {"unid" , "Discrete Uniform"}, @unidinv, 1, ... {"unif" , "Uniform"}, @unifinv, 2, ... {"vm" , "Von Mises"}, @vminv, 2, ... {"wbl" , "Weibull"}, @wblinv, 2}; if (! ischar (name)) error ("icdf: distribution NAME must a char string."); endif ## Check P being numeric and real if (! isnumeric (p)) error ("icdf: P must be numeric."); elseif (! isreal (p)) error ("icdf: values in P must be real."); endif ## Get number of arguments nargs = numel (varargin); ## Get available functions icdfnames = allDF(1:3:end); icdfhandl = allDF(2:3:end); icdf_args = allDF(3:3:end); ## Search for iCDF function idx = cellfun (@(x)any(strcmpi (name, x)), icdfnames); if (any (idx)) if (nargs == icdf_args{idx}) ## Check that all distribution parameters are numeric if (! all (cellfun (@(x)isnumeric(x), (varargin)))) error ("icdf: distribution parameters must be numeric."); endif ## Call appropriate iCDF x = feval (icdfhandl{idx}, p, varargin{:}); else if (icdf_args{idx} == 1) error ("icdf: %s distribution requires 1 parameter.", name); else error ("icdf: %s distribution requires %d parameters.", ... name, icdf_args{idx}); endif endif else error ("icdf: %s distribution is not implemented in Statistics.", name); endif endfunction ## Test results %!shared p %! p = [0.05:0.05:0.5]; %!assert (icdf ("Beta", p, 5, 2), betainv (p, 5, 2)) %!assert (icdf ("beta", p, 5, 2), betainv (p, 5, 2)) %!assert (icdf ("Binomial", p, 5, 2), binoinv (p, 5, 2)) %!assert (icdf ("bino", p, 5, 2), binoinv (p, 5, 2)) %!assert (icdf ("Birnbaum-Saunders", p, 5, 2), bisainv (p, 5, 2)) %!assert (icdf ("bisa", p, 5, 2), bisainv (p, 5, 2)) %!assert (icdf ("Burr", p, 5, 2, 2), burrinv (p, 5, 2, 2)) %!assert (icdf ("burr", p, 5, 2, 2), burrinv (p, 5, 2, 2)) %!assert (icdf ("Cauchy", p, 5, 2), cauchyinv (p, 5, 2)) %!assert (icdf ("cauchy", p, 5, 2), cauchyinv (p, 5, 2)) %!assert (icdf ("Chi-squared", p, 5), chi2inv (p, 5)) %!assert (icdf ("chi2", p, 5), chi2inv (p, 5)) %!assert (icdf ("Extreme Value", p, 5, 2), evinv (p, 5, 2)) %!assert (icdf ("ev", p, 5, 2), evinv (p, 5, 2)) %!assert (icdf ("Exponential", p, 5), expinv (p, 5)) %!assert (icdf ("exp", p, 5), expinv (p, 5)) %!assert (icdf ("F-Distribution", p, 5, 2), finv (p, 5, 2)) %!assert (icdf ("f", p, 5, 2), finv (p, 5, 2)) %!assert (icdf ("Gamma", p, 5, 2), gaminv (p, 5, 2)) %!assert (icdf ("gam", p, 5, 2), gaminv (p, 5, 2)) %!assert (icdf ("Geometric", p, 5), geoinv (p, 5)) %!assert (icdf ("geo", p, 5), geoinv (p, 5)) %!assert (icdf ("Generalized Extreme Value", p, 5, 2, 2), gevinv (p, 5, 2, 2)) %!assert (icdf ("gev", p, 5, 2, 2), gevinv (p, 5, 2, 2)) %!assert (icdf ("Generalized Pareto", p, 5, 2, 2), gpinv (p, 5, 2, 2)) %!assert (icdf ("gp", p, 5, 2, 2), gpinv (p, 5, 2, 2)) %!assert (icdf ("Gumbel", p, 5, 2), gumbelinv (p, 5, 2)) %!assert (icdf ("gumbel", p, 5, 2), gumbelinv (p, 5, 2)) %!assert (icdf ("Half-normal", p, 5, 2), hninv (p, 5, 2)) %!assert (icdf ("hn", p, 5, 2), hninv (p, 5, 2)) %!assert (icdf ("Hypergeometric", p, 5, 2, 2), hygeinv (p, 5, 2, 2)) %!assert (icdf ("hyge", p, 5, 2, 2), hygeinv (p, 5, 2, 2)) %!assert (icdf ("Inverse Gaussian", p, 5, 2), invginv (p, 5, 2)) %!assert (icdf ("invg", p, 5, 2), invginv (p, 5, 2)) %!assert (icdf ("Laplace", p, 5, 2), laplaceinv (p, 5, 2)) %!assert (icdf ("laplace", p, 5, 2), laplaceinv (p, 5, 2)) %!assert (icdf ("Logistic", p, 5, 2), logiinv (p, 5, 2)) %!assert (icdf ("logi", p, 5, 2), logiinv (p, 5, 2)) %!assert (icdf ("Log-Logistic", p, 5, 2), loglinv (p, 5, 2)) %!assert (icdf ("logl", p, 5, 2), loglinv (p, 5, 2)) %!assert (icdf ("Lognormal", p, 5, 2), logninv (p, 5, 2)) %!assert (icdf ("logn", p, 5, 2), logninv (p, 5, 2)) %!assert (icdf ("Nakagami", p, 5, 2), nakainv (p, 5, 2)) %!assert (icdf ("naka", p, 5, 2), nakainv (p, 5, 2)) %!assert (icdf ("Negative Binomial", p, 5, 2), nbininv (p, 5, 2)) %!assert (icdf ("nbin", p, 5, 2), nbininv (p, 5, 2)) %!assert (icdf ("Noncentral F-Distribution", p, 5, 2, 2), ncfinv (p, 5, 2, 2)) %!assert (icdf ("ncf", p, 5, 2, 2), ncfinv (p, 5, 2, 2)) %!assert (icdf ("Noncentral Student T", p, 5, 2), nctinv (p, 5, 2)) %!assert (icdf ("nct", p, 5, 2), nctinv (p, 5, 2)) %!assert (icdf ("Noncentral Chi-Squared", p, 5, 2), ncx2inv (p, 5, 2)) %!assert (icdf ("ncx2", p, 5, 2), ncx2inv (p, 5, 2)) %!assert (icdf ("Normal", p, 5, 2), norminv (p, 5, 2)) %!assert (icdf ("norm", p, 5, 2), norminv (p, 5, 2)) %!assert (icdf ("Poisson", p, 5), poissinv (p, 5)) %!assert (icdf ("poiss", p, 5), poissinv (p, 5)) %!assert (icdf ("Rayleigh", p, 5), raylinv (p, 5)) %!assert (icdf ("rayl", p, 5), raylinv (p, 5)) %!assert (icdf ("Rician", p, 5, 1), riceinv (p, 5, 1)) %!assert (icdf ("rice", p, 5, 1), riceinv (p, 5, 1)) %!assert (icdf ("Student T", p, 5), tinv (p, 5)) %!assert (icdf ("t", p, 5), tinv (p, 5)) %!assert (icdf ("location-scale T", p, 5, 1, 2), tlsinv (p, 5, 1, 2)) %!assert (icdf ("tls", p, 5, 1, 2), tlsinv (p, 5, 1, 2)) %!assert (icdf ("Triangular", p, 5, 2, 2), triinv (p, 5, 2, 2)) %!assert (icdf ("tri", p, 5, 2, 2), triinv (p, 5, 2, 2)) %!assert (icdf ("Discrete Uniform", p, 5), unidinv (p, 5)) %!assert (icdf ("unid", p, 5), unidinv (p, 5)) %!assert (icdf ("Uniform", p, 5, 2), unifinv (p, 5, 2)) %!assert (icdf ("unif", p, 5, 2), unifinv (p, 5, 2)) %!assert (icdf ("Von Mises", p, 5, 2), vminv (p, 5, 2)) %!assert (icdf ("vm", p, 5, 2), vminv (p, 5, 2)) %!assert (icdf ("Weibull", p, 5, 2), wblinv (p, 5, 2)) %!assert (icdf ("wbl", p, 5, 2), wblinv (p, 5, 2)) ## Test input validation %!error icdf (1) %!error icdf ({"beta"}) %!error icdf ("beta", {[1 2 3 4 5]}) %!error icdf ("beta", "text") %!error icdf ("beta", 1+i) %!error ... %! icdf ("Beta", p, "a", 2) %!error ... %! icdf ("Beta", p, 5, "") %!error ... %! icdf ("Beta", p, 5, {2}) %!error icdf ("chi2", p) %!error icdf ("Beta", p, 5) %!error icdf ("Burr", p, 5) %!error icdf ("Burr", p, 5, 2) statistics-release-1.7.3/inst/dist_wrap/makedist.m000066400000000000000000000773241475240274700223130ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pd} =} makedist (@var{distname}) ## @deftypefnx {statistics} {@var{pd} =} makedist (@var{distname}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {@var{list} =} makedist ## ## Create probability distribution object. ## ## @code{@var{pd} = makedist (@var{distname})} creates a probability ## distribution object for the distribution specified in @var{distname}, using ## the default parameter values. ## ## @code{@var{pd} = makedist (@var{distname}, @var{Name}, @var{Value})} also ## creates a probability distribution object with one or more distribution ## parameter values specified by @qcode{Name-Value} pair arguments. ## ## @code{@var{list} = makedist} returns a cell array, @var{list}, containing a ## list of the probability distributions that makedist can create. ## ## @seealso{fitdist} ## @end deftypefn function pd = makedist (varargin) ## Add list of supported probability distribution objects PDO = {'Beta'; 'Binomial'; 'BirnbaumSaunders'; 'Burr'; 'Exponential'; ... 'ExtremeValue'; 'Gamma'; 'GeneralizedExtremeValue'; ... 'GeneralizedPareto'; 'HalfNormal'; 'InverseGaussian'; ... 'Logistic'; 'Loglogistic'; 'Lognormal'; 'Loguniform'; ... 'Multinomial'; 'Nakagami'; 'NegativeBinomial'; 'Normal'; ... 'PiecewiseLinear'; 'Poisson'; 'Rayleigh'; 'Rician'; ... 'Stable'; 'tLocationScale'; 'Triangular'; 'Uniform'; 'Weibull'}; ABBR = {"bisa", "ev", "gev", "gp", "hn", "invg", "nbin", "tls"}; ## Check for input arguments if (nargin == 0) pd = PDO; return else distname = varargin{1}; varargin(1) = []; endif ## Check distribution name if (! (ischar (distname) && size (distname, 1) == 1)) error ("makedist: DISTNAME must be a character vector."); elseif (! (any (strcmpi (distname, PDO)) || any (strcmpi (distname, ABBR)))) error ("makedist: unrecognized distribution name."); endif ## Check for additional arguments being in pairs if (mod (numel (varargin), 2) != 0) error ("makedist: optional arguments must be in NAME-VALUE pairs."); endif ## Switch to selected distribution switch (tolower (distname)) case "beta" ## Add default parameters a = 1; b = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "a" a = varargin{2}; case "b" b = varargin{2}; otherwise error ("makedist: unknown parameter for 'Beta' distribution."); endswitch varargin([1:2]) = []; endwhile pd = BetaDistribution (a, b); case "binomial" N = 1; p = 0.5; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "n" N = varargin{2}; case "p" p = varargin{2}; otherwise error ("makedist: unknown parameter for 'Binomial' distribution."); endswitch varargin([1:2]) = []; endwhile pd = BinomialDistribution (N, p); case {"birnbaumsaunders", "bisa"} beta = 1; gamma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "beta" beta = varargin{2}; case "gamma" gamma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'BirnbaumSaunders' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = BirnbaumSaundersDistribution (beta, gamma); case "burr" alpha = 1; c = 1; k = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case {"lambda", "alpha"} alpha = varargin{2}; case "c" c = varargin{2}; case "k" k = varargin{2}; otherwise error ("makedist: unknown parameter for 'Burr' distribution."); endswitch varargin([1:2]) = []; endwhile pd = BurrDistribution (alpha, c, k); case "exponential" mu = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Exponential' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = ExponentialDistribution (mu); case {"extremevalue", "ev"} mu = 0; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'ExtremeValue' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = ExtremeValueDistribution (mu, sigma); case "gamma" a = 1; b = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "a" a = varargin{2}; case "b" b = varargin{2}; otherwise error ("makedist: unknown parameter for 'Gamma' distribution."); endswitch varargin([1:2]) = []; endwhile pd = GammaDistribution (a, b); case {"generalizedextremevalue", "gev"} k = 0; sigma = 1; mu = 0; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "k" k = varargin{2}; case "sigma" sigma = varargin{2}; case "mu" mu = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'GeneralizedExtremeValue' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = GeneralizedExtremeValueDistribution (k, sigma, mu); case {"generalizedpareto", "gp"} k = 1; sigma = 1; theta = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "k" k = varargin{2}; case "sigma" sigma = varargin{2}; case "theta" theta = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'GeneralizedPareto' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = GeneralizedParetoDistribution (k, sigma, theta); case {"halfnormal", "hn"} mu = 0; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'HalfNormal' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = HalfNormalDistribution (mu, sigma); case {"inversegaussian", "invg"} mu = 1; lambda = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "lambda" lambda = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'InverseGaussian' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = InverseGaussianDistribution (mu, lambda); case "logistic" mu = 0; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Logistic' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = LogisticDistribution (mu, sigma); case "loglogistic" mu = 0; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Loglogistic' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = LoglogisticDistribution (mu, sigma); case "lognormal" mu = 0; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Lognormal' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = LognormalDistribution (mu, sigma); case "loguniform" lower = 1; upper = 4; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "lower" lower = varargin{2}; case "upper" upper = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Loguniform' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = LoguniformDistribution (lower, upper); case "multinomial" probs = [0.5, 0.5]; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "probabilities" probs = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Multinomial' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = MultinomialDistribution (probs); case "nakagami" mu = 1; omega = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "omega" omega = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Nakagami' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = NakagamiDistribution (mu, omega); case {"negativebinomial", "nbin"} R = 1; P = 0.5; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "r" R = varargin{2}; case {"ps", "p"} P = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'NegativeBinomial' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = NegativeBinomialDistribution (R, P); case "normal" mu = 0; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "mu" mu = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error ("makedist: unknown parameter for 'Normal' distribution."); endswitch varargin([1:2]) = []; endwhile pd = NormalDistribution (mu, sigma); case "piecewiselinear" x = [0, 1]; Fx = [0, 1]; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "x" x = varargin{2}; case "fx" Fx = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'PiecewiseLinear' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = PiecewiseLinearDistribution (x, Fx); case "poisson" lambda = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "lambda" lambda = varargin{2}; otherwise error ("makedist: unknown parameter for 'Poisson' distribution."); endswitch varargin([1:2]) = []; endwhile pd = PoissonDistribution (lambda); case "rayleigh" sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case {"sigma", "b"} sigma = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Rayleigh' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = RayleighDistribution (sigma); case "rician" s = 1; sigma = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "s" s = varargin{2}; case "sigma" sigma = varargin{2}; otherwise error ("makedist: unknown parameter for 'Rician' distribution."); endswitch varargin([1:2]) = []; endwhile pd = RicianDistribution (s, sigma); case "stable" alpha = 2; beta = 0; gam = 1; delta = 0; while (numel (varargin) > 0) switch (tolower (varargin{1})) case {"alpha", "s"} alpha = varargin{2}; case "beta" beta = varargin{2}; case "gam" gam = varargin{2}; case "delta" delta = varargin{2}; otherwise error ("makedist: unknown parameter for 'Stable' distribution."); endswitch varargin([1:2]) = []; endwhile warning ("makedist: 'Stable' distribution not supported yet."); pd = []; case {"tlocationscale", "tls"} mu = 0; sigma = 1; df = 5; while (numel (varargin) > 0) switch (tolower (varargin{1})) case {"mu", "s"} mu = varargin{2}; case "sigma" sigma = varargin{2}; case {"df", "nu"} df = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'tLocationScale' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = tLocationScaleDistribution (mu, sigma, df); case "triangular" A = 0; B = 0.5; C = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "a" A = varargin{2}; case "b" B = varargin{2}; case "c" C = varargin{2}; otherwise error (strcat (["makedist: unknown parameter for"], ... [" 'Triangular' distribution."])); endswitch varargin([1:2]) = []; endwhile pd = TriangularDistribution (A, B, C); case "uniform" Lower = 0; Upper = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "lower" Lower = varargin{2}; case "upper" Upper = varargin{2}; otherwise error ("makedist: unknown parameter for 'Uniform' distribution."); endswitch varargin([1:2]) = []; endwhile pd = UniformDistribution (Lower, Upper); case "weibull" lambda = 1; k = 1; while (numel (varargin) > 0) switch (tolower (varargin{1})) case {"lambda", "a"} lambda = varargin{2}; case {"k", "b"} k = varargin{2}; otherwise error ("makedist: unknown parameter for 'Weibull' distribution."); endswitch varargin([1:2]) = []; endwhile pd = WeibullDistribution (lambda, k); endswitch endfunction ## Test output %!test %! pd = makedist ("beta"); %! assert (class (pd), "BetaDistribution"); %! assert (pd.a, 1); %! assert (pd.b, 1); %!test %! pd = makedist ("beta", "a", 5); %! assert (pd.a, 5); %! assert (pd.b, 1); %!test %! pd = makedist ("beta", "b", 5); %! assert (pd.a, 1); %! assert (pd.b, 5); %!test %! pd = makedist ("beta", "a", 3, "b", 5); %! assert (pd.a, 3); %! assert (pd.b, 5); %!test %! pd = makedist ("binomial"); %! assert (class (pd), "BinomialDistribution"); %! assert (pd.N, 1); %! assert (pd.p, 0.5); %!test %! pd = makedist ("binomial", "N", 5); %! assert (pd.N, 5); %! assert (pd.p, 0.5); %!test %! pd = makedist ("binomial", "p", 0.2); %! assert (pd.N, 1); %! assert (pd.p, 0.2); %!test %! pd = makedist ("binomial", "N", 3, "p", 0.3); %! assert (pd.N, 3); %! assert (pd.p, 0.3); %!test %! pd = makedist ("birnbaumsaunders"); %! assert (class (pd), "BirnbaumSaundersDistribution"); %! assert (pd.beta, 1); %! assert (pd.gamma, 1); %!test %! pd = makedist ("birnbaumsaunders", "beta", 5); %! assert (pd.beta, 5); %! assert (pd.gamma, 1); %!test %! pd = makedist ("birnbaumsaunders", "gamma", 5); %! assert (pd.beta, 1); %! assert (pd.gamma, 5); %!test %! pd = makedist ("birnbaumsaunders", "beta", 3, "gamma", 5); %! assert (pd.beta, 3); %! assert (pd.gamma, 5); %!test %! pd = makedist ("burr"); %! assert (class (pd), "BurrDistribution"); %! assert (pd.alpha, 1); %! assert (pd.c, 1); %! assert (pd.k, 1); %!test %! pd = makedist ("burr", "k", 5); %! assert (pd.alpha, 1); %! assert (pd.c, 1); %! assert (pd.k, 5); %!test %! pd = makedist ("burr", "c", 5); %! assert (pd.alpha, 1); %! assert (pd.c, 5); %! assert (pd.k, 1); %!test %! pd = makedist ("burr", "alpha", 3, "c", 5); %! assert (pd.alpha, 3); %! assert (pd.c, 5); %! assert (pd.k, 1); %!test %! pd = makedist ("burr", "k", 3, "c", 5); %! assert (pd.alpha, 1); %! assert (pd.c, 5); %! assert (pd.k, 3); %!test %! pd = makedist ("exponential"); %! assert (class (pd), "ExponentialDistribution"); %! assert (pd.mu, 1); %!test %! pd = makedist ("exponential", "mu", 5); %! assert (pd.mu, 5); %!test %! pd = makedist ("extremevalue"); %! assert (class (pd), "ExtremeValueDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %!test %! pd = makedist ("extremevalue", "mu", 5); %! assert (class (pd), "ExtremeValueDistribution"); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %!test %! pd = makedist ("ev", "sigma", 5); %! assert (class (pd), "ExtremeValueDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 5); %!test %! pd = makedist ("ev", "mu", -3, "sigma", 5); %! assert (class (pd), "ExtremeValueDistribution"); %! assert (pd.mu, -3); %! assert (pd.sigma, 5); %!test %! pd = makedist ("gamma"); %! assert (class (pd), "GammaDistribution"); %! assert (pd.a, 1); %! assert (pd.b, 1); %!test %! pd = makedist ("gamma", "a", 5); %! assert (pd.a, 5); %! assert (pd.b, 1); %!test %! pd = makedist ("gamma", "b", 5); %! assert (pd.a, 1); %! assert (pd.b, 5); %!test %! pd = makedist ("gamma", "a", 3, "b", 5); %! assert (pd.a, 3); %! assert (pd.b, 5); %!test %! pd = makedist ("GeneralizedExtremeValue"); %! assert (class (pd), "GeneralizedExtremeValueDistribution"); %! assert (pd.k, 0); %! assert (pd.sigma, 1); %! assert (pd.mu, 0); %!test %! pd = makedist ("GeneralizedExtremeValue", "k", 5); %! assert (pd.k, 5); %! assert (pd.sigma, 1); %! assert (pd.mu, 0); %!test %! pd = makedist ("GeneralizedExtremeValue", "sigma", 5); %! assert (pd.k, 0); %! assert (pd.sigma, 5); %! assert (pd.mu, 0); %!test %! pd = makedist ("GeneralizedExtremeValue", "k", 3, "sigma", 5); %! assert (pd.k, 3); %! assert (pd.sigma, 5); %! assert (pd.mu, 0); %!test %! pd = makedist ("GeneralizedExtremeValue", "mu", 3, "sigma", 5); %! assert (pd.k, 0); %! assert (pd.sigma, 5); %! assert (pd.mu, 3); %!test %! pd = makedist ("GeneralizedPareto"); %! assert (class (pd), "GeneralizedParetoDistribution"); %! assert (pd.k, 1); %! assert (pd.sigma, 1); %! assert (pd.theta, 1); %!test %! pd = makedist ("GeneralizedPareto", "k", 5); %! assert (pd.k, 5); %! assert (pd.sigma, 1); %! assert (pd.theta, 1); %!test %! pd = makedist ("GeneralizedPareto", "sigma", 5); %! assert (pd.k, 1); %! assert (pd.sigma, 5); %! assert (pd.theta, 1); %!test %! pd = makedist ("GeneralizedPareto", "k", 3, "sigma", 5); %! assert (pd.k, 3); %! assert (pd.sigma, 5); %! assert (pd.theta, 1); %!test %! pd = makedist ("GeneralizedPareto", "theta", 3, "sigma", 5); %! assert (pd.k, 1); %! assert (pd.sigma, 5); %! assert (pd.theta, 3); %!test %! pd = makedist ("HalfNormal"); %! assert (class (pd), "HalfNormalDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %!test %! pd = makedist ("HalfNormal", "mu", 5); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %!test %! pd = makedist ("HalfNormal", "sigma", 5); %! assert (pd.mu, 0); %! assert (pd.sigma, 5); %!test %! pd = makedist ("HalfNormal", "mu", 3, "sigma", 5); %! assert (pd.mu, 3); %! assert (pd.sigma, 5); %!test %! pd = makedist ("InverseGaussian"); %! assert (class (pd), "InverseGaussianDistribution"); %! assert (pd.mu, 1); %! assert (pd.lambda, 1); %!test %! pd = makedist ("InverseGaussian", "mu", 5); %! assert (pd.mu, 5); %! assert (pd.lambda, 1); %!test %! pd = makedist ("InverseGaussian", "lambda", 5); %! assert (pd.mu, 1); %! assert (pd.lambda, 5); %!test %! pd = makedist ("InverseGaussian", "mu", 3, "lambda", 5); %! assert (pd.mu, 3); %! assert (pd.lambda, 5); %!test %! pd = makedist ("logistic"); %! assert (class (pd), "LogisticDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %!test %! pd = makedist ("logistic", "mu", 5); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %!test %! pd = makedist ("logistic", "sigma", 5); %! assert (pd.mu, 0); %! assert (pd.sigma, 5); %!test %! pd = makedist ("logistic", "mu", 3, "sigma", 5); %! assert (pd.mu, 3); %! assert (pd.sigma, 5); %!test %! pd = makedist ("loglogistic"); %! assert (class (pd), "LoglogisticDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %!test %! pd = makedist ("loglogistic", "mu", 5); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %!test %! pd = makedist ("loglogistic", "sigma", 5); %! assert (pd.mu, 0); %! assert (pd.sigma, 5); %!test %! pd = makedist ("loglogistic", "mu", 3, "sigma", 5); %! assert (pd.mu, 3); %! assert (pd.sigma, 5); %!test %! pd = makedist ("Lognormal"); %! assert (class (pd), "LognormalDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Lognormal", "mu", 5); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Lognormal", "sigma", 5); %! assert (pd.mu, 0); %! assert (pd.sigma, 5); %!test %! pd = makedist ("Lognormal", "mu", -3, "sigma", 5); %! assert (pd.mu, -3); %! assert (pd.sigma, 5); %!test %! pd = makedist ("Loguniform"); %! assert (class (pd), "LoguniformDistribution"); %! assert (pd.Lower, 1); %! assert (pd.Upper, 4); %!test %! pd = makedist ("Loguniform", "Lower", 2); %! assert (pd.Lower, 2); %! assert (pd.Upper, 4); %!test %! pd = makedist ("Loguniform", "Lower", 1, "Upper", 3); %! assert (pd.Lower, 1); %! assert (pd.Upper, 3); %!test %! pd = makedist ("Multinomial"); %! assert (class (pd), "MultinomialDistribution"); %! assert (pd.Probabilities, [0.5, 0.5]); %!test %! pd = makedist ("Multinomial", "Probabilities", [0.2, 0.3, 0.1, 0.4]); %! assert (class (pd), "MultinomialDistribution"); %! assert (pd.Probabilities, [0.2, 0.3, 0.1, 0.4]); %!test %! pd = makedist ("Nakagami"); %! assert (class (pd), "NakagamiDistribution"); %! assert (pd.mu, 1); %! assert (pd.omega, 1); %!test %! pd = makedist ("Nakagami", "mu", 5); %! assert (class (pd), "NakagamiDistribution"); %! assert (pd.mu, 5); %! assert (pd.omega, 1); %!test %! pd = makedist ("Nakagami", "omega", 0.3); %! assert (class (pd), "NakagamiDistribution"); %! assert (pd.mu, 1); %! assert (pd.omega, 0.3); %!test %! pd = makedist ("NegativeBinomial"); %! assert (class (pd), "NegativeBinomialDistribution"); %! assert (pd.R, 1); %! assert (pd.P, 0.5); %!test %! pd = makedist ("NegativeBinomial", "R", 5); %! assert (class (pd), "NegativeBinomialDistribution"); %! assert (pd.R, 5); %! assert (pd.P, 0.5); %!test %! pd = makedist ("NegativeBinomial", "p", 0.3); %! assert (class (pd), "NegativeBinomialDistribution"); %! assert (pd.R, 1); %! assert (pd.P, 0.3); %!test %! pd = makedist ("Normal"); %! assert (class (pd), "NormalDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Normal", "mu", 5); %! assert (class (pd), "NormalDistribution"); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Normal", "sigma", 5); %! assert (class (pd), "NormalDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 5); %!test %! pd = makedist ("Normal", "mu", -3, "sigma", 5); %! assert (class (pd), "NormalDistribution"); %! assert (pd.mu, -3); %! assert (pd.sigma, 5); %!test %! pd = makedist ("PiecewiseLinear"); %! assert (class (pd), "PiecewiseLinearDistribution"); %! assert (pd.x, [0; 1]); %! assert (pd.Fx, [0; 1]); %!test %! pd = makedist ("PiecewiseLinear", "x", [0, 1, 2], "Fx", [0, 0.5, 1]); %! assert (pd.x, [0; 1; 2]); %! assert (pd.Fx, [0; 0.5; 1]); %!test %! pd = makedist ("Poisson"); %! assert (class (pd), "PoissonDistribution"); %! assert (pd.lambda, 1); %!test %! pd = makedist ("Poisson", "lambda", 5); %! assert (pd.lambda, 5); %!test %! pd = makedist ("Rayleigh"); %! assert (class (pd), "RayleighDistribution"); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Rayleigh", "sigma", 5); %! assert (pd.sigma, 5); %!test %! pd = makedist ("Rician"); %! assert (class (pd), "RicianDistribution"); %! assert (pd.s, 1); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Rician", "s", 3); %! assert (pd.s, 3); %! assert (pd.sigma, 1); %!test %! pd = makedist ("Rician", "sigma", 3); %! assert (pd.s, 1); %! assert (pd.sigma, 3); %!test %! pd = makedist ("Rician", "s", 2, "sigma", 3); %! assert (pd.s, 2); %! assert (pd.sigma, 3); %!warning %! pd = makedist ("stable"); %! assert (class (pd), "double"); %! assert (isempty (pd), true); %!test %! pd = makedist ("tlocationscale"); %! assert (class (pd), "tLocationScaleDistribution"); %! assert (pd.mu, 0); %! assert (pd.sigma, 1); %! assert (pd.nu, 5); %!test %! pd = makedist ("tlocationscale", "mu", 5); %! assert (pd.mu, 5); %! assert (pd.sigma, 1); %! assert (pd.nu, 5); %!test %! pd = makedist ("tlocationscale", "sigma", 2); %! assert (pd.mu, 0); %! assert (pd.sigma, 2); %! assert (pd.nu, 5); %!test %! pd = makedist ("tlocationscale", "mu", 5, "sigma", 2); %! assert (pd.mu, 5); %! assert (pd.sigma, 2); %! assert (pd.nu, 5); %!test %! pd = makedist ("tlocationscale", "nu", 1, "sigma", 2); %! assert (pd.mu, 0); %! assert (pd.sigma, 2); %! assert (pd.nu, 1); %!test %! pd = makedist ("tlocationscale", "mu", -2, "sigma", 3, "nu", 1); %! assert (pd.mu, -2); %! assert (pd.sigma, 3); %! assert (pd.nu, 1); %!test %! pd = makedist ("Triangular"); %! assert (class (pd), "TriangularDistribution"); %! assert (pd.A, 0); %! assert (pd.B, 0.5); %! assert (pd.C, 1); %!test %! pd = makedist ("Triangular", "A", -2); %! assert (pd.A, -2); %! assert (pd.B, 0.5); %! assert (pd.C, 1); %!test %! pd = makedist ("Triangular", "A", 0.5, "B", 0.9); %! assert (pd.A, 0.5); %! assert (pd.B, 0.9); %! assert (pd.C, 1); %!test %! pd = makedist ("Triangular", "A", 1, "B", 2, "C", 5); %! assert (pd.A, 1); %! assert (pd.B, 2); %! assert (pd.C, 5); %!test %! pd = makedist ("Uniform"); %! assert (class (pd), "UniformDistribution"); %! assert (pd.Lower, 0); %! assert (pd.Upper, 1); %!test %! pd = makedist ("Uniform", "Lower", -2); %! assert (pd.Lower, -2); %! assert (pd.Upper, 1); %!test %! pd = makedist ("Uniform", "Lower", 1, "Upper", 3); %! assert (pd.Lower, 1); %! assert (pd.Upper, 3); %!test %! pd = makedist ("Weibull"); %! assert (class (pd), "WeibullDistribution"); %! assert (pd.lambda, 1); %! assert (pd.k, 1); %!test %! pd = makedist ("Weibull", "lambda", 3); %! assert (pd.lambda, 3); %! assert (pd.k, 1); %!test %! pd = makedist ("Weibull", "lambda", 3, "k", 2); %! assert (pd.lambda, 3); %! assert (pd.k, 2); ## Test input validation %!error makedist (1) %!error makedist (["as";"sd"]) %!error makedist ("some") %!error ... %! makedist ("Beta", "a") %!error ... %! makedist ("Beta", "a", 1, "Q", 23) %!error ... %! makedist ("Binomial", "N", 1, "Q", 23) %!error ... %! makedist ("BirnbaumSaunders", "N", 1) %!error ... %! makedist ("Burr", "lambda", 1, "sdfs", 34) %!error ... %! makedist ("extremevalue", "mu", 1, "sdfs", 34) %!error ... %! makedist ("exponential", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Gamma", "k", 1, "sdfs", 34) %!error ... %! makedist ("GeneralizedExtremeValue", "k", 1, "sdfs", 34) %!error ... %! makedist ("GeneralizedPareto", "k", 1, "sdfs", 34) %!error ... %! makedist ("HalfNormal", "k", 1, "sdfs", 34) %!error ... %! makedist ("InverseGaussian", "k", 1, "sdfs", 34) %!error ... %! makedist ("Logistic", "k", 1, "sdfs", 34) %!error ... %! makedist ("Loglogistic", "k", 1, "sdfs", 34) %!error ... %! makedist ("Lognormal", "k", 1, "sdfs", 34) %!error ... %! makedist ("Loguniform", "k", 1, "sdfs", 34) %!error ... %! makedist ("Multinomial", "k", 1, "sdfs", 34) %!error ... %! makedist ("Nakagami", "mu", 1, "sdfs", 34) %!error ... %! makedist ("NegativeBinomial", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Normal", "mu", 1, "sdfs", 34) %!error ... %! makedist ("PiecewiseLinear", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Poisson", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Rayleigh", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Rician", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Stable", "mu", 1, "sdfs", 34) %!error ... %! makedist ("tLocationScale", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Triangular", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Uniform", "mu", 1, "sdfs", 34) %!error ... %! makedist ("Weibull", "mu", 1, "sdfs", 34) statistics-release-1.7.3/inst/dist_wrap/mle.m000066400000000000000000000442541475240274700212630ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{phat} =} mle (@var{x}) ## @deftypefnx {statistics} {@var{phat} =} mle (@var{x}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{phat}, @var{pci}] =} mle (@dots{}) ## ## Compute maximum likelihood estimates. ## ## @code{@var{phat} = mle (@var{x})} returns the maximum likelihood estimates ## (MLEs) for the parameters of a normal distribution using the sample data in ## @var{x}, which must be a numeric vector of real values. ## ## @code{@var{phat} = mle (@var{x}, @var{Name}, @var{Value})} returns the MLEs ## with additional options specified by @qcode{Name-Value} pair arguments listed ## below. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"distribution"} @tab @tab A character vector specifying the ## distribution type for which to estimate parameters. ## ## @item @qcode{"Ntrials"} @tab @tab A scalar specifying the number of trials ## for the corresponding element of @var{x} for the binomial distribution. ## ## @item @qcode{"theta"} @tab @tab A scalar specifying the location parameter ## for the generalized Pareto distribution. ## ## @item @qcode{"mu"} @tab @tab A scalar specifying the location parameter ## for the half-normal distribution. ## ## @item @qcode{"censoring"} @tab @tab A vector of the same size as @var{x} ## indicating censored data in @var{x}. By default it is ## @qcode{@var{censor} = zeros (size (@var{x}))}. ## ## @item @qcode{"frequency"} @tab @tab A vector of nonnegative integer counts of ## the same size as @var{x} used as frequency observations. By default it is ## @qcode{@var{freq} = ones (size (@var{x}))}. ## ## @item @qcode{"alpha"} @tab @tab A scalar in the range @math{(0,1)}, as the ## significance level for the confidence interval @var{pci}. By default it is ## 0.05 corresponding to 95% confidence intervals. ## ## @item @qcode{"options"} @tab @tab A structure specifying the control ## parameters for the iterative algorithm used to compute ML estimates with the ## @code{fminsearch} function. ## @end multitable ## ## @seealso{fitdist, makedist} ## @end deftypefn function [phat, pci] = mle (x, varargin) ## Check data if (! (isvector (x) && isnumeric (x) && isreal (x))) error ("mle: X must be a numeric vector of real values."); endif ## Add defaults censor = []; freq = ones (size (x)); alpha = 0.05; ntrials = []; mu = 0; theta = 1; options.Display = "off"; options.MaxFunEvals = 400; options.MaxIter = 200; options.TolX = 1e-6; distname = "normal"; ## Parse extra arguments if (mod (numel (varargin), 2) != 0) error ("mle: optional arguments must be in NAME-VALUE pairs."); endif while (numel (varargin) > 0) switch (tolower (varargin{1})) case "distribution" distname = varargin{2}; case "censoring" censor = varargin{2}; if (! isequal (size (x), size (censor)) && ! isempty (censor)) error (strcat (["mle: 'censoring' argument must have the same"], ... [" size as the input data in X."])); endif case "frequency" freq = varargin{2}; if (! isequal (size (x), size (freq))) error (strcat (["mle: 'frequency' argument must have the same"], ... [" size as the input data in X."])); endif if (any (freq != round (freq)) || any (freq < 0)) error (strcat (["mle: 'frequency' argument must contain"], ... [" non-negative integer values."])); endif case "alpha" alpha = varargin{2}; if (! isscalar (alpha) || ! isreal (alpha) || alpha <= 0 || alpha >= 1) error ("mle: invalid value for 'alpha' argument."); endif case "ntrials" ntrials = varargin{2}; if (! (isscalar (ntrials) && isreal (ntrials) && ntrials > 0 && fix (ntrials) == ntrials)) error (strcat (["mle: 'ntrials' argument must be a positive"], ... [" integer scalar value."])); endif case {"mu"} mu = varargin{2}; case {"theta"} theta = varargin{2}; case "options" options = varargin{2}; if (! isstruct (options) || ! isfield (options, "Display") || ! isfield (options, "MaxFunEvals") || ! isfield (options, "MaxIter") || ! isfield (options, "TolX")) error (strcat (["mle: 'options' argument must be a structure"], ... [" compatible for 'fminsearch'."])); endif case {"pdf", "cdf", "logpdf", "logsf", "nloglf", "truncationbounds", ... "start", "lowerbound", "upperbound", "optimfun"} printf ("mle: parameter not supported yet."); otherwise error ("mle: unknown parameter name."); endswitch varargin([1:2]) = []; endwhile ## Switch to known distributions switch (tolower (distname)) case "bernoulli" if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Bernoulli distribution."])); elseif (any (x != 0 & x != 1)) error ("mle: invalid data for the Bernoulli distribution."); endif if (! isempty (freq)) x = expandFreq (x, freq); endif if (nargout < 2) phat = binofit (sum (x), numel (x)); else [phat, pci] = binofit (sum (x), numel (x), alpha); endif case "beta" if (! isempty (censor)) error ("mle: censoring is not supported for the Beta distribution."); endif if (nargout < 2) phat = betafit (x, alpha, freq, options); else [phat, pci] = betafit (x, alpha, freq, options); endif case {"binomial", "bino"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Binomial distribution."])); elseif (isempty (ntrials)) error (strcat (["mle: 'Ntrials' parameter is required"], ... [" for the Binomial distribution."])); endif if (nargout < 2) phat = binofit (sum (x .* freq), sum (freq) .* ntrials); else [phat, pci] = binofit (sum (x .* freq), sum (freq) .* ntrials, alpha); endif case {"bisa", "BirnbaumSaunders"} if (nargout < 2) phat = bisafit (x, alpha, censor, freq, options); else [phat, pci] = bisafit (x, alpha, censor, freq, options); endif case "burr" if (nargout < 2) phat = burrfit (x, alpha, censor, freq, options); else [phat, pci] = burrfit (x, alpha, censor, freq, options); endif case {"ev", "extreme value"} if (nargout < 2) phat = evfit (x, alpha, censor, freq, options); else [phat, pci] = evfit (x, alpha, censor, freq, options); endif case {"exp", "exponential"} if (nargout < 2) phat = expfit (x, alpha, censor, freq); else [phat, pci] = expfit (x, alpha, censor, freq); endif case {"gam", "gamma"} if (nargout < 2) phat = gamfit (x, alpha, censor, freq, options); else [phat, pci] = gamfit (x, alpha, censor, freq, options); endif case {"geo", "geometric"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for the"], ... [" Geometric distribution."])); endif if (nargout < 2) phat = gevfit (x, alpha, freq); else [phat, pci] = gevfit (x, alpha, freq); endif case {"gev", "generalized extreme value"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for the"], ... [" Generalized Extreme Value distribution."])); endif if (nargout < 2) phat = gevfit (x, alpha, freq, options); else [phat, pci] = gevfit (x, alpha, freq, options); endif case {"gp", "generalized pareto"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Generalized Pareto distribution."])); endif if (any (x < theta)) error (strcat (["mle: invalid 'theta' location parameter"], ... [" for the Generalized Pareto distribution."])); endif if (nargout < 2) phat = gpfit (x, theta, alpha, freq, options); else [phat, pci] = gpfit (x, theta, alpha, freq, options); endif case "gumbel" if (nargout < 2) phat = gumbelfit (x, alpha, censor, freq, options); else [phat, pci] = gumbelfit (x, alpha, censor, freq, options); endif case {"hn", "half normal", "halfnormal"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Half Normal distribution."])); endif if (any (x < mu)) error (strcat (["mle: invalid 'mu' location parameter"], ... [" for the Half Normal distribution."])); endif if (nargout < 2) phat = hnfit (x, mu, alpha, freq); else [phat, pci] = hnfit (x, mu, alpha, freq); endif case {"invg", "inversegaussian", "inverse gaussian"} if (nargout < 2) phat = invgfit (x, alpha, censor, freq, options); else [phat, pci] = invgfit (x, alpha, censor, freq, options); endif case {"logi", "logistic"} if (nargout < 2) phat = logifit (x, alpha, censor, freq, options); else [phat, pci] = logifit (x, alpha, censor, freq, options); endif case {"logl", "loglogistic"} if (nargout < 2) phat = loglfit (x, alpha, censor, freq, options); else [phat, pci] = loglfit (x, alpha, censor, freq, options); endif case {"logn", "lognormal"} if (nargout < 2) phat = lognfit (x, alpha, censor, freq, options); else [phat, pci] = lognfit (x, alpha, censor, freq, options); endif case {"naka", "nakagami"} if (nargout < 2) phat = nakafit (x, alpha, censor, freq, options); else [phat, pci] = nakafit (x, alpha, censor, freq, options); endif case {"nbin", "negativebinomial", "negative binomial"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Negative Binomial distribution."])); endif if (nargout < 2) phat = nbinfit (x, alpha, freq, options); else [phat, pci] = nbinfit (x, alpha, freq, options); endif case {"norm", "normal"} if (nargout < 2) [muhat, sigmahat] = normfit (x, alpha, censor, freq, options); phat = [muhat, sigmahat]; else [muhat, sigmahat, muci, sigmaci] = normfit (x, alpha, censor, ... freq, options); phat = [muhat, sigmahat]; pci = [muci, sigmaci]; endif case {"poiss", "poisson"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Poisson distribution."])); endif if (nargout < 2) phat = poissfit (x, alpha, freq); else [phat, pci] = poissfit (x, alpha, freq); endif case {"rayl", "rayleigh"} if (nargout < 2) phat = raylfit (x, alpha, censor, freq); else [phat, pci] = raylfit (x, alpha, censor, freq); endif case {"rice", "rician"} if (nargout < 2) phat = ricefit (x, alpha, censor, freq, options); else [phat, pci] = ricefit (x, alpha, censor, freq, options); endif case {"tls", "tlocationscale"} if (nargout < 2) phat = tlsfit (x, alpha, censor, freq, options); else [phat, pci] = tlsfit (x, alpha, censor, freq, options); endif case {"unid", "uniform discrete", "discrete uniform", "discrete"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Discrete Uniform distribution."])); endif if (nargout < 2) phat = unidfit (x, alpha, freq); else [phat, pci] = unidfit (x, alpha, freq); endif case {"unif", "uniform", "continuous uniform"} if (! isempty (censor)) error (strcat (["mle: censoring is not supported for"], ... [" the Continuous Uniform distribution."])); endif if (nargout < 2) phat = uniffit (x, alpha, freq); else [phat, pci] = uniffit (x, alpha, freq); endif case {"wbl", "weibull"} if (nargout < 2) phat = wblfit (x, alpha, censor, freq, options); else [phat, pci] = wblfit (x, alpha, censor, freq, options); endif otherwise error ("mle: unrecognized distribution name."); endswitch endfunction ## Helper function for expanding data according to frequency vector function [x, freq] = expandFreq (x, freq) ## Remove NaNs and zeros remove = isnan (freq); x(remove) = []; freq(remove) = []; if (! all (freq == 1)) xf = []; for i = 1:numel (freq) xf = [xf, repmat(x(i), 1, freq(i))]; endfor endif x = xf; endfunction ## Test input validation %!error mle (ones (2)) %!error mle ("text") %!error mle ([1, 2, 3, i, 5]) %!error ... %! mle ([1:50], "distribution") %!error ... %! mle ([1:50], "censoring", logical ([1,0,1,0])) %!error ... %! mle ([1:50], "frequency", [1,0,1,0]) %!error ... %! mle ([1 0 1 0], "frequency", [-1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "nbin", "frequency", [-1 1 0 0]) %!error mle ([1:50], "alpha", [0.05, 0.01]) %!error mle ([1:50], "alpha", 1) %!error mle ([1:50], "alpha", -1) %!error mle ([1:50], "alpha", i) %!error ... %! mle ([1:50], "ntrials", -1) %!error ... %! mle ([1:50], "ntrials", [20, 50]) %!error ... %! mle ([1:50], "ntrials", [20.3]) %!error ... %! mle ([1:50], "ntrials", 3i) %!error ... %! mle ([1:50], "options", 4) %!error ... %! mle ([1:50], "options", struct ("x", 3)) %!error mle ([1:50], "NAME", "value") %!error ... %! mle ([1 0 1 0], "distribution", "bernoulli", "censoring", [1 1 0 0]) %!error ... %! mle ([1 2 1 0], "distribution", "bernoulli") %!error ... %! mle ([1 0 1 0], "distribution", "beta", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "bino", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "bino") %!error ... %! mle ([1 0 1 0], "distribution", "geo", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "gev", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "gp", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 -1 0], "distribution", "gp") %!error ... %! mle ([1 0 1 0], "distribution", "hn", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 -1 0], "distribution", "hn") %!error ... %! mle ([1 0 1 0], "distribution", "nbin", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "poisson", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "unid", "censoring", [1 1 0 0]) %!error ... %! mle ([1 0 1 0], "distribution", "unif", "censoring", [1 1 0 0]) %!error mle ([1:50], "distribution", "value") %!error ... %! mle ([1 0 1 0], "distribution", "unif", "censoring", [1 1 0 0]) statistics-release-1.7.3/inst/dist_wrap/pdf.m000066400000000000000000000340001475240274700212430ustar00rootroot00000000000000## Copyright (C) 2016 Andreas Stahel ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} pdf (@var{name}, @var{x}, @var{A}) ## @deftypefnx {statistics} {@var{y} =} pdf (@var{name}, @var{x}, @var{A}, @var{B}) ## @deftypefnx {statistics} {@var{y} =} pdf (@var{name}, @var{x}, @var{A}, @var{B}, @var{C}) ## ## Return the PDF of a univariate distribution evaluated at @var{x}. ## ## @code{pdf} is a wrapper for the univariate cumulative distribution functions ## available in the statistics package. See the corresponding functions' help ## to learn the signification of the parameters after @var{x}. ## ## @code{@var{y} = pdf (@var{name}, @var{x}, @var{A})} returns the CDF for the ## one-parameter distribution family specified by @var{name} and the ## distribution parameter @var{A}, evaluated at the values in @var{x}. ## ## @code{@var{y} = pdf (@var{name}, @var{x}, @var{A}, @var{B})} returns the CDF ## for the two-parameter distribution family specified by @var{name} and the ## distribution parameters @var{A} and @var{B}, evaluated at the values in ## @var{x}. ## ## @code{@var{y} = pdf (@var{name}, @var{x}, @var{A}, @var{B}, @var{C})} returns ## the CDF for the three-parameter distribution family specified by @var{name} ## and the distribution parameters @var{A}, @var{B}, and @var{C}, evaluated at ## the values in @var{x}. ## ## @var{name} must be a char string of the name or the abbreviation of the ## desired cumulative distribution function as listed in the followng table. ## The last column shows the number of required parameters that should be parsed ## after @var{x} to the desired PDF. ## ## @multitable @columnfractions 0.4 0.05 0.2 0.05 0.3 ## @headitem Distribution Name @tab @tab Abbreviation @tab @tab Input Parameters ## @item @qcode{"Beta"} @tab @tab @qcode{"beta"} @tab @tab 2 ## @item @qcode{"Binomial"} @tab @tab @qcode{"bino"} @tab @tab 2 ## @item @qcode{"Birnbaum-Saunders"} @tab @tab @qcode{"bisa"} @tab @tab 2 ## @item @qcode{"Burr"} @tab @tab @qcode{"burr"} @tab @tab 3 ## @item @qcode{"Cauchy"} @tab @tab @qcode{"cauchy"} @tab @tab 2 ## @item @qcode{"Chi-squared"} @tab @tab @qcode{"chi2"} @tab @tab 1 ## @item @qcode{"Extreme Value"} @tab @tab @qcode{"ev"} @tab @tab 2 ## @item @qcode{"Exponential"} @tab @tab @qcode{"exp"} @tab @tab 1 ## @item @qcode{"F-Distribution"} @tab @tab @qcode{"f"} @tab @tab 2 ## @item @qcode{"Gamma"} @tab @tab @qcode{"gam"} @tab @tab 2 ## @item @qcode{"Geometric"} @tab @tab @qcode{"geo"} @tab @tab 1 ## @item @qcode{"Generalized Extreme Value"} @tab @tab @qcode{"gev"} @tab @tab 3 ## @item @qcode{"Generalized Pareto"} @tab @tab @qcode{"gp"} @tab @tab 3 ## @item @qcode{"Gumbel"} @tab @tab @qcode{"gumbel"} @tab @tab 2 ## @item @qcode{"Half-normal"} @tab @tab @qcode{"hn"} @tab @tab 2 ## @item @qcode{"Hypergeometric"} @tab @tab @qcode{"hyge"} @tab @tab 3 ## @item @qcode{"Inverse Gaussian"} @tab @tab @qcode{"invg"} @tab @tab 2 ## @item @qcode{"Laplace"} @tab @tab @qcode{"laplace"} @tab @tab 2 ## @item @qcode{"Logistic"} @tab @tab @qcode{"logi"} @tab @tab 2 ## @item @qcode{"Log-Logistic"} @tab @tab @qcode{"logl"} @tab @tab 2 ## @item @qcode{"Lognormal"} @tab @tab @qcode{"logn"} @tab @tab 2 ## @item @qcode{"Nakagami"} @tab @tab @qcode{"naka"} @tab @tab 2 ## @item @qcode{"Negative Binomial"} @tab @tab @qcode{"nbin"} @tab @tab 2 ## @item @qcode{"Noncentral F-Distribution"} @tab @tab @qcode{"ncf"} @tab @tab 3 ## @item @qcode{"Noncentral Student T"} @tab @tab @qcode{"nct"} @tab @tab 2 ## @item @qcode{"Noncentral Chi-Squared"} @tab @tab @qcode{"ncx2"} @tab @tab 2 ## @item @qcode{"Normal"} @tab @tab @qcode{"norm"} @tab @tab 2 ## @item @qcode{"Poisson"} @tab @tab @qcode{"poiss"} @tab @tab 1 ## @item @qcode{"Rayleigh"} @tab @tab @qcode{"rayl"} @tab @tab 1 ## @item @qcode{"Rician"} @tab @tab @qcode{"rice"} @tab @tab 2 ## @item @qcode{"Student T"} @tab @tab @qcode{"t"} @tab @tab 1 ## @item @qcode{"location-scale T"} @tab @tab @qcode{"tls"} @tab @tab 3 ## @item @qcode{"Triangular"} @tab @tab @qcode{"tri"} @tab @tab 3 ## @item @qcode{"Discrete Uniform"} @tab @tab @qcode{"unid"} @tab @tab 1 ## @item @qcode{"Uniform"} @tab @tab @qcode{"unif"} @tab @tab 2 ## @item @qcode{"Von Mises"} @tab @tab @qcode{"vm"} @tab @tab 2 ## @item @qcode{"Weibull"} @tab @tab @qcode{"wbl"} @tab @tab 2 ## @end multitable ## ## @seealso{cdf, icdf, random, betapdf, binopdf, bisapdf, burrpdf, cauchypdf, ## chi2pdf, evpdf, exppdf, fpdf, gampdf, geopdf, gevpdf, gppdf, gumbelpdf, ## hnpdf, hygepdf, invgpdf, laplacepdf, logipdf, loglpdf, lognpdf, nakapdf, ## nbinpdf, ncfpdf, nctpdf, ncx2pdf, normpdf, poisspdf, raylpdf, ricepdf, tpdf, ## tlspdf, tripdf, unidpdf, unifpdf, vmpdf, wblpdf} ## @end deftypefn function y = pdf (name, x, varargin) ## implemented functions persistent allDF = { ... {"beta" , "Beta"}, @betapdf, 2, ... {"bino" , "Binomial"}, @binopdf, 2, ... {"bisa" , "Birnbaum-Saunders"}, @bisapdf, 2, ... {"burr" , "Burr"}, @burrpdf, 3, ... {"cauchy" , "Cauchy"}, @cauchypdf, 2, ... {"chi2" , "Chi-squared"}, @chi2pdf, 1, ... {"ev" , "Extreme Value"}, @evpdf, 2, ... {"exp" , "Exponential"}, @exppdf, 1, ... {"f" , "F-Distribution"}, @fpdf, 2, ... {"gam" , "Gamma"}, @gampdf, 2, ... {"geo" , "Geometric"}, @geopdf, 1, ... {"gev" , "Generalized Extreme Value"}, @gevpdf, 3, ... {"gp" , "Generalized Pareto"}, @gppdf, 3, ... {"gumbel" , "Gumbel"}, @gumbelpdf, 2, ... {"hn" , "Half-normal"}, @hnpdf, 2, ... {"hyge" , "Hypergeometric"}, @hygepdf, 3, ... {"invg" , "Inverse Gaussian"}, @invgpdf, 2, ... {"laplace" , "Laplace"}, @laplacepdf, 2, ... {"logi" , "Logistic"}, @logipdf, 2, ... {"logl" , "Log-Logistic"}, @loglpdf, 2, ... {"logn" , "Lognormal"}, @lognpdf, 2, ... {"naka" , "Nakagami"}, @nakapdf, 2, ... {"nbin" , "Negative Binomial"}, @nbinpdf, 2, ... {"ncf" , "Noncentral F-Distribution"}, @ncfpdf, 3, ... {"nct" , "Noncentral Student T"}, @nctpdf, 2, ... {"ncx2" , "Noncentral Chi-squared"}, @ncx2pdf, 2, ... {"norm" , "Normal"}, @normpdf, 2, ... {"poiss" , "Poisson"}, @poisspdf, 1, ... {"rayl" , "Rayleigh"}, @raylpdf, 1, ... {"rice" , "Rician"}, @ricepdf, 2, ... {"t" , "Student T"}, @tpdf, 1, ... {"tls" , "location-scale T"}, @tlspdf, 3, ... {"tri" , "Triangular"}, @tripdf, 3, ... {"unid" , "Discrete Uniform"}, @unidpdf, 1, ... {"unif" , "Uniform"}, @unifpdf, 2, ... {"vm" , "Von Mises"}, @vmpdf, 2, ... {"wbl" , "Weibull"}, @wblpdf, 2}; if (! ischar (name)) error ("pdf: distribution NAME must a char string."); endif ## Check X being numeric and real if (! isnumeric (x)) error ("pdf: X must be numeric."); elseif (! isreal (x)) error ("pdf: values in X must be real."); endif ## Get number of arguments nargs = numel (varargin); ## Get available functions pdfnames = allDF(1:3:end); pdfhandl = allDF(2:3:end); pdf_args = allDF(3:3:end); ## Search for PDF function idx = cellfun (@(x)any(strcmpi (name, x)), pdfnames); if (any (idx)) if (nargs == pdf_args{idx}) ## Check that all distribution parameters are numeric if (! all (cellfun (@(x)isnumeric(x), (varargin)))) error ("pdf: distribution parameters must be numeric."); endif ## Call appropriate iCDF y = feval (pdfhandl{idx}, x, varargin{:}); else if (pdf_args{idx} == 1) error ("pdf: %s distribution requires 1 parameter.", name); else error ("pdf: %s distribution requires %d parameters.", ... name, pdf_args{idx}); endif endif else error ("pdf: %s distribution is not implemented in Statistics.", name); endif endfunction ## Test results %!shared x %! x = [1:5]; %!assert (pdf ("Beta", x, 5, 2), betapdf (x, 5, 2)) %!assert (pdf ("beta", x, 5, 2), betapdf (x, 5, 2)) %!assert (pdf ("Binomial", x, 5, 2), binopdf (x, 5, 2)) %!assert (pdf ("bino", x, 5, 2), binopdf (x, 5, 2)) %!assert (pdf ("Birnbaum-Saunders", x, 5, 2), bisapdf (x, 5, 2)) %!assert (pdf ("bisa", x, 5, 2), bisapdf (x, 5, 2)) %!assert (pdf ("Burr", x, 5, 2, 2), burrpdf (x, 5, 2, 2)) %!assert (pdf ("burr", x, 5, 2, 2), burrpdf (x, 5, 2, 2)) %!assert (pdf ("Cauchy", x, 5, 2), cauchypdf (x, 5, 2)) %!assert (pdf ("cauchy", x, 5, 2), cauchypdf (x, 5, 2)) %!assert (pdf ("Chi-squared", x, 5), chi2pdf (x, 5)) %!assert (pdf ("chi2", x, 5), chi2pdf (x, 5)) %!assert (pdf ("Extreme Value", x, 5, 2), evpdf (x, 5, 2)) %!assert (pdf ("ev", x, 5, 2), evpdf (x, 5, 2)) %!assert (pdf ("Exponential", x, 5), exppdf (x, 5)) %!assert (pdf ("exp", x, 5), exppdf (x, 5)) %!assert (pdf ("F-Distribution", x, 5, 2), fpdf (x, 5, 2)) %!assert (pdf ("f", x, 5, 2), fpdf (x, 5, 2)) %!assert (pdf ("Gamma", x, 5, 2), gampdf (x, 5, 2)) %!assert (pdf ("gam", x, 5, 2), gampdf (x, 5, 2)) %!assert (pdf ("Geometric", x, 5), geopdf (x, 5)) %!assert (pdf ("geo", x, 5), geopdf (x, 5)) %!assert (pdf ("Generalized Extreme Value", x, 5, 2, 2), gevpdf (x, 5, 2, 2)) %!assert (pdf ("gev", x, 5, 2, 2), gevpdf (x, 5, 2, 2)) %!assert (pdf ("Generalized Pareto", x, 5, 2, 2), gppdf (x, 5, 2, 2)) %!assert (pdf ("gp", x, 5, 2, 2), gppdf (x, 5, 2, 2)) %!assert (pdf ("Gumbel", x, 5, 2), gumbelpdf (x, 5, 2)) %!assert (pdf ("gumbel", x, 5, 2), gumbelpdf (x, 5, 2)) %!assert (pdf ("Half-normal", x, 5, 2), hnpdf (x, 5, 2)) %!assert (pdf ("hn", x, 5, 2), hnpdf (x, 5, 2)) %!assert (pdf ("Hypergeometric", x, 5, 2, 2), hygepdf (x, 5, 2, 2)) %!assert (pdf ("hyge", x, 5, 2, 2), hygepdf (x, 5, 2, 2)) %!assert (pdf ("Inverse Gaussian", x, 5, 2), invgpdf (x, 5, 2)) %!assert (pdf ("invg", x, 5, 2), invgpdf (x, 5, 2)) %!assert (pdf ("Laplace", x, 5, 2), laplacepdf (x, 5, 2)) %!assert (pdf ("laplace", x, 5, 2), laplacepdf (x, 5, 2)) %!assert (pdf ("Logistic", x, 5, 2), logipdf (x, 5, 2)) %!assert (pdf ("logi", x, 5, 2), logipdf (x, 5, 2)) %!assert (pdf ("Log-Logistic", x, 5, 2), loglpdf (x, 5, 2)) %!assert (pdf ("logl", x, 5, 2), loglpdf (x, 5, 2)) %!assert (pdf ("Lognormal", x, 5, 2), lognpdf (x, 5, 2)) %!assert (pdf ("logn", x, 5, 2), lognpdf (x, 5, 2)) %!assert (pdf ("Nakagami", x, 5, 2), nakapdf (x, 5, 2)) %!assert (pdf ("naka", x, 5, 2), nakapdf (x, 5, 2)) %!assert (pdf ("Negative Binomial", x, 5, 2), nbinpdf (x, 5, 2)) %!assert (pdf ("nbin", x, 5, 2), nbinpdf (x, 5, 2)) %!assert (pdf ("Noncentral F-Distribution", x, 5, 2, 2), ncfpdf (x, 5, 2, 2)) %!assert (pdf ("ncf", x, 5, 2, 2), ncfpdf (x, 5, 2, 2)) %!assert (pdf ("Noncentral Student T", x, 5, 2), nctpdf (x, 5, 2)) %!assert (pdf ("nct", x, 5, 2), nctpdf (x, 5, 2)) %!assert (pdf ("Noncentral Chi-Squared", x, 5, 2), ncx2pdf (x, 5, 2)) %!assert (pdf ("ncx2", x, 5, 2), ncx2pdf (x, 5, 2)) %!assert (pdf ("Normal", x, 5, 2), normpdf (x, 5, 2)) %!assert (pdf ("norm", x, 5, 2), normpdf (x, 5, 2)) %!assert (pdf ("Poisson", x, 5), poisspdf (x, 5)) %!assert (pdf ("poiss", x, 5), poisspdf (x, 5)) %!assert (pdf ("Rayleigh", x, 5), raylpdf (x, 5)) %!assert (pdf ("rayl", x, 5), raylpdf (x, 5)) %!assert (pdf ("Rician", x, 5, 1), ricepdf (x, 5, 1)) %!assert (pdf ("rice", x, 5, 1), ricepdf (x, 5, 1)) %!assert (pdf ("Student T", x, 5), tpdf (x, 5)) %!assert (pdf ("t", x, 5), tpdf (x, 5)) %!assert (pdf ("location-scale T", x, 5, 1, 2), tlspdf (x, 5, 1, 2)) %!assert (pdf ("tls", x, 5, 1, 2), tlspdf (x, 5, 1, 2)) %!assert (pdf ("Triangular", x, 5, 2, 2), tripdf (x, 5, 2, 2)) %!assert (pdf ("tri", x, 5, 2, 2), tripdf (x, 5, 2, 2)) %!assert (pdf ("Discrete Uniform", x, 5), unidpdf (x, 5)) %!assert (pdf ("unid", x, 5), unidpdf (x, 5)) %!assert (pdf ("Uniform", x, 5, 2), unifpdf (x, 5, 2)) %!assert (pdf ("unif", x, 5, 2), unifpdf (x, 5, 2)) %!assert (pdf ("Von Mises", x, 5, 2), vmpdf (x, 5, 2)) %!assert (pdf ("vm", x, 5, 2), vmpdf (x, 5, 2)) %!assert (pdf ("Weibull", x, 5, 2), wblpdf (x, 5, 2)) %!assert (pdf ("wbl", x, 5, 2), wblpdf (x, 5, 2)) ## Test input validation %!error pdf (1) %!error pdf ({"beta"}) %!error pdf ("beta", {[1 2 3 4 5]}) %!error pdf ("beta", "text") %!error pdf ("beta", 1+i) %!error ... %! pdf ("Beta", x, "a", 2) %!error ... %! pdf ("Beta", x, 5, "") %!error ... %! pdf ("Beta", x, 5, {2}) %!error pdf ("chi2", x) %!error pdf ("Beta", x, 5) %!error pdf ("Burr", x, 5) %!error pdf ("Burr", x, 5, 2) statistics-release-1.7.3/inst/dist_wrap/random.m000066400000000000000000000412131475240274700217560ustar00rootroot00000000000000## Copyright (C) 2007 Soren Hauberg ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{r} =} random (@var{name}, @var{A}) ## @deftypefnx {statistics} {@var{r} =} random (@var{name}, @var{A}, @var{B}) ## @deftypefnx {statistics} {@var{r} =} random (@var{name}, @var{A}, @var{B}, @var{C}) ## @deftypefnx {statistics} {@var{r} =} random (@var{name}, @dots{}, @var{rows}, @var{cols}) ## @deftypefnx {statistics} {@var{r} =} random (@var{name}, @dots{}, @var{rows}, @var{cols}, @dots{}) ## @deftypefnx {statistics} {@var{r} =} random (@var{name}, @dots{}, [@var{sz}]) ## ## Random arrays from a given one-, two-, or three-parameter distribution. ## ## The variable @var{name} must be a string with the name of the distribution to ## sample from. If this distribution is a one-parameter distribution, @var{A} ## must be supplied, if it is a two-parameter distribution, @var{B} must also be ## supplied, and if it is a three-parameter distribution, @var{C} must also be ## supplied. Any arguments following the distribution parameters will determine ## the size of the result. ## ## When called with a single size argument, return a square matrix with the ## dimension specified. When called with more than one scalar argument the ## first two arguments are taken as the number of rows and columns and any ## further arguments specify additional matrix dimensions. The size may also ## be specified with a vector of dimensions @var{sz}. ## ## @var{name} must be a char string of the name or the abbreviation of the ## desired probability distribution function as listed in the followng table. ## The last column shows the required number of parameters that must be passed ## passed to the desired @qcode{*rnd} distribution function. ## ## @multitable @columnfractions 0.4 0.05 0.2 0.05 0.3 ## @headitem Distribution Name @tab @tab Abbreviation @tab @tab Input Parameters ## @item @qcode{"Beta"} @tab @tab @qcode{"beta"} @tab @tab 2 ## @item @qcode{"Binomial"} @tab @tab @qcode{"bino"} @tab @tab 2 ## @item @qcode{"Birnbaum-Saunders"} @tab @tab @qcode{"bisa"} @tab @tab 2 ## @item @qcode{"Burr"} @tab @tab @qcode{"burr"} @tab @tab 3 ## @item @qcode{"Cauchy"} @tab @tab @qcode{"cauchy"} @tab @tab 2 ## @item @qcode{"Chi-squared"} @tab @tab @qcode{"chi2"} @tab @tab 1 ## @item @qcode{"Extreme Value"} @tab @tab @qcode{"ev"} @tab @tab 2 ## @item @qcode{"Exponential"} @tab @tab @qcode{"exp"} @tab @tab 1 ## @item @qcode{"F-Distribution"} @tab @tab @qcode{"f"} @tab @tab 2 ## @item @qcode{"Gamma"} @tab @tab @qcode{"gam"} @tab @tab 2 ## @item @qcode{"Geometric"} @tab @tab @qcode{"geo"} @tab @tab 1 ## @item @qcode{"Generalized Extreme Value"} @tab @tab @qcode{"gev"} @tab @tab 3 ## @item @qcode{"Generalized Pareto"} @tab @tab @qcode{"gp"} @tab @tab 3 ## @item @qcode{"Gumbel"} @tab @tab @qcode{"gumbel"} @tab @tab 2 ## @item @qcode{"Half-normal"} @tab @tab @qcode{"hn"} @tab @tab 2 ## @item @qcode{"Hypergeometric"} @tab @tab @qcode{"hyge"} @tab @tab 3 ## @item @qcode{"Inverse Gaussian"} @tab @tab @qcode{"invg"} @tab @tab 2 ## @item @qcode{"Laplace"} @tab @tab @qcode{"laplace"} @tab @tab 2 ## @item @qcode{"Logistic"} @tab @tab @qcode{"logi"} @tab @tab 2 ## @item @qcode{"Log-Logistic"} @tab @tab @qcode{"logl"} @tab @tab 2 ## @item @qcode{"Lognormal"} @tab @tab @qcode{"logn"} @tab @tab 2 ## @item @qcode{"Nakagami"} @tab @tab @qcode{"naka"} @tab @tab 2 ## @item @qcode{"Negative Binomial"} @tab @tab @qcode{"nbin"} @tab @tab 2 ## @item @qcode{"Noncentral F-Distribution"} @tab @tab @qcode{"ncf"} @tab @tab 3 ## @item @qcode{"Noncentral Student T"} @tab @tab @qcode{"nct"} @tab @tab 2 ## @item @qcode{"Noncentral Chi-Squared"} @tab @tab @qcode{"ncx2"} @tab @tab 2 ## @item @qcode{"Normal"} @tab @tab @qcode{"norm"} @tab @tab 2 ## @item @qcode{"Poisson"} @tab @tab @qcode{"poiss"} @tab @tab 1 ## @item @qcode{"Rayleigh"} @tab @tab @qcode{"rayl"} @tab @tab 1 ## @item @qcode{"Rician"} @tab @tab @qcode{"rice"} @tab @tab 2 ## @item @qcode{"Student T"} @tab @tab @qcode{"t"} @tab @tab 1 ## @item @qcode{"location-scale T"} @tab @tab @qcode{"tls"} @tab @tab 3 ## @item @qcode{"Triangular"} @tab @tab @qcode{"tri"} @tab @tab 3 ## @item @qcode{"Discrete Uniform"} @tab @tab @qcode{"unid"} @tab @tab 1 ## @item @qcode{"Uniform"} @tab @tab @qcode{"unif"} @tab @tab 2 ## @item @qcode{"Von Mises"} @tab @tab @qcode{"vm"} @tab @tab 2 ## @item @qcode{"Weibull"} @tab @tab @qcode{"wbl"} @tab @tab 2 ## @end multitable ## ## @seealso{cdf, icdf, pdf, betarnd, binornd, bisarnd, burrrnd, cauchyrnd, ## chi2rnd, evrnd, exprnd, frnd, gamrnd, geornd, gevrnd, gprnd, gumbelrnd, ## hnrnd, hygernd, invgrnd, laplacernd, logirnd, loglrnd, lognrnd, nakarnd, ## nbinrnd, ncfrnd, nctrnd, ncx2rnd, normrnd, poissrnd, raylrnd, ricernd, trnd, ## tlsrnd, trirnd, unidrnd, unifrnd, vmrnd, wblrnd} ## @end deftypefn function r = random (name, varargin) ## implemented functions persistent allDF = { ... {"beta" , "Beta"}, @betarnd, 2, ... {"bino" , "Binomial"}, @binornd, 2, ... {"bisa" , "Birnbaum-Saunders"}, @bisarnd, 2, ... {"burr" , "Burr"}, @burrrnd, 3, ... {"cauchy" , "Cauchy"}, @cauchyrnd, 2, ... {"chi2" , "Chi-squared"}, @chi2rnd, 1, ... {"ev" , "Extreme Value"}, @evrnd, 2, ... {"exp" , "Exponential"}, @exprnd, 1, ... {"f" , "F-Distribution"}, @frnd, 2, ... {"gam" , "Gamma"}, @gamrnd, 2, ... {"geo" , "Geometric"}, @geornd, 1, ... {"gev" , "Generalized Extreme Value"}, @gevrnd, 3, ... {"gp" , "Generalized Pareto"}, @gprnd, 3, ... {"gumbel" , "Gumbel"}, @gumbelrnd, 2, ... {"hn" , "Half-normal"}, @hnrnd, 2, ... {"hyge" , "Hypergeometric"}, @hygernd, 3, ... {"invg" , "Inverse Gaussian"}, @invgrnd, 2, ... {"laplace" , "Laplace"}, @laplacernd, 2, ... {"logi" , "Logistic"}, @logirnd, 2, ... {"logl" , "Log-Logistic"}, @loglrnd, 2, ... {"logn" , "Lognormal"}, @lognrnd, 2, ... {"naka" , "Nakagami"}, @nakarnd, 2, ... {"nbin" , "Negative Binomial"}, @nbinrnd, 2, ... {"ncf" , "Noncentral F-Distribution"}, @ncfrnd, 3, ... {"nct" , "Noncentral Student T"}, @nctrnd, 2, ... {"ncx2" , "Noncentral Chi-squared"}, @ncx2rnd, 2, ... {"norm" , "Normal"}, @normrnd, 2, ... {"poiss" , "Poisson"}, @poissrnd, 1, ... {"rayl" , "Rayleigh"}, @raylrnd, 1, ... {"rice" , "Rician"}, @ricernd, 2, ... {"t" , "Student T"}, @trnd, 1, ... {"tls" , "location-scale T"}, @tlsrnd, 3, ... {"tri" , "Triangular"}, @trirnd, 3, ... {"unid" , "Discrete Uniform"}, @unidrnd, 1, ... {"unif" , "Uniform"}, @unifrnd, 2, ... {"vm" , "Von Mises"}, @vmrnd, 2, ... {"wbl" , "Weibull"}, @wblrnd, 2}; if (! ischar (name)) error ("random: distribution NAME must a char string."); endif ## Get number of arguments nargs = numel (varargin); ## Get available functions rndnames = allDF(1:3:end); rndhandl = allDF(2:3:end); rnd_args = allDF(3:3:end); ## Search for RND function idx = cellfun (@(x)any(strcmpi (name, x)), rndnames); if (any (idx)) if (nargs == rnd_args{idx}) ## Check that all distribution parameters are numeric if (! all (cellfun (@(x)isnumeric(x), (varargin)))) error ("random: distribution parameters must be numeric."); endif ## Call appropriate RND r = feval (rndhandl{idx}, varargin{:}); elseif (nargs > rnd_args{idx}) ## Check that all distribution parameters are numeric if (! all (cellfun (@(x)isnumeric(x), (varargin(1:rnd_args{idx}))))) error ("random: distribution parameters must be numeric."); endif ## Call appropriate RND. SIZE arguments are checked by the RND function. r = feval (rndhandl{idx}, varargin{:}); else if (rnd_args{idx} == 1) error ("random: %s distribution requires 1 parameter.", name); else error ("random: %s distribution requires %d parameters.", ... name, rnd_args{idx}); endif endif else error ("random: %s distribution is not implemented in Statistics.", name); endif endfunction ## Test results %!assert (size (random ("Beta", 5, 2, 2, 10)), size (betarnd (5, 2, 2, 10))) %!assert (size (random ("beta", 5, 2, 2, 10)), size (betarnd (5, 2, 2, 10))) %!assert (size (random ("Binomial", 5, 2, [10, 20])), size (binornd (5, 2, 10, 20))) %!assert (size (random ("bino", 5, 2, [10, 20])), size (binornd (5, 2, 10, 20))) %!assert (size (random ("Birnbaum-Saunders", 5, 2, [10, 20])), size (bisarnd (5, 2, 10, 20))) %!assert (size (random ("bisa", 5, 2, [10, 20])), size (bisarnd (5, 2, 10, 20))) %!assert (size (random ("Burr", 5, 2, 2, [10, 20])), size (burrrnd (5, 2, 2, 10, 20))) %!assert (size (random ("burr", 5, 2, 2, [10, 20])), size (burrrnd (5, 2, 2, 10, 20))) %!assert (size (random ("Cauchy", 5, 2, [10, 20])), size (cauchyrnd (5, 2, 10, 20))) %!assert (size (random ("cauchy", 5, 2, [10, 20])), size (cauchyrnd (5, 2, 10, 20))) %!assert (size (random ("Chi-squared", 5, [10, 20])), size (chi2rnd (5, 10, 20))) %!assert (size (random ("chi2", 5, [10, 20])), size (chi2rnd (5, 10, 20))) %!assert (size (random ("Extreme Value", 5, 2, [10, 20])), size (evrnd (5, 2, 10, 20))) %!assert (size (random ("ev", 5, 2, [10, 20])), size (evrnd (5, 2, 10, 20))) %!assert (size (random ("Exponential", 5, [10, 20])), size (exprnd (5, 10, 20))) %!assert (size (random ("exp", 5, [10, 20])), size (exprnd (5, 10, 20))) %!assert (size (random ("F-Distribution", 5, 2, [10, 20])), size (frnd (5, 2, 10, 20))) %!assert (size (random ("f", 5, 2, [10, 20])), size (frnd (5, 2, 10, 20))) %!assert (size (random ("Gamma", 5, 2, [10, 20])), size (gamrnd (5, 2, 10, 20))) %!assert (size (random ("gam", 5, 2, [10, 20])), size (gamrnd (5, 2, 10, 20))) %!assert (size (random ("Geometric", 5, [10, 20])), size (geornd (5, 10, 20))) %!assert (size (random ("geo", 5, [10, 20])), size (geornd (5, 10, 20))) %!assert (size (random ("Generalized Extreme Value", 5, 2, 2, [10, 20])), size (gevrnd (5, 2, 2, 10, 20))) %!assert (size (random ("gev", 5, 2, 2, [10, 20])), size (gevrnd (5, 2, 2, 10, 20))) %!assert (size (random ("Generalized Pareto", 5, 2, 2, [10, 20])), size (gprnd (5, 2, 2, 10, 20))) %!assert (size (random ("gp", 5, 2, 2, [10, 20])), size (gprnd (5, 2, 2, 10, 20))) %!assert (size (random ("Gumbel", 5, 2, [10, 20])), size (gumbelrnd (5, 2, 10, 20))) %!assert (size (random ("gumbel", 5, 2, [10, 20])), size (gumbelrnd (5, 2, 10, 20))) %!assert (size (random ("Half-normal", 5, 2, [10, 20])), size (hnrnd (5, 2, 10, 20))) %!assert (size (random ("hn", 5, 2, [10, 20])), size (hnrnd (5, 2, 10, 20))) %!assert (size (random ("Hypergeometric", 5, 2, 2, [10, 20])), size (hygernd (5, 2, 2, 10, 20))) %!assert (size (random ("hyge", 5, 2, 2, [10, 20])), size (hygernd (5, 2, 2, 10, 20))) %!assert (size (random ("Inverse Gaussian", 5, 2, [10, 20])), size (invgrnd (5, 2, 10, 20))) %!assert (size (random ("invg", 5, 2, [10, 20])), size (invgrnd (5, 2, 10, 20))) %!assert (size (random ("Laplace", 5, 2, [10, 20])), size (laplacernd (5, 2, 10, 20))) %!assert (size (random ("laplace", 5, 2, [10, 20])), size (laplacernd (5, 2, 10, 20))) %!assert (size (random ("Logistic", 5, 2, [10, 20])), size (logirnd (5, 2, 10, 20))) %!assert (size (random ("logi", 5, 2, [10, 20])), size (logirnd (5, 2, 10, 20))) %!assert (size (random ("Log-Logistic", 5, 2, [10, 20])), size (loglrnd (5, 2, 10, 20))) %!assert (size (random ("logl", 5, 2, [10, 20])), size (loglrnd (5, 2, 10, 20))) %!assert (size (random ("Lognormal", 5, 2, [10, 20])), size (lognrnd (5, 2, 10, 20))) %!assert (size (random ("logn", 5, 2, [10, 20])), size (lognrnd (5, 2, 10, 20))) %!assert (size (random ("Nakagami", 5, 2, [10, 20])), size (nakarnd (5, 2, 10, 20))) %!assert (size (random ("naka", 5, 2, [10, 20])), size (nakarnd (5, 2, 10, 20))) %!assert (size (random ("Negative Binomial", 5, 2, [10, 20])), size (nbinrnd (5, 2, 10, 20))) %!assert (size (random ("nbin", 5, 2, [10, 20])), size (nbinrnd (5, 2, 10, 20))) %!assert (size (random ("Noncentral F-Distribution", 5, 2, 2, [10, 20])), size (ncfrnd (5, 2, 2, 10, 20))) %!assert (size (random ("ncf", 5, 2, 2, [10, 20])), size (ncfrnd (5, 2, 2, 10, 20))) %!assert (size (random ("Noncentral Student T", 5, 2, [10, 20])), size (nctrnd (5, 2, 10, 20))) %!assert (size (random ("nct", 5, 2, [10, 20])), size (nctrnd (5, 2, 10, 20))) %!assert (size (random ("Noncentral Chi-Squared", 5, 2, [10, 20])), size (ncx2rnd (5, 2, 10, 20))) %!assert (size (random ("ncx2", 5, 2, [10, 20])), size (ncx2rnd (5, 2, 10, 20))) %!assert (size (random ("Normal", 5, 2, [10, 20])), size (normrnd (5, 2, 10, 20))) %!assert (size (random ("norm", 5, 2, [10, 20])), size (normrnd (5, 2, 10, 20))) %!assert (size (random ("Poisson", 5, [10, 20])), size (poissrnd (5, 10, 20))) %!assert (size (random ("poiss", 5, [10, 20])), size (poissrnd (5, 10, 20))) %!assert (size (random ("Rayleigh", 5, [10, 20])), size (raylrnd (5, 10, 20))) %!assert (size (random ("rayl", 5, [10, 20])), size (raylrnd (5, 10, 20))) %!assert (size (random ("Rician", 5, 1, [10, 20])), size (ricernd (5, 1, 10, 20))) %!assert (size (random ("rice", 5, 1, [10, 20])), size (ricernd (5, 1, 10, 20))) %!assert (size (random ("Student T", 5, [10, 20])), size (trnd (5, 10, 20))) %!assert (size (random ("t", 5, [10, 20])), size (trnd (5, 10, 20))) %!assert (size (random ("location-scale T", 5, 1, 2, [10, 20])), size (tlsrnd (5, 1, 2, 10, 20))) %!assert (size (random ("tls", 5, 1, 2, [10, 20])), size (tlsrnd (5, 1, 2, 10, 20))) %!assert (size (random ("Triangular", 5, 2, 2, [10, 20])), size (trirnd (5, 2, 2, 10, 20))) %!assert (size (random ("tri", 5, 2, 2, [10, 20])), size (trirnd (5, 2, 2, 10, 20))) %!assert (size (random ("Discrete Uniform", 5, [10, 20])), size (unidrnd (5, 10, 20))) %!assert (size (random ("unid", 5, [10, 20])), size (unidrnd (5, 10, 20))) %!assert (size (random ("Uniform", 5, 2, [10, 20])), size (unifrnd (5, 2, 10, 20))) %!assert (size (random ("unif", 5, 2, [10, 20])), size (unifrnd (5, 2, 10, 20))) %!assert (size (random ("Von Mises", 5, 2, [10, 20])), size (vmrnd (5, 2, 10, 20))) %!assert (size (random ("vm", 5, 2, [10, 20])), size (vmrnd (5, 2, 10, 20))) %!assert (size (random ("Weibull", 5, 2, [10, 20])), size (wblrnd (5, 2, 10, 20))) %!assert (size (random ("wbl", 5, 2, [10, 20])), size (wblrnd (5, 2, 10, 20))) ## Test input validation %!error random (1) %!error random ({"beta"}) %!error ... %! random ("Beta", "a", 2) %!error ... %! random ("Beta", 5, "") %!error ... %! random ("Beta", 5, {2}) %!error ... %! random ("Beta", "a", 2, 2, 10) %!error ... %! random ("Beta", 5, "", 2, 10) %!error ... %! random ("Beta", 5, {2}, 2, 10) %!error ... %! random ("Beta", 5, "", 2, 10) %!error random ("chi2") %!error random ("Beta", 5) %!error random ("Burr", 5) %!error random ("Burr", 5, 2) statistics-release-1.7.3/inst/ecdf.m000066400000000000000000000257531475240274700174160ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{f}, @var{x}] =} ecdf (@var{y}) ## @deftypefnx {statistics} {[@var{f}, @var{x}, @var{flo}, @var{fup}] =} ecdf (@var{y}) ## @deftypefnx {statistics} {} ecdf (@dots{}) ## @deftypefnx {statistics} {} ecdf (@var{ax}, @dots{}) ## @deftypefnx {statistics} {[@dots{}] =} ecdf (@var{y}, @var{name}, @var{value}, @dots{}) ## @deftypefnx {statistics} {[@dots{}] =} ecdf (@var{ax}, @var{y}, @var{name}, @var{value}, @dots{}) ## ## Empirical (Kaplan-Meier) cumulative distribution function. ## ## @code{[@var{f}, @var{x}] = ecdf (@var{y})} calculates the Kaplan-Meier ## estimate of the cumulative distribution function (cdf), also known as the ## empirical cdf. @var{y} is a vector of data values. @var{f} is a vector of ## values of the empirical cdf evaluated at @var{x}. ## ## @code{[@var{f}, @var{x}, @var{flo}, @var{fup}] = ecdf (@var{y})} also returns ## lower and upper confidence bounds for the cdf. These bounds are calculated ## using Greenwood's formula, and are not simultaneous confidence bounds. ## ## @code{ecdf (@dots{})} without output arguments produces a plot of the ## empirical cdf. ## ## @code{ecdf (@var{ax}, @dots{})} plots into existing axes @var{ax}. ## ## @code{[@dots{}] = ecdf (@var{y}, @var{name}, @var{value}, @dots{})} specifies ## additional parameter name/value pairs chosen from the following: ## ## @multitable @columnfractions 0.20 0.8 ## @headitem @var{name} @tab @var{value} ## @item "censoring" @tab A boolean vector of the same size as Y that is 1 for ## observations that are right-censored and 0 for observations that are observed ## exactly. Default is all observations observed exactly. ## ## @item "frequency" @tab A vector of the same size as Y containing non-negative ## integer counts. The jth element of this vector gives the number of times the ## jth element of Y was observed. Default is 1 observation per Y element. ## ## @item "alpha" @tab A value @var{alpha} between 0 and 1 specifying the ## significance level. Default is 0.05 for 5% significance. ## ## @item "function" @tab The type of function returned as the F output argument, ## chosen from "cdf" (the default), "survivor", or "cumulative hazard". ## ## @item "bounds" @tab Either "on" to include bounds or "off" (the default) to ## omit them. Used only for plotting. ## @end multitable ## ## Type @code{demo ecdf} to see examples of usage. ## ## @seealso{cdfplot, ecdfhist} ## @end deftypefn function [Fout, x, Flo, Fup] = ecdf (y, varargin) ## Check for valid input arguments narginchk (1, Inf); ## Parse input arguments if (nargin == 1) ax = []; if (! isvector (y) || ! isreal (y)) error ("ecdf: Y must be a vector of real numbers."); endif ##x = varargin{1}; else ## ax = y; ## Check that ax is a valid axis handle try isstruct (get (y)); ax = y; y = varargin{1}; varargin{1} = []; catch ##error ("ecdf: invalid handle %f.", ax); ax = []; end_try_catch #y = varargin{1}; #varargin{1} = []; endif ## Make y a column vector x = y(:); ## Add defaults cens = zeros (size (x)); freq = ones (size (x)); alpha = 0.05; fname = "cdf"; bound = "off"; ## Check for remaining varargins and parse extra parameters if (length (varargin) > 0 && mod (numel (varargin), 2) == 0) [~, prop] = parseparams (varargin); while (!isempty (prop)) switch (lower (prop{1})) case "censoring" cens = prop{2}; ## Check for valid input if (! isequal (size (x), size (cens))) error ("ecdf: censoring data mismatch Y vector."); endif ## Make double in case censoring data is logical if (islogical (cens)) cens = double (cens); endif case "frequency" freq = prop{2}; ## Check for valid input if (! isequal (size (x), size (freq))) error ("ecdf: frequency data mismatch Y vector."); endif ## Make double in case frequency data is logical if (islogical (freq)) freq = double (freq); endif case "alpha" alpha = prop{2}; ## Check for valid alpha value if (numel (alpha) != 1 || ! isnumeric (alpha) || alpha <= 0 || alpha >= 1) error ("ecdf: alpha must be a numeric scalar in the range (0,1)."); endif case "function" fname = prop{2}; ## Check for valid function name option if (sum (strcmpi (fname, {"cdf", "survivor", "cumulative hazard"})) < 1) error ("ecdf: wrong function name."); endif case "bounds" bound = prop{2}; ## Check for valid bounds option if (! (strcmpi (bound, "off") || strcmpi (bound, "on"))) error ("ecdf: wrong bounds."); endif otherwise error ("ecdf: unknown option %s", prop{1}); endswitch prop = prop(3:end); endwhile elseif nargin > 2 error ("ecdf: optional parameters must be in name/value pairs."); endif ## Remove NaNs from data rn = ! isnan (x) & ! isnan (cens) & freq > 0; x = x(rn); if (length (x) == 0) error ("ecdf: not enought data."); endif cens = cens(rn); freq = freq(rn); ## Sort data in ascending order [x, sr] = sort (x); cens = cens(sr); freq = freq(sr); ## Keep class for data (single | double) if (isa (x, "single")) freq = single (freq); endif ## Calculate cumulative frequencies tcfreq = cumsum (freq); ocfreq = cumsum (freq .* ! cens); x_diff = (diff (x) == 0); if (any (x_diff)) x(x_diff) = []; tcfreq(x_diff) = []; ocfreq(x_diff) = []; endif max_count = tcfreq(end); ## Get Deaths and Number at Risk for each unique X Death = [ocfreq(1); diff(ocfreq)]; NRisk = max_count - [0; tcfreq(1:end-1)]; ## Remove no Death observations x = x(Death > 0); NRisk = NRisk(Death > 0); Death = Death(Death > 0); ## Estimate function switch (fname) case "cdf" S = cumprod (1 - Death ./ NRisk); Fun_x = 1 - S; Fzero = 0; fdisp = "F(x)"; case "survivor" S = cumprod (1 - Death ./ NRisk); Fun_x = S; Fzero = 1; fdisp = "S(x)"; case "cumulative hazard" Fun_x = cumsum (Death ./ NRisk); Fzero = 0; fdisp = "H(x)"; endswitch ## Check for remaining non-censored data and add a starting value if (! isempty (Death)) x = [min(y); x]; F = [Fzero; Fun_x]; else warning("ecdf: No Death in data"); F = Fun_x; endif ## Calculate lower and upper confidence bounds if requested if (nargout > 2 || (nargout == 0 && strcmpi (bound, "on"))) switch (fname) case {"cdf", "survivor"} se = NaN (size (Death)); if (! isempty (Death)) if (NRisk(end) == Death(end)) t = 1:length (NRisk) - 1; else t = 1:length (NRisk); endif se(t) = S(t) .* sqrt (cumsum (Death(t) ./ ... (NRisk(t) .* (NRisk(t) - Death(t))))); endif case "cumulative hazard" se = sqrt (cumsum (Death ./ (NRisk .* NRisk))); endswitch ## Calculate confidence limits if (! isempty (se)) z_a = - norminv (alpha / 2); h_w = z_a * se; Flo = max (0, Fun_x - h_w); Flo(isnan (h_w)) = NaN; switch (fname) case {"cdf", "survivor"} Fup = min (1, Fun_x + h_w); Fup(isnan (h_w)) = NaN; case "cumulative hazard" Fup = Fun_x + h_w; endswitch Flo = [NaN; Flo]; Fup = [NaN; Fup]; else Flo = []; Fup = []; endif else Flo = []; Fup = []; endif ## Plot stairs if no output is requested if (nargout == 0) if (isempty (ax)) ax = newplot(); end h = stairs(ax, x , [F, Flo, Fup]); xlabel (ax, "x"); ylabel (ax, fdisp); title ("ecdf"); else Fout = F; endif endfunction %!demo %! y = exprnd (10, 50, 1); ## random failure times are exponential(10) %! d = exprnd (20, 50, 1); ## drop-out times are exponential(20) %! t = min (y, d); ## we observe the minimum of these times %! censored = (y > d); ## we also observe whether the subject failed %! %! ## Calculate and plot the empirical cdf and confidence bounds %! [f, x, flo, fup] = ecdf (t, "censoring", censored); %! stairs (x, f); %! hold on; %! stairs (x, flo, "r:"); stairs (x, fup, "r:"); %! %! ## Superimpose a plot of the known true cdf %! xx = 0:.1:max (t); yy = 1 - exp (-xx / 10); plot (xx, yy, "g-"); %! hold off; %!demo %! R = wblrnd (100, 2, 100, 1); %! ecdf (R, "Function", "survivor", "Alpha", 0.01, "Bounds", "on"); %! hold on %! x = 1:1:250; %! wblsurv = 1 - cdf ("weibull", x, 100, 2); %! plot (x, wblsurv, "g-", "LineWidth", 2) %! legend ("Empirical survivor function", "Lower confidence bound", ... %! "Upper confidence bound", "Weibull survivor function", ... %! "Location", "northeast"); %! hold off ## Test input %!error ecdf (); %!error ecdf (randi (15,2)); %!error ecdf ([3,2,4,3+2i,5]); %!error kstest ([2,3,4,5,6],"tail"); %!error kstest ([2,3,4,5,6],"tail", "whatever"); %!error kstest ([2,3,4,5,6],"function", ""); %!error kstest ([2,3,4,5,6],"badoption", 0.51); %!error kstest ([2,3,4,5,6],"tail", 0); %!error kstest ([2,3,4,5,6],"alpha", 0); %!error kstest ([2,3,4,5,6],"alpha", NaN); %!error kstest ([NaN,NaN,NaN,NaN,NaN],"tail", "unequal"); %!error kstest ([2,3,4,5,6],"alpha", 0.05, "CDF", [2,3,4;1,3,4;1,2,1]); ## Test output against MATLAB results %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [2, 3, 4, 3, 5, 4, 6, 5, 8, 3, 7, 8, 9, 0]; %! [F, x, Flo, Fup] = ecdf (x); %! F_out = [0; 0.0714; 0.1429; 0.3571; 0.5; 0.6429; 0.7143; 0.7857; 0.9286; 1]; %! assert (F, F_out, ones (10,1) * 1e-4); %! x_out = [0 0 2 3 4 5 6 7 8 9]'; %! assert (x, x_out); %! Flo_out = [NaN, 0, 0, 0.1061, 0.2381, 0.3919, 0.4776, 0.5708, 0.7937, NaN]'; %! assert (Flo, Flo_out, ones (10,1) * 1e-4); %! Fup_out = [NaN, 0.2063, 0.3262, 0.6081, 0.7619, 0.8939, 0.9509, 1, 1, NaN]'; %! assert (Fup, Fup_out, ones (10,1) * 1e-4); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [2, 3, 4, 3, 5, 4, 6, 5, 8, 3, 7, 8, 9, 0]; %! ecdf (x); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect statistics-release-1.7.3/inst/einstein.m000066400000000000000000000225361475240274700203270ustar00rootroot00000000000000## Copyright (C) 2023 Arun Giridhar ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} einstein () ## @deftypefnx {statistics} {@var{tiles} =} einstein (@var{a}, @var{b}) ## @deftypefnx {statistics} {[@var{tiles}, @var{rhat}] =} einstein (@var{a}, @var{b}) ## @deftypefnx {statistics} {[@var{tiles}, @var{rhat}, @var{that}] =} einstein (@var{a}, @var{b}) ## @deftypefnx {statistics} {[@var{tiles}, @var{rhat}, @var{that}, @var{shat}] =} einstein (@var{a}, @var{b}) ## @deftypefnx {statistics} {[@var{tiles}, @var{rhat}, @var{that}, @var{shat}, @var{phat}] =} einstein (@var{a}, @var{b}) ## @deftypefnx {statistics} {[@var{tiles}, @var{rhat}, @var{that}, @var{shat}, @var{phat}, @var{fhat}] =} einstein (@var{a}, @var{b}) ## ## Plots the tiling of the basic clusters of einstein tiles. ## ## Scalars @var{a} and @var{b} define the shape of the einstein tile. ## See Smith et al (2023) for details: @url{https://arxiv.org/abs/2303.10798} ## ## @itemize ## @item @var{tiles} is a structure containing the coordinates of the einstein ## tiles that are tiled on the plot. Each field contains the tile coordinates ## of the corresponding clusters. ## @itemize ## @item @var{tiles}@qcode{.rhat} contains the reflected einstein tiles ## @item @var{tiles}@qcode{.that} contains the three-hat shells ## @item @var{tiles}@qcode{.shat} contains the single-hat clusters ## @item @var{tiles}@qcode{.phat} contains the paired-hat clusters ## @item @var{tiles}@qcode{.fhat} contains the fylfot clusters ## @end itemize ## ## @item @var{rhat} contains the coordinates of the first reflected tile ## @item @var{that} contains the coordinates of the first three-hat shell ## @item @var{shat} contains the coordinates of the first single-hat cluster ## @item @var{phat} contains the coordinates of the first paired-hat cluster ## @item @var{fhat} contains the coordinates of the first fylfot cluster ## @end itemize ## ## @end deftypefn function [varargout] = einstein (a, b, varargin) ## Check for valid number of input arguments if (nargin < 2) print_usage; endif ## Check A and B for valid type and range if (! (isscalar (a) && isscalar (b) && isnumeric (a) && isnumeric (b)... && isreal (a) && isreal (b))) error ("einstein: A and B must real scalars."); end if (a <= 0 || a >= 1 || b <= 0 || b >= 1) error ("einstein: A and B must be within the open interval (0,1)."); endif ## Get initial hat points single_hat = getpoly (a, b) * rotz (-150)([1,2],[1,2]); ## Make a reflected-hat reflecthat = [-1, 1] .* single_hat; ## Make a three-hat shell cluster three_hat1 = rotatehat (single_hat, -60); three_hat1 = three_hat1 + (reflecthat(1,:) - three_hat1(9,:)); three_hat2 = three_hat1 + (reflecthat(6,:) - three_hat1(11,:)); three_hat3 = rotatehat (three_hat1, 120); three_hat3 = three_hat3 + (reflecthat(5,:) - three_hat1(9,:)); three_hat = [three_hat1, three_hat2, three_hat3]; ## Translate another four-tile cluster translate = three_hat(5,[3,4]) - three_hat(12,[1,2]); all.rhat = translatehat (reflecthat, translate); all.that = translatehat (three_hat, translate); all.rhat = [reflecthat, all.rhat]; all.that = [three_hat, all.that]; ## Rotate and translate another four-tile cluster tmp_3hat = rotatehat (three_hat, 120); tmp_rhat = rotatehat (reflecthat, 120); translate = three_hat(12,[5,6]) - tmp_3hat(5,[3,4]); tmp_3hat = translatehat (tmp_3hat, translate); tmp_rhat = translatehat (tmp_rhat, translate); all.that = [all.that, tmp_3hat]; all.rhat = [all.rhat, tmp_rhat]; ## Plot four-tile clusters patch (all.that(:,[1:2:end]), all.that(:,[2:2:end]), "LineWidth", 2, ... "FaceColor", "c", "EdgeColor","k"); patch (all.rhat(:,[1:2:end]), all.rhat(:,[2:2:end]), "LineWidth", 2, ... "FaceColor", "b", "EdgeColor","k"); title (sprintf ("a = %4.2f b = %4.2f", a, b), "FontSize",30) ## Make a single-hat cluster translate = three_hat(10,[3,4]) - single_hat(2,:); singlehat = translatehat (single_hat, translate); all.shat = singlehat; ## Plot single-tile cluster patch (all.shat(:,1), all.shat(:,2), "LineWidth", 2, ... "FaceColor", [0.95, 0.95, 0.95], "EdgeColor","k"); axis ("equal") ## Make a paired-hat cluster paired_hat1 = all.shat + (all.rhat(9,[5,6]) - all.shat(5,:)); paired_hat2 = three_hat(:,[3,4]) + (three_hat(1,[5,6]) - three_hat(3,[3,4])); paired_hat = [paired_hat1, paired_hat2]; ## Rotate and translate another two paired-hat clusters tmp_phat = rotatehat (paired_hat, -120); translate = three_hat(4,[3,4]) - tmp_phat(4,[3,4]); tmp_phat = translatehat (tmp_phat, translate); all.phat = [paired_hat, tmp_phat]; tmp_phat = rotatehat (paired_hat, -60); translate = all.that(10,[11,12]) - tmp_phat(11,[3,4]); tmp_phat = translatehat (tmp_phat, translate); all.phat = [all.phat, tmp_phat]; ## Plot paired-tiles clusters patch (all.phat(:,[1:2:end]), all.phat(:,[2:2:end]), "LineWidth", 2, ... "FaceColor", [0.9, 0.9, 0.9], "EdgeColor","k"); ## Make a fylfot cluster fylfot_hat = paired_hat; tmp_fhat = rotatehat (paired_hat, -120); translate = fylfot_hat(1,[3,4]) - tmp_fhat(3,[3,4]); tmp_fhat = translatehat (tmp_fhat, translate); fylfot_hat = [fylfot_hat, tmp_fhat]; tmp_fhat = rotatehat (paired_hat, 120); translate = fylfot_hat(3,[3,4]) - tmp_fhat(1,[3,4]); tmp_fhat = translatehat (tmp_fhat, translate); fylfot_hat = [fylfot_hat, tmp_fhat]; translate = all.that(13,[13,14]) - fylfot_hat(13,[7,8]); fylfot_hat = translatehat (fylfot_hat, translate); ## Translate another two fylfot clusters translate = all.that(4,[9,10]) - fylfot_hat(13,[3,4]); tmp_fhat = translatehat (fylfot_hat, translate); all.fhat = [fylfot_hat, tmp_fhat]; translate = all.that(13,[1,2]) - fylfot_hat(4,[3,4]); tmp_fhat = translatehat (fylfot_hat, translate); all.fhat = [all.fhat, tmp_fhat]; ## Plot fylfot clusters patch (all.fhat(:,[1:2:end]), all.fhat(:,[2:2:end]), "LineWidth", 2, ... "FaceColor", "r", "EdgeColor","k"); if (nargout > 0) varargout{1} = all; endif if (nargout > 1) varargout{2} = reflecthat; endif if (nargout > 2) varargout{3} = three_hat; endif if (nargout > 3) varargout{4} = singlehat; endif if (nargout > 4) varargout{5} = paired_hat; endif if (nargout > 5) varargout{6} = fylfot_hat; endif endfunction ## Rotates a cluster of hats. function newhat = rotatehat (hat, degrees) rotM = rotz (degrees)([1,2],[1,2]); newhat = zeros (size (hat)); for i=1:2:columns (hat) newhat(:,[i,i+1]) = hat(:,[i,i+1]) * rotM; endfor endfunction ## Translates a cluster of hats. function newhat = translatehat (hat, dist) nhats = columns (hat) / 2; newhat = hat + repmat (dist, 1, nhats); endfunction ## Returns a unit vector given a direction angle in degrees function ret = u (t) persistent angles = (0:30:330); persistent tbl = [cosd(angles); sind(angles)]'; ret = tbl(angles == mod (t, 360), :); endfunction ## Returns the hat polygon function single_hat = getpoly (a, b) single_hat = zeros (14, 2); pos = 0; single_hat(++pos, :) = [0 0]; t = 270; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t); t += 60; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t); t -= 90; single_hat(pos+1, :) = single_hat(pos++, :) + b * u(t); t += 60; single_hat(pos+1, :) = single_hat(pos++, :) + b * u(t); t += 90; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t); t -= 60; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t); t += 90; single_hat(pos+1, :) = single_hat(pos++, :) + b * u(t); t -= 60; single_hat(pos+1, :) = single_hat(pos++, :) + b * u(t); t += 90; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t); t += 60; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t) * 2; t += 60; single_hat(pos+1, :) = single_hat(pos++, :) + a * u(t); t -= 90; single_hat(pos+1, :) = single_hat(pos++, :) + b * u(t); t += 60; single_hat(pos+1, :) = single_hat(pos++, :) + b * u(t); ## t(14, :) == t(1, :) to within numerical roundoff endfunction %!demo %! einstein (0.4, 0.6) %!demo %! einstein (0.2, 0.5) %!demo %! einstein (0.6, 0.1) ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! tiles = einstein (0.4, 0.6); %! assert (isstruct (tiles), true); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error einstein %!error einstein (0.5) %!error einstein (0, 0.9) %!error einstein (0.4, 1) %!error einstein (-0.4, 1) statistics-release-1.7.3/inst/evalclusters.m000066400000000000000000000361651475240274700212300ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{eva} =} evalclusters (@var{x}, @var{clust}, @var{criterion}) ## @deftypefnx {statistics} {@var{eva} =} evalclusters (@dots{}, @qcode{Name}, @qcode{Value}) ## ## Create a clustering evaluation object to find the optimal number of clusters. ## ## @code{evalclusters} creates a clustering evaluation object to evaluate the ## optimal number of clusters for data @var{x}, using criterion @var{criterion}. ## The input data @var{x} is a matrix with @code{n} observations of @code{p} ## variables. ## The evaluation criterion @var{criterion} is one of the following: ## @table @code ## @item @qcode{CalinskiHarabasz} ## to create a @code{CalinskiHarabaszEvaluation} object. ## ## @item @qcode{DaviesBouldin} ## to create a @code{DaviesBouldinEvaluation} object. ## ## @item @qcode{gap} ## to create a @code{GapEvaluation} object. ## ## @item @qcode{silhouette} ## to create a @code{SilhouetteEvaluation} object. ## ## @end table ## The clustering algorithm @var{clust} is one of the following: ## @table @code ## @item @qcode{kmeans} ## to cluster the data using @code{kmeans} with @code{EmptyAction} set to ## @code{singleton} and @code{Replicates} set to 5. ## ## @item @qcode{linkage} ## to cluster the data using @code{clusterdata} with @code{linkage} set to ## @code{Ward}. ## ## @item @qcode{gmdistribution} ## to cluster the data using @code{fitgmdist} with @code{SharedCov} set to ## @code{true} and @code{Replicates} set to 5. ## ## @end table ## If the @var{criterion} is @code{CalinskiHarabasz}, @code{DaviesBouldin}, or ## @code{silhouette}, @var{clust} can also be a function handle to a function ## of the form @code{c = clust(x, k)}, where @var{x} is the input data, ## @var{k} the number of clusters to evaluate and @var{c} the clustering result. ## The clustering result can be either an array of size @code{n} with @code{k} ## different integer values, or a matrix of size @code{n} by @code{k} with a ## likelihood value assigned to each one of the @code{n} observations for each ## one of the @var{k} clusters. In the latter case, each observation is assigned ## to the cluster with the higher value. ## If the @var{criterion} is @code{CalinskiHarabasz}, @code{DaviesBouldin}, or ## @code{silhouette}, @var{clust} can also be a matrix of size @code{n} by ## @code{k}, where @code{k} is the number of proposed clustering solutions, so ## that each column of @var{clust} is a clustering solution. ## ## In addition to the obligatory @var{x}, @var{clust} and @var{criterion} inputs ## there is a number of optional arguments, specified as pairs of @code{Name} ## and @code{Value} options. The known @code{Name} arguments are: ## @table @code ## @item @qcode{KList} ## a vector of positive integer numbers, that is the cluster sizes to evaluate. ## This option is necessary, unless @var{clust} is a matrix of proposed ## clustering solutions. ## ## @item @qcode{Distance} ## a distance metric as accepted by the chosen @var{clust}. It can be the ## name of the distance metric as a string or a function handle. When ## @var{criterion} is @code{silhouette}, it can be a vector as created by ## function @code{pdist}. Valid distance metric strings are: @code{sqEuclidean} ## (default), @code{Euclidean}, @code{cityblock}, @code{cosine}, ## @code{correlation}, @code{Hamming}, @code{Jaccard}. ## Only used by @code{silhouette} and @code{gap} evaluation. ## ## @item @qcode{ClusterPriors} ## the prior probabilities of each cluster, which can be either @code{empirical} ## (default), or @code{equal}. When @code{empirical} the silhouette value is ## the average of the silhouette values of all points; when @code{equal} the ## silhouette value is the average of the average silhouette value of each ## cluster. Only used by @code{silhouette} evaluation. ## ## @item @qcode{B} ## the number of reference datasets generated from the reference distribution. ## Only used by @code{gap} evaluation. ## ## @item @qcode{ReferenceDistribution} ## the reference distribution used to create the reference data. It can be ## @code{PCA} (default) for a distribution based on the principal components of ## @var{X}, or @code{uniform} for a uniform distribution based on the range of ## the observed data. @code{PCA} is currently not implemented. ## Only used by @code{gap} evaluation. ## ## @item @qcode{SearchMethod} ## the method for selecting the optimal value with a @code{gap} evaluation. It ## can be either @code{globalMaxSE} (default) for selecting the smallest number ## of clusters which is inside the standard error of the maximum gap value, or ## @code{firstMaxSE} for selecting the first number of clusters which is inside ## the standard error of the following cluster number. ## Only used by @code{gap} evaluation. ## ## @end table ## ## Output @var{eva} is a clustering evaluation object. ## ## @end deftypefn ## ## @seealso{CalinskiHarabaszEvaluation, DaviesBouldinEvaluation, GapEvaluation, ## SilhouetteEvaluation} function cc = evalclusters (x, clust, criterion, varargin) ## input check if (nargin < 3) print_usage (); endif ## parsing input data if ((! ismatrix (x)) || (! isnumeric (x))) error ("evalclusters: 'x' must be a numeric matrix"); endif ## useful values for input check n = rows (x); p = columns (x); ## parsing the clustering algorithm if (ischar (clust)) clust = lower (clust); if (! any (strcmpi (clust, {"kmeans", "linkage", "gmdistribution"}))) error ("evalclusters: unknown clustering algorithm '%s'", clust); endif elseif (! isscalar (clust)) if ((! isnumeric (clust)) || (length (size (clust)) != 2) || ... (rows (clust) != n)) error ("evalclusters: invalid matrix of clustering solutions"); endif elseif (! isa (clust, "function_handle")) error ("evalclusters: invalid argument for 'clust'"); endif ## parsing the criterion parameter ## we check the rest later, as the check depends on the chosen criterion if (! ischar (criterion)) error ("evalclusters: invalid criterion, it must be a string"); else criterion = lower (criterion); if (! any (strcmpi (criterion, {"calinskiharabasz", "daviesbouldin", ... "silhouette", "gap"}))) error ("evalclusters: unknown criterion '%s'", criterion); endif endif ## some default value klist = []; distance = "sqeuclidean"; clusterpriors = "empirical"; b = 100; referencedistribution = "pca"; searchmethod = "globalmaxse"; ## parse the name/value pairs pair_index = 1; while (pair_index < (nargin - 3)) ## type check if (! ischar (varargin{pair_index})) error ("evalclusters: invalid property, string expected"); endif ## now parse the parameter switch (lower (varargin{pair_index})) case "klist" ## klist must be an array of positive interger numbers; ## there is a special case when it can be empty, but that is not the ## suggested way to use it (it is better to omit it instead) if (isempty (varargin{pair_index + 1})) if (ischar (clust) || isa (clust, "function_handle")) error (["evalclusters: 'KList' can be empty only when 'clust' "... "is a matrix"]); endif elseif ((! isnumeric (varargin{pair_index + 1})) || ... (! isvector (varargin{pair_index + 1})) || ... any (find (varargin{pair_index + 1} < 1)) || ... any (floor (varargin{pair_index + 1}) != varargin{pair_index + 1})) error ("evalclusters: 'KList' must be an array of positive integers"); endif klist = varargin{pair_index + 1}; case "distance" ## used by silhouette and gap if (! (strcmpi (criterion, "silhouette") || strcmpi (criterion, "gap"))) error (["evalclusters: distance metric cannot be used with '%s'"... " criterion"], criterion); endif if (ischar (varargin{pair_index + 1})) if (! any (strcmpi (varargin{pair_index + 1}, ... {"sqeuclidean", "euclidean", "cityblock", "cosine", ... "correlation", "hamming", "jaccard"}))) error ("evalclusters: unknown distance criterion '%s'", ... varargin{pair_index + 1}); endif elseif (! isa (varargin{pair_index + 1}, "function_handle") || ! ((isvector (varargin{pair_index + 1}) && ... isnumeric (varargin{pair_index + 1})))) error ("evalclusters: invalid distance metric"); endif distance = varargin{pair_index + 1}; case "clusterpriors" ## used by silhouette evaluation if (! strcmpi (criterion, "silhouette")) error (["evalclusters: cluster prior probabilities cannot be used "... "with '%s' criterion"], criterion); endif if (any (strcmpi (varargin{pair_index + 1}, {"empirical", "equal"}))) clusterpriors = lower (varargin{pair_index + 1}); else error ("evalclusters: invalid cluster prior probabilities value"); endif case "b" ## used by gap evaluation if (! isnumeric (varargin{pair_index + 1}) || ... ! isscalar (varargin{pair_index + 1}) || ... varargin{pair_index + 1} != floor (varargin{pair_index + 1}) || ... varargin{pair_index + 1} < 1) error ("evalclusters: b must a be positive integer number"); endif b = varargin{pair_index + 1}; case "referencedistribution" ## used by gap evaluation if (! ischar (varargin{pair_index + 1}) || ! any (strcmpi ... (varargin{pair_index + 1}, {"pca", "uniform"}))) error (["evalclusters: the reference distribution must be either" ... "'PCA' or 'uniform'"]); endif referencedistribution = lower (varargin{pair_index + 1}); case "searchmethod" ## used by gap evaluation if (! ischar (varargin{pair_index + 1}) || any (strcmpi ... (varargin{pair_index + 1}, {"globalmaxse", "uniform"}))) error (["evalclusters: the search method must be either" ... "'globalMaxSE' or 'firstmaxse'"]); endif searchmethod = lower (varargin{pair_index + 1}); otherwise error ("evalclusters: unknown property %s", varargin{pair_index}); endswitch pair_index += 2; endwhile ## check if there are parameters without a value or a name left if (nargin - 2 - pair_index) if (ischar (varargin{pair_index})) error ("evalclusters: invalid parameter '%s'", varargin{pair_index}); else error ("evalclusters: invalid parameter '%d'", varargin{pair_index}); endif endif ## another check on klist if (isempty (klist) && (ischar (clust) || isa (clust, "function_handle"))) error (["evalclusters: 'KList' can be empty only when 'clust' ", ... "is a matrix"]); endif ## main switch (lower (criterion)) case "calinskiharabasz" ## further compatibility checks between the chosen parameters are ## delegated to the class constructor if (isempty (klist)) klist = 1 : columns (clust); endif cc = CalinskiHarabaszEvaluation (x, clust, klist); case "daviesbouldin" ## further compatibility checks between the chosen parameters are ## delegated to the class constructor if (isempty (klist)) klist = 1 : columns (clust); endif cc = DaviesBouldinEvaluation (x, clust, klist); case "silhouette" ## further compatibility checks between the chosen parameters are ## delegated to the class constructor if (isempty (klist)) klist = 1 : columns (clust); endif cc = SilhouetteEvaluation (x, clust, klist, distance, clusterpriors); case "gap" ## gap cannot be used with a pre-computed solution, i.e. a matrix for ## 'clust', and klist must be specified if (isnumeric (clust)) error (["evalclusters: 'clust' must be a clustering algorithm when "... "using the gap criterion"]); endif if (isempty (klist)) error (["evalclusters: 'klist' cannot be empty when using the gap " ... "criterion"]); endif cc = GapEvaluation (x, clust, klist, b, distance, ... referencedistribution, searchmethod); otherwise error ("evalclusters: invalid criterion '%s'", criterion); endswitch endfunction ## Demo code %!demo %! load fisheriris; %! eva = evalclusters (meas, "kmeans", "calinskiharabasz", "KList", [1:6]) %! plot (eva) ## input tests %!error evalclusters () %!error evalclusters ([1 1;0 1]) %!error evalclusters ([1 1;0 1], "kmeans") %!error <'x' must be a numeric*> evalclusters ("abc", "kmeans", "gap") %!error evalclusters ([1 1;0 1], "xxx", "gap") %!error evalclusters ([1 1;0 1], [1 2], "gap") %!error evalclusters ([1 1;0 1], 1.2, "gap") %!error evalclusters ([1 1;0 1], [1; 2], 123) %!error evalclusters ([1 1;0 1], [1; 2], "xxx") %!error <'KList' can be empty*> evalclusters ([1 1;0 1], "kmeans", "gap") %!error evalclusters ([1 1;0 1], [1; 2], "gap", 1) %!error evalclusters ([1 1;0 1], [1; 2], "gap", 1, 1) %!error evalclusters ([1 1;0 1], [1; 2], "gap", "xxx", 1) %!error <'KList'*> evalclusters ([1 1;0 1], [1; 2], "gap", "KList", [-1 0]) %!error <'KList'*> evalclusters ([1 1;0 1], [1; 2], "gap", "KList", [1 .5]) %!error <'KList'*> evalclusters ([1 1;0 1], [1; 2], "gap", "KList", [1 1; 1 1]) %!error evalclusters ([1 1;0 1], [1; 2], "gap", ... %! "distance", "a") %!error evalclusters ([1 1;0 1], [1; 2], "daviesbouldin", ... %! "distance", "a") %!error evalclusters ([1 1;0 1], [1; 2], "gap", ... %! "clusterpriors", "equal") %!error evalclusters ([1 1;0 1], [1; 2], ... %! "silhouette", "clusterpriors", "xxx") %!error <'clust' must be a clustering*> evalclusters ([1 1;0 1], [1; 2], "gap") %!test %! load fisheriris; %! eva = evalclusters (meas, "kmeans", "calinskiharabasz", "KList", [1:6]); %! assert (isa (eva, "CalinskiHarabaszEvaluation")); %! assert (eva.NumObservations, 150); %! assert (eva.OptimalK, 3); %! assert (eva.InspectedK, [1 2 3 4 5 6]); statistics-release-1.7.3/inst/ff2n.m000066400000000000000000000037051475240274700173410ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## based on public domain work by Paul Kienzle ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{dFF2} =} ff2n (@var{n}) ## ## Two-level full factorial design. ## ## @code{@var{dFF2} = ff2n (@var{n})} gives factor settings dFF2 for a two-level ## full factorial design with n factors. @var{dFF2} is m-by-n, where m is the ## number of treatments in the full-factorial design. Each row of @var{dFF2} ## corresponds to a single treatment. Each column contains the settings for a ## single factor, with values of 0 and 1 for the two levels. ## ## @seealso {fullfact} ## @end deftypefn function A = ff2n (n) if (nargin != 1) error ("ff2n: wrong number of input arguments."); endif if (floor (n) != n || numel (n) != 1 || n < 1 ... || ! isfinite (n) || ! isreal (n)) error ("ff2n: @var{N} must be a positive integer scalar."); endif A = fullfact (2 * ones (1, n)) - 1; endfunction %!error ff2n (); %!error ff2n (2, 5); %!error ff2n (2.5); %!error ff2n (0); %!error ff2n (-3); %!error ff2n (3+2i); %!error ff2n (Inf); %!error ff2n (NaN); %!test %! A = ff2n (3); %! assert (A, fullfact (3)); %!test %! A = ff2n (8); %! assert (A, fullfact (8)); statistics-release-1.7.3/inst/fillmissing.m000066400000000000000000003404371475240274700210340ustar00rootroot00000000000000## Copyright (C) 1995-2023 The Octave Project Developers ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{B} =} fillmissing (@var{A}, "constant", @var{v}) ## @deftypefnx {statistics} {@var{B} =} fillmissing (@var{A}, @var{method}) ## @deftypefnx {statistics} {@var{B} =} fillmissing (@var{A}, @var{move_method}, @var{window_size}) ## @deftypefnx {statistics} {@var{B} =} fillmissing (@var{A}, @var{fill_function}, @var{window_size}) ## @deftypefnx {statistics} {@var{B} =} fillmissing (@dots{}, @var{dim}) ## @deftypefnx {statistics} {@var{B} =} fillmissing (@dots{}, @var{PropertyName}, @var{PropertyValue}) ## @deftypefnx {statistics} {[@var{B}, @var{idx}] =} fillmissing (@dots{}) ## ## Replace missing entries of array @var{A} either with values in @var{v} or ## as determined by other specified methods. 'missing' values are determined ## by the data type of @var{A} as identified by the function @ref{ismissing}, ## curently defined as: ## ## @itemize ## @item ## @qcode{NaN}: @code{single}, @code{double} ## ## @item ## @qcode{" "} (white space): @code{char} ## ## @item ## @qcode{@{""@}} (white space in cell): string cells. ## @end itemize ## ## @var{A} can be a numeric scalar or array, a character vector or array, or ## a cell array of character vectors (a.k.a. string cells). ## ## @var{v} can be a scalar or an array containing values for replacing the ## missing values in @var{A} with a compatible data type for isertion into ## @var{A}. The shape of @var{v} must be a scalar or an array with number ## of elements in @var{v} equal to the number of elements orthoganal to the ## operating dimension. E.g., if @code{size(@var{A})} = [3 5 4], operating ## along @code{dim} = 2 requires @var{v} to contain either 1 or 3x4=12 ## elements. ## ## If requested, the optional output @var{idx} will contain a logical array ## the same shape as @var{A} indicating with 1's which locations in @var{A} ## were filled. ## ## Alternate Input Arguments and Values: ## @itemize ## @item @var{method} - replace missing values with: ## @table @code ## ## @item next ## @itemx previous ## @itemx nearest ## next, previous, or nearest non-missing value (nearest defaults to next ## when equidistant as determined by @code{SamplePoints}.) ## ## @item linear ## linear interpolation of neigboring, non-missing values ## ## @item spline ## piecewise cubic spline interpolation of neigboring, non-missing values ## ## @item pchip ## 'shape preserving' piecewise cubic spline interposaliton of neighboring, ## non-missing values ## @end table ## ## @item @var{move_method} - moving window calculated replacement values: ## @table @code ## ## @item movmean ## @itemx movmedian ## moving average or median using a window determined by @var{window_size}. ## @var{window_size} must be either a positive scalar value or a two element ## positive vector of sizes @w{@code{[@var{nb}, @var{na}]}} measured in the ## same units as @code{SamplePoints}. For scalar values, the window is ## centered on the missing element and includes all data points within a ## distance of half of @var{window_size} on either side of the window center ## point. Note that for compatability, when using a scalar value, the backward ## window limit is inclusive and the forward limit is exclusive. If a ## two-element @var{window_size} vector is specified, the window includes all ## points within a distance of @var{nb} backward and @var{na} forward from the ## current element at the window center (both limits inclusive). ## @end table ## ## @item @var{fill_function} - custom method specified as a function handle. ## The supplied fill function must accept three inputs in the following order ## for each missing gap in the data: ## @table @var ## @item A_values - ## elements of @var{A} within the window on either side of the gap as ## determined by @var{window_size}. (Note these elements can include missing ## values from other nearby gaps.) ## @item A_locs - ## locations of the reference data, @var{A_values}, in terms of the default ## or specified @code{SamplePoints}. ## @item gap_locs - ## location of the gap data points that need to be filled in terms of the ## default or specified @code{SamplePoints}. ## @end table ## ## The supplied function must return a scalar or vector with the same number of ## elements in @var{gap_locs}. The required @var{window_size} parameter ## follows similar rules as for the moving average and median methods ## described above, with the two exceptions that (1) each gap is processed as a ## single element, rather than gap elements being processed individually, and ## (2) the window extended on either side of the gap has inclusive endpoints ## regardless of how @var{window_size} is specified. ## ## @item @var{dim} - specify a dimension for vector operation (default = ## first non-singeton dimension) ## ## @item @var{PropertyName}-@var{PropertyValue} pairs ## @table @code ## @item SamplePoints ## @var{PropertyValue} is a vector of sample point values representing the ## sorted and unique x-axis values of the data in @var{A}. If unspecified, ## the default is assumed to be the vector @var{[1 : size (A, dim)]}. The ## values in @code{SamplePoints} will affect methods and properties that rely ## on the effective distance between data points in @var{A}, such as ## interpolants and moving window functions where the @var{window_size} ## specified for moving window functions is measured relative to the ## @code{SamplePoints}. ## ## @item EndValues ## Apply a separate handling method for missing values at the front or back of ## the array. @var{PropertyValue} can be: ## @itemize ## @item A constant scalar or array with the same shape requirments as @var{v}. ## @item @code{none} - Do not fill end gap values. ## @item @code{extrap} - Use the same procedure as @var{method} to fill the ## end gap values. ## @item Any valid @var{method} listed above except for @code{movmean}, ## @code{movmedian}, and @code{fill_function}. Those methods can only be ## applied to end gap values with @code{extrap}. ## @end itemize ## ## @item MissingLocations ## @var{PropertyValue} must be a logical array the same size as @var{A} ## indicating locations of known missing data with a value of @code{true}. ## (cannot be combined with MaxGap) ## ## @item MaxGap ## @var{PropertyValue} is a numeric scalar indicating the maximum gap length ## to fill, and assumes the same distance scale as the sample points. Gap ## length is calculated by the difference in locations of the sample points ## on either side of the gap, and gaps larger than MaxGap are ignored by ## @var{fillmissing}. (cannot be combined with MissingLocations) ## @end table ## @end itemize ## ## Compatibility Notes: ## @itemize ## @item ## Numerical and logical inputs for @var{A} and @var{v} may be specified ## in any combination. The output will be the same class as @var{A}, with the ## @var{v} converted to that data type for filling. Only @code{single} and ## @code{double} have defined 'missing' values, so except for when the ## @code{missinglocations} option specifies the missing value identification of ## logical and other numeric data types, the output will always be ## @code{@var{B} = @var{A}} with @code{@var{idx} = false(size(@var{A}))}. ## @item ## All interpolation methods can be individually applied to @code{EndValues}. ## @item ## @sc{Matlab}'s @var{fill_function} method currently has several ## inconsistencies with the other methods (tested against version 2022a), and ## Octave's implementation has chosen the following consistent behavior over ## compatibility: (1) a column full of missing data is considered part of ## @code{EndValues}, (2) such columns are then excluded from ## @var{fill_function} processing because the moving window is always empty. ## (3) operation in dimensions higher than 2 perform identically to operations ## in dims 1 and 2, most notable on vectors. ## @item ## Method "makima" is not yet implemented in @code{interp1}, which is ## used by @code{fillmissing}. Attempting to call this method will produce ## an error until the method is implemented in @code{interp1}. ## @end itemize ## ## @seealso{ismissing, rmmissing, standardizeMissing} ## @end deftypefn function [A, idx_out] = fillmissing (A, varargin) if ((nargin < 2)|| (nargin > 12)) print_usage (); endif method = varargin{1}; if (ischar (method)) method = lower (method); elseif (! is_function_handle (method)) error ("fillmissing: second input must be a string or function handle."); endif sz_A = size (A); ndims_A = numel (sz_A); dim = []; missing_locs = []; endgap_method = []; endgap_locs = []; endgap_val = []; fill_vals = []; idx_flag = (nargout > 1); maxgap = []; missinglocations = false; reshape_flag = false; samplepoints = []; standard_samplepoints = true; v = []; if (idx_flag) idx_out = false (sz_A); endif ## Process input arguments. if (is_function_handle (method)) ## Verify function handle and window. if ((nargin < 3) || ! isnumeric (varargin{2}) || ... ! any (numel (varargin{2}) == [1, 2])) error (["fillmissing: fill function handle must be followed by ", ... "a numeric scalar or two-element vector window size."]); elseif (nargin (method) < 3) error ("fillmissing: fill function must accept at least three inputs."); endif move_fcn = method; method = "movfcn"; window_size = varargin{2}; next_varg = 3; else switch (method) case {"previous", "next", "nearest"} next_varg = 2; case {"linear", "spline", "pchip", "makima"} next_varg = 2; if (! (isnumeric (A) || islogical (A))) error (["fillmissing: interpolation methods only valid for ", ... "numeric input types."]); endif case "constant" if ((nargin < 3)) error (["fillmissing: 'constant' method must be followed by a ", ... "numeric scalar or array."]); endif v = varargin{2}; if (! (isscalar (v) || isempty (v))) v = v(:); endif if ((! ischar(v)) && isempty (v)) error ("fillmissing: a numeric fill value cannot be emtpy."); endif ## Type check v against A. if (iscellstr (A) && ischar (v) && ! iscellstr (v)) v = {v}; endif if ((! isempty (v)) && ... ((isnumeric (A) && ! (isnumeric (v) || islogical (v))) || ... (ischar (A) && ! ischar (v)) || ... (iscellstr (A) && ! (iscellstr (v))))) error ("fillmissing: fill value must be the same data type as 'A'."); endif ## v can't be size checked until after processing rest of inputs. next_varg = 3; case {"movmean", "movmedian"} if (! (isnumeric (A) || islogical (A))) error (["fillmissing: 'movmean' and 'movmedian' methods only ", ... "valid for numeric input types."]); endif if ((nargin < 3) || ! isnumeric (varargin{2}) || ... ! any (numel (varargin{2}) == [1, 2])) error (["fillmissing: moving window method must be followed by ", ... "a numeric scalar or two-element vector."]); endif window_size = varargin{2}; next_varg = 3; otherwise error ("fillmissing: unknown fill method '%s'.", method); endswitch endif ## Process any more parameters. if (next_varg < nargin) ## Set dim. If specified, it is the only numeric option allowed next. if (isnumeric (varargin{next_varg})) dim = varargin{next_varg}; if (! (isscalar (dim) && (dim > 0))) error ("fillmissing: DIM must be a positive scalar."); endif next_varg++; else ## Default dim is first nonsingleton dimension of A. if (isscalar (A)) dim = 1; else dim = find (sz_A > 1, 1, "first"); endif endif sz_A_dim = size (A, dim); ## Process any remaining inputs, must be name-value pairs. while (next_varg < nargin) propname = varargin{next_varg}; if (next_varg + 1 == nargin) ## Must be at least one more input with 1st containing value. error ("fillmissing: properties must be given as name-value pairs."); else propval = varargin{next_varg + 1}; next_varg = next_varg + 2; if (! ischar (propname)) error ("fillmissing: invalid parameter name specified."); else propname = lower (propname); endif ## Input validation for names and values. switch (propname) case "samplepoints" ## val must be sorted, unique, numeric vector the same size ## as size(A,dim). if (! (isnumeric (propval) && isvector (propval) && (numel (propval) == sz_A_dim) && issorted (propval) && (numel (propval) == numel (unique (propval))))) error (["fillmissing: SamplePoints must be a sorted ", ... "non-repeating, numeric vector with %d elements."], ... sz_A_dim); endif samplepoints = propval(:); standard_samplepoints = all (diff (samplepoints, 1, 1) == 1); case "endvalues" ## For numeric A, val must be numeric scalar, a numeric ## array with numel equal to the elements orthogonal to ## the dim or certain string methads. For non-numeric A, ## "constant" method is not valid. if (ischar (propval)) switch (lower (propval)) case {"extrap", "previous", "next", "nearest", "none", ... "linear", "spline", "pchip", "makima"} endgap_method = propval; otherwise error ("fillmissing: invalid EndValues method '%s'.", propval); endswitch elseif (isnumeric (propval)) if (! (isnumeric (A) || islogical (A))) error (["fillmissing: EndValues method 'constant' only ", ... "valid for numeric arrays."]); endif endgap_method = 'constant'; endgap_val = propval; else error (["fillmissing: EndValues must be numeric or a ", ... "valid method name."]); endif case "missinglocations" if (! (isnumeric (A) || islogical (A) || isinteger (A) || ... ischar (A) || iscellstr (A))) error (["fillmissing: MissingLocations option is not ", ... "compatible with data type '%s'."], class (A)); endif if (! isempty (maxgap)) error (["fillmissing: MissingLocations and MaxGap options ", ... "cannot be used simultaneously."]); endif ## val must be logical array same size as A. if (! (islogical (propval) && isequal (sz_A, size (propval)))) error (["fillmissing: MissingLocations must be a logical ", ... "array the same size as A."]); endif missinglocations = true; missing_locs = propval; case "maxgap" ## val must be positive numeric scalar. if (! (isnumeric (propval) && isscalar (propval) && (propval > 0))) error ("fillmissing: MaxGap must be a positive numeric scalar."); endif if (! isempty (missing_locs)) error (["fillmissing: MissingLocations and MaxGap options ", ... "cannot be used simultaneously."]); endif maxgap = propval; case {"replacevalues", "datavariables"} error ("fillmissing: the '%s' option has not been implemented.", ... propname); otherwise error ("invalid parameter name '%s'.", propname); endswitch endif endwhile else ## No inputs after method. ## Set default dim. if (isscalar (A)) dim = 1; else dim = find (sz_A > 1, 1, "first"); endif sz_A_dim = size (A, dim); endif ## Reduce calls to size and avoid overruns checking sz_A for high dims. if (dim > ndims_A) sz_A = [sz_A, ones(1, dim - ndims_A)]; ndims_A = numel (sz_A); endif ## Set defaults for any unspecified parameters. if (isempty (samplepoints)) samplepoints = [1 : sz_A_dim]'; endif if (isempty (missing_locs)) missing_locs = ismissing (A); endif ## endvalues treated separately from interior missing_locs. if (isempty (endgap_method) || strcmp (endgap_method, "extrap")) endgap_method = method; if (strcmp(endgap_method, "constant")) endgap_val = v; endif endif ## missingvalues option not compatible with some methods and inputs: if (isinteger (A) || islogical (A)) if (any (ismember (method, ... {"linear", "spline", "pchip", "makima", "movmean", "movmedian"}))) error (["fillmissing: MissingLocations cannot be used ", ... "with method '%s' and inputs of type '%s'."], method, class (A)); elseif (any (ismember (endgap_method, ... {"linear", "spline", "pchip", "makima"}))) error (["fillmissing: MissingLocations cannot be used with EndValues", ... " method '%s' and inputs of type '%s'."], method, class (A)); endif endif ## Verify size of v and endgap_val for 'constant' methods, resize for A. orthogonal_size = [sz_A(1:dim-1), 1, sz_A(dim+1:end)]; # orthog. to dim size. numel_orthogonal = prod (orthogonal_size); # numel perpen. to dim. if (strcmp (method, "constant") && (! isscalar (v))) if (numel (v) != numel_orthogonal) error (["fillmissing: fill value 'V' must be a scalar or a %d ", ... " element array."], numel_orthogonal); else v = reshape (v, orthogonal_size); endif endif if (strcmp (endgap_method, "constant") && (! isscalar (endgap_val))) if (numel (endgap_val) != numel_orthogonal) error (["fillmissing: EndValues must be a scalar or a %d element ", ... "array."], numel_orthogonal); else endgap_val = reshape (endgap_val, orthogonal_size); endif endif ## Simplify processing by temporarily permuting A so operation always on dim1. ## Revert permutation at the end. dim_idx_perm = [1 : ndims_A]; dim_idx_flip(1 : max(dim, ndims_A)) = {":"}; dim_idx_flip(1) = [sz_A_dim:-1:1]; if (dim != 1) dim_idx_perm([1, dim]) = [dim, 1]; A = permute (A, dim_idx_perm); sz_A([1, dim]) = sz_A([dim, 1]); missing_locs = permute (missing_locs, dim_idx_perm); reshape_flag = true; orthogonal_size = [1, sz_A(2:end)]; if (idx_flag) idx_out = false (sz_A); endif if (! isempty (v) && ! isscalar (v)) v = permute (v, dim_idx_perm); endif if (! isempty (endgap_val) && ! isscalar (endgap_val)) endgap_val = permute (endgap_val, dim_idx_perm); endif endif ## Precalculate fill data for several methods. zero_padding = zeros (orthogonal_size); samplepoints_expand = samplepoints(:, ones (1, prod (sz_A(2:end)))); ## Find endgap locations. if (sz_A_dim < 3) ## All missing are endgaps. endgap_locs = missing_locs; else ## Use cumsums starting from first and last part in dim to find missing ## values in and adjacent to end locations. endgap_locs = cumprod (missing_locs,1) | ... (cumprod (missing_locs(dim_idx_flip{:}),1))(dim_idx_flip{:}); endif ## Remove endgap_locs from missing_locs to avoid double processing. missing_locs(endgap_locs) = false; ## Remove elements from missing and end location arrays if maxgap is specified. if (! isempty (maxgap)) ## missing_locs: If samplepoints value diff on either side of missing ## elements is > maxgap, remove those values. ## For endgaps, use diff of inside and missing end samplepoint values ## and remove from endgaps. ## First check gapsize of any interior missings in missing_locs. if (any (missing_locs(:))) ## Locations in front of gaps loc_before = [diff(missing_locs,1,1); zero_padding] == 1; ## Locations in back of gaps loc_after = diff ([zero_padding; missing_locs],1,1) == -1; ## Value of samplepoints at front and back locs sampvals_before = samplepoints_expand(loc_before); sampvals_after = samplepoints_expand(loc_after); ## Evaluate which gaps are too big to fill. gaps_to_remove = (sampvals_after - sampvals_before) > maxgap; ## Convert those gaps into an array element list. idxs_to_remove = arrayfun ("colon", ... ((find (loc_before))(gaps_to_remove ) + 1), ... ((find (loc_after))(gaps_to_remove ) - 1), ... "UniformOutput", false); ## Remove those elements from missing_locs. missing_locs([idxs_to_remove{:}]) = false; endif ## Then do any endgaps. if (any (endgap_locs(:))) ## If any are all missing, remove for any value of maxgap. endgap_locs &= ! prod (endgap_locs, 1); if ((sz_A_dim < 3) && (abs (samplepoints(2) - samplepoints(1)) > maxgap)) ## Shortcut - all missings are ends and exceed maxgap. endgap_locs(:) = false; else ## Check gap size of front endgaps. ## Find loc element after gap. nextvals = sum (cumprod (endgap_locs,1)) + 1; ## Compare diff between values at those points and at base with maxgap. ends_to_remove = abs (samplepoints(nextvals) - samplepoints(1)) ... > maxgap; ## Remove any with gap>maxgap. endgap_locs((cumprod (endgap_locs,1)) & ends_to_remove) = false; ## Flip, repeat for back endgaps, then unflip and remove. nextvals = sum (cumprod (endgap_locs(dim_idx_flip{:}),1)) + 1; ends_to_remove = abs (samplepoints(end:-1:1)(nextvals) ... - samplepoints(end)) > maxgap; endgap_locs((cumprod (... endgap_locs(dim_idx_flip{:}), 1)(dim_idx_flip{:})) & ... ends_to_remove) = false; endif endif endif if (any (strcmp (endgap_method, {"movmean", "movmedian", "movfcn"}))) ## These methods only called for endgaps with "extrap", so endgaps ## are processed together in the missing_locs section. missing_locs = missing_locs | endgap_locs; endgap_locs(:) = false; endif ## Actaully fill the missing data. ## Process central missing values (all gaps bound by two valid datapoints). ## For each method, calcualte fill_vals, which will be used in assignment ## A(missing_locs) = fill_vals, and if idx_flag, populate idx_out. if (any (missing_locs(:))) switch (method) case "constant" if (isscalar (v)) fill_vals = v; else fill_vals = (missing_locs .* v)(missing_locs); endif if (idx_flag) ## If any v are the missing type, those get removed from idx_out ## unless using 'missinglocations'. if ((! missinglocations) && any (miss_v = ismissing (v))) idx_out(missing_locs) = true; idx_out(missing_locs & miss_v) = false; else idx_out(missing_locs) = true; endif endif case {"previous", "next", "nearest", "linear"} ## Find element locations bounding each gap. loc_before = [diff(missing_locs, 1, 1); zero_padding] == 1; loc_after = diff ([zero_padding; missing_locs], 1, 1) == -1; gapsizes = find (loc_after) - find (loc_before) - 1; gap_count_idx = [1 : numel(gapsizes); gapsizes']; switch (method) case "previous" fill_vals = repelems (A(loc_before), gap_count_idx)'; case "next" fill_vals = repelems (A(loc_after), gap_count_idx)'; case {"nearest", "linear"} ## Determine which missings go with values before or after ## gap based on samplevalue distance. (Equal dist goes to after.) ## Find sample values before and after gaps. sampvals_before = samplepoints_expand(loc_before); sampvals_after = samplepoints_expand(loc_after); ## Build cell with linear indices of elements in each gap. gap_locations = arrayfun ("colon", (find (loc_before)) + 1, ... (find (loc_after)) - 1, "UniformOutput", false); ## Get sample values at those elements. [sampvals_in_gaps, ~] = ind2sub (sz_A, [gap_locations{:}]); sampvals_in_gaps = samplepoints(sampvals_in_gaps); ## Expand first and last vectors for each gap point. Avals_before = repelems (A(loc_before), gap_count_idx)'; Avals_after = repelems (A(loc_after), gap_count_idx)'; switch (method) case "nearest" ## Calculate gap mid point for each gap element. sampvals_midgap = repelems ( ... (sampvals_before + sampvals_after)/2, gap_count_idx)'; ## Generate fill vectors sorting elements into nearest before ## or after. prev_fill = (sampvals_in_gaps < sampvals_midgap); next_fill = (sampvals_in_gaps >= sampvals_midgap); fill_vals = A(missing_locs); fill_vals(prev_fill) = Avals_before(prev_fill); fill_vals(next_fill) = Avals_after(next_fill); case "linear" ## Expand samplepoint values for interpolation x-values. sampvals_before = repelems (sampvals_before, gap_count_idx)'; sampvals_after = repelems (sampvals_after, gap_count_idx)'; ## Linearly interpolate: fill_vals = ((Avals_after - Avals_before) ... ./ (sampvals_after - sampvals_before)) ... .* (sampvals_in_gaps - sampvals_before) ... + Avals_before; endswitch endswitch if (idx_flag) ## Mid gaps will always be filled by above methods. idx_out(missing_locs) = true; endif case {"spline", "pchip", "makima"} ## Pass more complex interpolations to interp1. ## FIXME: vectorized 'linear' is ~10-100x faster than using interp1. ## Look to speed these up as well. ## Identify columns needing interpolation to reduce empty operations. cols_to_use = any (missing_locs, 1); ## missinglocations may send columns with NaN and less than 2 ## real values resulting in interp1 error. Trim those columns, ## pre-populate fill_vals with NaN, mark as filled. if (missinglocations) fill_vals = NaN (sum (missing_locs(:, cols_to_use)(:)), 1); cols_enough_points = (sum ( ... !isnan(A) & (! missing_locs), 1) > 1) & cols_to_use; interp_cols = (cols_enough_points & cols_to_use); interp_vals = (missing_locs & cols_enough_points)(missing_locs & ... cols_to_use); fill_vals(interp_vals) = other_interpolants (A(:, interp_cols), missing_locs(:, interp_cols), endgap_locs(:, interp_cols), ... method, samplepoints); else fill_vals = other_interpolants (A(:, cols_to_use), missing_locs(:, cols_to_use), endgap_locs(:, cols_to_use), ... method, samplepoints); endif if (idx_flag) idx_out(missing_locs) = true; endif case {"movmean", "movmedian"} ## Check window size versus smallest sample gaps. If window smaller, ## nothing to do, break out early. if ((isscalar (window_size) && ... (window_size/2 >= min (diff (samplepoints)))) || ... (isvector (window_size) && (sum (window_size) >= min (diff (samplepoints))))) switch (method) case "movmean" if (sz_A_dim > 1) allmissing = (missing_locs | endgap_locs)(:,:); ## Create temporary flattened array for processing, A_sum = A(:,:); A_sum (allmissing) = 0; if (standard_samplepoints && ... all (round (window_size) == window_size)) ## Window size based on vector elements. ## Faster codepath for uniform, unit-spacing samplepoints ## and integer valued window sizes. if (isscalar (window_size)) window_width = window_size; if (mod (window_size, 2)) ## Odd window size: ## Equal number of values on either side of gap. window_size = (window_width - 1) .* [0.5, 0.5]; else ## Even window size: ## One extra element on previous side of gap. window_size(1) = window_width/2; window_size(2) = window_size(1) - 1; endif else window_width = window_size(1) + window_size(2) + 1; endif ## Use columnwise convolution of windowing vector and A for ## vectorized summation. conv_vector = ones (window_width, 1); A_sum = convn (A_sum, conv_vector, ... "full")(1 + window_size(2):end - window_size(1), :); ## Get count of values contributing to convolution to account ## for missing elements and to calculate mean. A_sum_count = convn (! allmissing, conv_vector, ... "full")(1 + window_size(2):end - window_size(1), :); else ## Window size based on sample point distance. Works for non ## integer, non uniform values. ## Use A_sum (flattened to 2D), project slice windows in dim3 ## automatic broadcasting to get window summations & counts samplepoints_shift = ... samplepoints(:, ones(1, sz_A_dim)) - samplepoints'; if (isscalar (window_size)) ## [nb, na) window_size = window_size * [-0.5, 0.5]; samplepoints_slice_windows = permute (... samplepoints_shift >= window_size(1) & ... samplepoints_shift < window_size(2), [1,3,2]); else ## [nb, na] window_size(1) = -window_size(1); samplepoints_slice_windows = permute (... samplepoints_shift >= window_size(1) & ... samplepoints_shift <= window_size(2), [1,3,2]); endif if (missinglocations) ## NaNs left in A_sum will cause all sums to produce NaN ## FIXME: when sum can handle nanflag, the 'else' path ## should be able to be made to handle the vectorized ## summation even with 'missinglocations'. A_nan = isnan (A_sum); A_temp = A_sum .* samplepoints_slice_windows; A_temp(! samplepoints_slice_windows & A_nan) = 0; A_sum = permute (sum (A_temp, 1), [3,2,1]); else A_sum = permute (... sum (A_sum .* samplepoints_slice_windows, 1), [3,2,1]); endif A_sum_count = permute (... sum (! allmissing & samplepoints_slice_windows, 1), ... [3,2,1]); endif ## Build fill values. fill_vals = A(missing_locs); # Prefill to include missing vals. fillable_gaps = missing_locs(:,:) & A_sum_count; fill_vals(fillable_gaps(missing_locs(:,:))) = ... A_sum(fillable_gaps) ./ A_sum_count(fillable_gaps); endif case "movmedian" if (sz_A_dim > 1) if (missinglocations) ## Median assumes empty locs have NaN. Missinglocations ## may point to a non-NaN number that will be assumed valid. ## Replace with NaNs. A(missing_locs) = NaN; endif cols_to_use = any (missing_locs(:,:), 1); samplepoints_shift = ... samplepoints(:, ones(1, sz_A_dim)) - samplepoints'; if (isscalar (window_size)) window_size = window_size * [-0.5, 0.5]; ## [nb, na) samplepoints_slice_windows = permute (... samplepoints_shift >= window_size(1) & ... samplepoints_shift < window_size(2), [1,3,2]); else window_size(1) = -window_size(1); ## [nb, na] samplepoints_slice_windows = permute (... samplepoints_shift >= window_size(1) & ... samplepoints_shift <= window_size(2), [1,3,2]); endif ## Use moving window slices to project A and use ## custom function for vectorized full array median computation. A_med = A(:, cols_to_use); nan_slice_windows = double (samplepoints_slice_windows); nan_slice_windows(! samplepoints_slice_windows) = NaN; A_med_slices = A_med .* nan_slice_windows; A_med = permute (columnwise_median (A_med_slices), [3 2 1]); fillable_gaps = missing_locs(:, cols_to_use); fill_vals = A_med(fillable_gaps); endif endswitch if (idx_flag) ## Matlab compatiblity - NaNs filled back in by movmean and ## movmedian should _not_ show as filled. idx_out(fillable_gaps) = true; still_nan = missing_locs; still_nan(missing_locs) = isnan (fill_vals); idx_out(still_nan) = false; endif endif case "movfcn" ## For each gap construct: ## xval - data values in window on either side of gap, including ## other missing values ## xloc - sample point values for those xval ## gap_loc - sample point values for gap elements ## If window has xval fully empty skip processing gap. ## missing_locs might include endgap_locs. ## Need to build gap locations accounting for both types. ## Missing values can include more than just numeric inputs. ## Windows containing no data points (e.g., endgaps when window ## is one sided [3 0] or [0 2], will be dropped from processing, ## not being passed to the mov_fcn. if (isscalar (window_size)) window_size = window_size * [-0.5, 0.5]; else window_size(1) = -window_size(1); endif ## Midgap bounds loc_before = [diff(missing_locs, 1, 1); zero_padding] == 1; loc_after = diff ([zero_padding; missing_locs], 1, 1) == -1; ## Front/back endgap locations and bounds front_gap_locs = logical (cumprod (missing_locs, 1)); front_next_locs = diff ([zero_padding; front_gap_locs], 1, 1) == -1; back_gap_locs = logical ( ... cumprod (missing_locs(dim_idx_flip{:}), 1)(dim_idx_flip{:})); back_prev_locs = [diff(back_gap_locs, 1, 1); zero_padding] == 1; ## Remove gap double counting. back_gap_locs &= ! front_gap_locs; loc_before &= ! back_prev_locs; loc_after &= ! front_next_locs; ## Build gap location array using gap starts and lengths. ## Simplest to use front / mid / back ordering, track later with sort. gap_start_locs = ... [find(front_gap_locs & [true; false(sz_A_dim-1,1)])(:); ... find(circshift (loc_before, 1, 1))(:); find(circshift (back_prev_locs, 1, 1))(:)]; gapsizes = [(sum (front_gap_locs, 1))(any (front_gap_locs, 1))(:);... find(loc_after) - find(loc_before) - 1;... (sum (back_gap_locs, 1))(any (back_gap_locs, 1))(:)]; ## Separate arrayfun/cellfun faster than single fun with ## composite anonymous function. gap_locations = arrayfun ("colon", gap_start_locs, ... gap_start_locs + gapsizes - 1, "UniformOutput", false); gap_locations = cellfun("transpose", ... gap_locations, "UniformOutput", false); ## Sorting index to bridge front-mid-back and linear index ordering. [~, gap_full_sort_idx] = sort (vertcat (gap_locations{:})); ## Remove front or back gaps from gapsizes & gap_locations. ## If front/back window size = 0, or if full column is missing. ## Index to track empty/removed elements. removed_element_idx = true (numel (gap_full_sort_idx), 1); removed_front_elements = 0; removed_back_elements = 0; ## Simple front/back gap trimming for either window size = 0. if (! window_size(2)) ## If no back facing window, ignore front gaps. removed_front_gap_count = sum (front_gap_locs(1,:)); removed_front_elements = sum (gapsizes(1 : removed_front_gap_count)); removed_element_idx(1 : removed_front_elements) = false; gap_locations(1 : removed_front_gap_count ) = []; gapsizes(1 : removed_front_gap_count ) = []; elseif (any (missing_col_gaps = (gapsizes == sz_A_dim))) missing_col_elements = ... repelems (missing_col_gaps, [1:numel(gapsizes); gapsizes'])'; removed_element_idx(missing_col_elements) = false; gap_locations(missing_col_gaps) = []; gapsizes(missing_col_gaps) = []; endif if (! window_size(1)) ## If no front facing window, ignore back gaps. removed_back_gap_count = sum (back_gap_locs(end,:)); removed_back_elements = sum (... gapsizes(end - removed_back_gap_count + 1 : end)); removed_element_idx(end - removed_back_elements + 1 : end) = false; gap_locations(end - removed_back_gap_count + 1 : end) = []; gapsizes(end - removed_back_gap_count + 1 : end) = []; endif if (! isempty (gapsizes)) gap_sample_values = cellfun_subsref (gap_locations, false, ... {samplepoints_expand}); ## Build [row,column] locations array for windows around each gap. window_points_r_c = cell (numel (gapsizes), 2); window_points_r_c(:,1) = cellfun (@(x) ... ([1:sz_A_dim]')((samplepoints= ... max (x(1) + window_size(1), samplepoints(1))) | ... (samplepoints>x(end) & samplepoints <= ... min (x(end) + window_size(2), samplepoints(end)))), ... gap_sample_values, "UniformOutput", false); window_points_r_c(:,2) = cellfun ( ... @(x,y) (fix ((x(1)-1)/sz_A_dim)+1)(ones (size(y))), ... gap_locations, window_points_r_c(:,1), "UniformOutput",false); ## If any window is emtpy, do not pass that gap to the move_fcn. empty_gaps = cellfun ("isempty", window_points_r_c(:,1)); if (any (empty_gaps)) removed_element_idx(... repelems (empty_gaps, [1:numel(gapsizes); gapsizes'])')... = false; window_points_r_c(empty_gaps,:) = []; gap_sample_values(empty_gaps) = []; gapsizes(empty_gaps) = []; gap_locations(empty_gaps) = []; endif if (! isempty (gapsizes)) ## Aval = A values at window locations ## Aloc = sample values at window locations A_window_indexes = cellfun ("sub2ind", {sz_A}, ... window_points_r_c(:,1), window_points_r_c(:,2), ... "UniformOutput", false); Aval = cellfun_subsref (A_window_indexes, false, {A}); Aloc = cellfun_subsref (window_points_r_c(:,1), false, ... {samplepoints}); ## Build fill values. fill_vals_C = cellfun (move_fcn, Aval, Aloc, ... gap_sample_values(:,1), "UniformOutput", false); ## Check for output of move_fcn having different size than gaps. if (! all (cellfun ("numel", fill_vals_C) == gapsizes)) error (["fillmissing: fill function return values must be ", ... "the same size as the gaps."]); endif [~, gap_trim_sort_idx] = sort (vertcat (gap_locations{:})); fill_vals_trim = cell2mat (fill_vals_C); if (! isempty (fill_vals_trim)) fill_vals = A(missing_locs); # prefill to include missing vals fill_vals(removed_element_idx(gap_full_sort_idx)) = ... fill_vals_trim(gap_trim_sort_idx); if (idx_flag) ## For movfcn with A of type having missing: ## Any outputs still containing class's 'missing' values ## are counted as not filled in idx_out, even if the value ## was put there by the movfcn. This is true even if ## missinglocations is used. If missinglocations changed ## a value with no apparent change, it still shows up ## as filled. ## If A has no missing value (int or logical), then without ## missinglocations, idx_out is always empty. With ## missinglocations, compatible behavior is undefined as ## Matlab 2022a has an apparent bug producing a error message ## saying missinglocations with int/logical needs a method that ## incldues function handle. Expect behavior should match other ## methods, where any processed missing value should be marked ## as filled no matter the fill value. if ((isnumeric(A) && !isinteger(A)) || ischar (A) || iscellstr (A)) idx_out(missing_locs) = ! ismissing (fill_vals); elseif (missinglocations) ## Any missing_locs processed and not skipped must become true. idx_out(missing_locs) = removed_element_idx(gap_full_sort_idx); endif endif endif endif endif endswitch if (! isempty (fill_vals)) A(missing_locs) = fill_vals; fill_vals = []; endif endif ## Process endgaps: if (any (endgap_locs(:))) switch (endgap_method) case "none" endgap_locs(:) = false; case "constant" if (isscalar (endgap_val)) fill_vals = endgap_val; else fill_vals = (endgap_locs .* endgap_val)(endgap_locs); endif if (idx_flag) ## If any v are the missing type, those get removed from idx_out ## unless using 'missinglocations'. idx_out(endgap_locs) = true; if (! missinglocations) && any (miss_ev = ismissing (endgap_val)) idx_out(endgap_locs & miss_ev) = false; endif endif case {"previous", "next", "nearest", "linear", ... "spline", "pchip", "makima"} ## All of these methods require sz_A_dim >= 2. shortcut path otherwise. if (sz_A_dim < 2) endgap_locs(:) = false; else switch (endgap_method) case "previous" ## Remove any gaps at front of array, includes all-missing cols. endgap_locs (logical (cumprod (endgap_locs,1))) = false; if (any (endgap_locs(:))) ## Find locations of the 'prev' value to use for filling. subsval_loc = [diff(endgap_locs, 1, 1); zero_padding] == 1; ## Calculate the number of spots each 'prev' needs to fill. gapsizes = (sum (endgap_locs, 1))(any (endgap_locs, 1))(:); ## Construct substitution value vector. fill_vals = repelems (A(subsval_loc), ... [1:numel(gapsizes); gapsizes'])'; endif case "next" ## Remove any gaps at back of array from endgap_locs ## including any all-missing columns. endgap_locs(logical (cumprod ( ... endgap_locs(dim_idx_flip{:}), 1)(dim_idx_flip{:}))) ... = false; if (any (endgap_locs(:))) ## Find locations of the 'next' value to use for filling. subsval_loc = diff ([zero_padding; endgap_locs],1,1) == -1; ## Calculate the number of spots each 'next' needs to fill. gapsizes = (sum (endgap_locs, 1))(any (endgap_locs, 1))(:); ## Construct substitution value vector. fill_vals = repelems (A(subsval_loc), ... [1:numel(gapsizes); gapsizes'])'; endif case "nearest" ## Remove any all-missing columns. endgap_locs &= (! prod (endgap_locs, 1)); if (any (endgap_locs(:))) ## Find front end info. front_gap_locs = logical (cumprod (endgap_locs, 1)); front_next_loc = diff ( ... [zero_padding; front_gap_locs], 1, 1) == -1; front_gapsizes = (sum (front_gap_locs, 1))(any ... (front_gap_locs,1)); ## Find back end info. back_gap_locs = logical ( ... cumprod (endgap_locs(dim_idx_flip{:}), 1)(dim_idx_flip{:})); back_prev_loc = [diff(back_gap_locs, 1, 1); zero_padding] == 1; back_gapsizes = (sum (back_gap_locs, 1))(any (back_gap_locs,1)); ## Combine into fill variables. [~, fb_sort_idx] = sort ... ([find(front_gap_locs); find(back_gap_locs)]); fillval_loc = [find(front_next_loc); find(back_prev_loc)]; gapsizes = [front_gapsizes; back_gapsizes]; ## Construct substitution value vector with sort order to mix ## fronts and backs in column order. fill_vals = (repelems (A(fillval_loc), ... [1:numel(gapsizes); gapsizes'])')(fb_sort_idx); endif case "linear" ## Endgaps not guaranteed to have enough points to interpolate. cols_to_use = (sum (! (missing_locs | endgap_locs), 1) > 1) ... & any (endgap_locs, 1); interp_locs = ! (missing_locs | endgap_locs) & cols_to_use; endgap_locs &= cols_to_use; if (any (endgap_locs(:))) ## Process front endgaps: front_gap_locs = logical (cumprod (endgap_locs, 1)); fill_vals_front = []; if (any (front_gap_locs(:))) front_gapsizes = (sum (front_gap_locs, 1))(any ... (front_gap_locs,1)); ## Collect first data point after gap & expand to gapsize. front_interppoint_1 = repelems ( find (... diff ([zero_padding; front_gap_locs], 1, 1) == -1), ... [1:numel(front_gapsizes); front_gapsizes'])'; ## Collect second data point after gap & expand to gapsize. front_interppoint_2 = repelems ( find ( ... diff ([zero_padding; ((cumsum (interp_locs, 1) .* ... any (front_gap_locs, 1)) == 2)], 1, 1) == 1), ... [1:numel(front_gapsizes); front_gapsizes'])'; front_interp_Avals = A([front_interppoint_1, ... front_interppoint_2]); front_interp_sampvals = samplepoints_expand( ... [front_interppoint_1, front_interppoint_2]); front_gap_loc_sampvals = samplepoints_expand(front_gap_locs); ## Hack for vector automatic orientation forcing col vector. if (isvector (front_interp_Avals)) front_interp_Avals = (front_interp_Avals(:)).'; endif if (isvector (front_interp_sampvals)) front_interp_sampvals = (front_interp_sampvals(:)).'; endif ## Perform interpolation for every gap point. interp_slopes_front = diff (front_interp_Avals, 1, 2) ... ./ diff (front_interp_sampvals, 1, 2); fill_vals_front = interp_slopes_front .* ... (front_gap_loc_sampvals - ... front_interp_sampvals(:,1)) + ... front_interp_Avals(:,1); endif ## Process back endgaps: back_gap_locs = logical ( ... cumprod (endgap_locs(dim_idx_flip{:}), 1)(dim_idx_flip{:})); fill_vals_back = []; if (any (back_gap_locs(:))) back_gapsizes = (sum ( ... back_gap_locs, 1))(any (back_gap_locs,1)); ## Collect last data point before gap & expand to gapsize. back_interppoint_2 = repelems ( ... find ([diff(back_gap_locs, 1, 1); zero_padding] == 1), ... [1:numel(back_gapsizes); back_gapsizes'])'; ## Collect 2nd to last data point before gap & expand to gap. back_interppoint_1 = repelems ( ... find ((diff ([zero_padding; ... ((cumsum (interp_locs(dim_idx_flip{:}), 1) .* ... any (back_gap_locs, 1)) == 2)], ... 1, 1) == 1)(dim_idx_flip{:})), ... [1:numel(back_gapsizes); back_gapsizes'])'; ## Build linear interpolant vectors. back_interp_Avals = A([back_interppoint_1, ... back_interppoint_2]); back_interp_sampvals = samplepoints_expand( ... [back_interppoint_1, back_interppoint_2]); back_gap_loc_sampvals = samplepoints_expand(back_gap_locs); ## Hack for vector automatic orientation forcing col vector. if (isvector (back_interp_Avals)) back_interp_Avals = (back_interp_Avals(:)).'; endif if (isvector (back_interp_sampvals)) back_interp_sampvals = (back_interp_sampvals(:)).'; endif ## Perform interpolation for every gap point. interp_slopes_back = diff (back_interp_Avals, 1, 2) ... ./ diff (back_interp_sampvals, 1, 2); fill_vals_back = interp_slopes_back .* ... (back_gap_loc_sampvals - ... back_interp_sampvals(:,1)) + ... back_interp_Avals(:,1); endif [~, fb_sort_idx] = sort ... ([find(front_gap_locs); find(back_gap_locs)]); fill_vals = [fill_vals_front; fill_vals_back](fb_sort_idx); endif case {"spline", "pchip", "makima"} ## endgap_locs not guaranteed to have 2 points. ## Need to ignore columns with < 2 values, or with nothing to do. cols_to_use = (sum (! (endgap_locs | missing_locs), 1) > 1) ... & any (endgap_locs, 1); ## Trim out unused cols from endgap_locs. endgap_locs &= cols_to_use; if (missinglocations) ## missinglocations may send columns with NaN and less than 2 ## real values resulting in interp1 error. Trim those columns, ## prepopulate fill_vals with NaN, mark as filled. fill_vals = NaN (sum (endgap_locs(:, cols_to_use)(:)), 1); cols_enough_points = (sum ( ... !isnan(A) & (! endgap_locs), 1) > 1) & cols_to_use; interp_cols = (cols_enough_points & cols_to_use); interp_vals = (endgap_locs & ... cols_enough_points)(endgap_locs & cols_to_use); fill_vals(interp_vals) = other_interpolants ( ... A(:, interp_cols),endgap_locs(:, interp_cols), ... missing_locs(:, interp_cols), endgap_method, ... samplepoints); else fill_vals = other_interpolants ( A(:, cols_to_use), endgap_locs(:, cols_to_use), ... missing_locs(:, cols_to_use), endgap_method, samplepoints); endif endswitch endif if (idx_flag) idx_out(endgap_locs) = true; endif endswitch ## Some methods remove fill locations, only process if not empty. if (any (endgap_locs(:))) ## Replace missings with appropriate fill values. A(endgap_locs) = fill_vals; endif endif if (reshape_flag) ## Revert permutation: A = permute (A, dim_idx_perm); if (idx_flag) idx_out = permute (idx_out, dim_idx_perm); endif endif endfunction function varargout = cellfun_subsref (x, TF, varargin) ## Utility fcn for cellfun (@(x) A(x), x, "UniformOutput", true/false). ## ~50% faster than anonymous function call. ## pass A, x, and truefalse. ## If nargout > 1, repeat for C2 with B(x), C3 with C(x), etc. x_C = num2cell (struct ("type", "()", "subs", num2cell (x))); for (idx = 1 : numel (varargin)) varargout{idx} = cellfun ("subsref", varargin{idx}, x_C, ... "UniformOutput", TF); endfor endfunction function fill_vals = other_interpolants (data_array, primary_locs, secondary_locs, method, samplepoints) ## Use interp1 to perform more complex interpolations. Will only be performed ## on numerical data. ## primary_locs is missing_locs or endgap_locs, whichever the fill_vals are ## being returned for. secondary_locs is the other. ## ## FIXME: splitting out from columnwise cellfun to interp1 would increase ## speed a lot, but cannot count on same number of elements being processed ## in each column. ## ## Will error on any columns without at least two non-NaN interpolation ## values. ## ## Matlab incompatibility - using interp1, if 'missinglocations' sends through ## data_array values with NaN, interp1 will ignore those and interpolate with ## other data points. Matlab will instead return NaN. ## Find logical data and empty location indices for each column to be ## used in columnwise call to interp1 (cast as columnwise cell arrays). interp_data_locs = num2cell ((! (primary_locs | secondary_locs)), 1); interp_locs_tofill = num2cell (primary_locs, 1); ## Build cell arrays with sample and interp values to pass to interp1. [A_interpvalues, interp_samplevals] = cellfun_subsref (interp_data_locs, ... false, num2cell (data_array, 1), {samplepoints}); interp_empty_samplevals = cellfun_subsref (interp_locs_tofill, false, ... {samplepoints}); ## Generate fill_vals using interp1 for missing locations. fill_vals = vertcat (cellfun ("interp1", interp_samplevals, ... A_interpvalues, interp_empty_samplevals, {method}, ... {"extrap"}, "UniformOutput", false){:}); endfunction function med = columnwise_median (x) ## Take a column of values, ignore any NaN values, return the median ## of what's left. Return NaN if no values. ## Uses only built-in fns to avoid 3x 'median' slowdown. szx = size (x); if (isempty (x)) med = NaN ([1, szx(2:end)]); elseif (isvector (x)) x = x(! isnan (x)); x = sort (x); n = numel (x); if (mod (n, 2)) ## odd med = x((n+1)/2); elseif (n > 0) ## even med = (x(n/2) + x((n/2)+1))/2; else ## Only called for types with missing = NaN. med = NaN; endif else x = sort (x, 1); # NaNs sent to bottom. n = sum (! isnan (x), 1); m_idx_odd = logical (mod (n, 2)); # 0 even or zero, 1 odd. m_idx_even = !m_idx_odd & (n != 0); k = floor ((n + 1) ./ 2); med = NaN ([1, szx(2:end)]); if (! ismatrix (x)) szx = [szx(1), prod(szx(2:end))]; endif if (any (m_idx_odd(:))) x_idx_odd = sub2ind (szx, k(m_idx_odd)(:), find(m_idx_odd)(:)); med(m_idx_odd) = x(x_idx_odd); endif if (any (m_idx_even(:))) k_even = k(m_idx_even)(:); x_idx_even = sub2ind (szx, [k_even, k_even+1], ... (find (m_idx_even))(:, [1, 1])); med(m_idx_even) = sum (x(x_idx_even), 2) / 2; endif endif endfunction %!assert (fillmissing ([1, 2, 3], "constant", 99), [1, 2, 3]) %!assert (fillmissing ([1, 2, NaN], "constant", 99), [1, 2, 99]) %!assert (fillmissing ([NaN, 2, NaN], "constant", 99), [99, 2, 99]) %!assert (fillmissing ([1, 2, 3]', "constant", 99), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN]', "constant", 99), [1, 2, 99]') %!assert (fillmissing ([1, 2, 3; 4, 5, 6], "constant", 99), [1, 2, 3; 4, 5, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "constant", 99), [1, 2, 99; 4, 99, 6]) %!assert (fillmissing ([NaN, 2, NaN; 4, NaN, 6], "constant", [97, 98, 99]), [97, 2, 99; 4, 98, 6]) %!test %! x = cat (3, [1, 2, NaN; 4, NaN, 6], [NaN, 2, 3; 4, 5, NaN]); %! y = cat (3, [1, 2, 99; 4, 99, 6], [99, 2, 3; 4, 5, 99]); %! assert (fillmissing (x, "constant", 99), y); %! y = cat (3, [1, 2, 96; 4, 95, 6], [97, 2, 3; 4, 5, 99]); %! assert (fillmissing (x, "constant", [94:99]), y); %! assert (fillmissing (x, "constant", [94:99]'), y); %! assert (fillmissing (x, "constant", permute ([94:99], [1 3 2])), y); %! assert (fillmissing (x, "constant", [94, 96, 98; 95, 97, 99]), y); %! assert (fillmissing (x, "constant", [94:99], 1), y); %! y = cat (3, [1, 2, 96; 4, 97, 6], [98, 2, 3; 4, 5, 99]); %! assert (fillmissing (x, "constant", [96:99], 2), y); %! y = cat (3, [1, 2, 98; 4, 97, 6], [94, 2, 3; 4, 5, 99]); %! assert (fillmissing (x, "constant", [94:99], 3), y); %! y = cat (3, [1, 2, 92; 4, 91, 6], [94, 2, 3; 4, 5, 99]); %! assert (fillmissing (x, "constant", [88:99], 99), y); %!test %! x = reshape ([1:24], 4, 3, 2); %! x([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = NaN; %! y = x; %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [94, 95, 95, 96, 96, 97, 97, 98, 99, 99]; %! assert (fillmissing (x, "constant", [94:99], 1), y); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [92, 93, 94, 92, 95, 97, 99, 98, 97, 98]; %! assert (fillmissing (x, "constant", [92:99], 2), y); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [88, 93, 94, 96, 99, 89, 91, 94, 97, 98]; %! assert (fillmissing (x, "constant", [88:99], 3), y); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [76, 81, 82, 84, 87, 89, 91, 94, 97, 98]; %! assert (fillmissing (x, "constant", [76:99], 99), y); ## Tests with different endvalues behavior %!assert (fillmissing ([1, 2, 3], "constant", 99, "endvalues", 88), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 99, "endvalues", 88), [1, 99, 3]) %!assert (fillmissing ([1, 2, NaN], "constant", 99, "endvalues", 88), [1, 2, 88]) %!assert (fillmissing ([NaN, 2, 3], "constant", 99, "endvalues", 88), [88, 2, 3]) %!assert (fillmissing ([NaN, NaN, 3], "constant", 99, "endvalues", 88), [88, 88, 3]) %!assert (fillmissing ([1, NaN, NaN], "constant", 99, "endvalues", 88), [1, 88, 88]) %!assert (fillmissing ([NaN, 2, NaN], "constant", 99, "endvalues", 88), [88, 2, 88]) %!assert (fillmissing ([NaN, 2, NaN]', "constant", 99, "endvalues", 88), [88, 2, 88]') %!assert (fillmissing ([1, NaN, 3, NaN, 5], "constant", 99, "endvalues", 88), [1, 99, 3, 99, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "constant", 99, "endvalues", 88), [1, 99, 99, 99, 5]) %!assert (fillmissing ([NaN, NaN, NaN, NaN, 5], "constant", 99, "endvalues", 88), [88, 88, 88, 88, 5]) %!assert (fillmissing ([1, NaN, 3, 4, NaN], "constant", 99, "endvalues", 88), [1, 99, 3, 4, 88]) %!assert (fillmissing ([1, NaN, 3, 4, NaN], "constant", 99, 1, "endvalues", 88), [1, 88, 3, 4, 88]) %!assert (fillmissing ([1, NaN, 3, 4, NaN], "constant", 99, 1, "endvalues", "extrap"), [1, 99, 3, 4, 99]) %!test %! x = reshape ([1:24], 3, 4, 2); %! y = x; %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y([1, 2, 5, 6, 10, 13, 16, 18, 19, 20, 21, 22]) = 88; %! y([8]) = 99; %! assert (fillmissing (x, "constant", 99, "endvalues", 88), y); %! assert (fillmissing (x, "constant", 99, 1, "endvalues", 88), y); %! y = x; %! y([1, 2, 5, 8, 10, 13, 16, 19, 22]) = 88; %! y([6, 18, 20, 21]) = 99; %! assert (fillmissing (x, "constant", 99, 2, "endvalues", 88), y); %! y(y == 99) = 88; %! assert (fillmissing (x, "constant", 99, 3, "endvalues", 88), y); %! assert (fillmissing (x, "constant", 99, 4, "endvalues", 88), y); %! assert (fillmissing (x, "constant", 99, 99, "endvalues", 88), y); %! y([8]) = 94; %! assert (fillmissing (x, "constant", [92:99], 1, "endvalues", 88), y); %! y([6, 8, 18, 20, 21]) = [96, 88, 99, 98, 99]; %! assert (fillmissing (x, "constant", [94:99], 2, "endvalues", 88), y); %! y = x; %! y(isnan (y)) = 88; %! assert (fillmissing (x, "constant", [88:99], 3, "endvalues", 88), y); %! y = x; %! y(isnan (y)) = [82, 82, 83, 83, 94, 85, 86, 87, 87, 88, 88, 88, 89]; %! assert (fillmissing (x, "constant", [92:99], 1, "endvalues", [82:89]), y); %! y = x; %! y(isnan (y)) = [84, 85, 85, 96, 85, 84, 87, 87, 99, 87, 98, 99, 87]; %! assert (fillmissing (x, "constant", [94:99], 2, "endvalues", [84:89]), y); %! y = x; %! y(isnan (y)) = [68, 69, 72, 73, 75, 77, 68, 71, 73, 74, 75, 76, 77]; %! assert (fillmissing (x, "constant", [88:99], 3, "endvalues", [68:79]), y); %! assert (fillmissing (x, "constant", [88:93; 94:99]', 3, "endvalues", [68:73; 74:79]'), y) %!test %! x = reshape ([1:24],4,3,2); %! x([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = NaN; %! y = x; %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [94, 95, 95, 96, 96, 97, 97, 98, 99, 99]; %! assert (fillmissing (x, "constant", [94:99], 1), y); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [92, 93, 94, 92, 95, 97, 99, 98, 97, 98]; %! assert (fillmissing (x, "constant", [92:99], 2), y); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [88, 93, 94, 96, 99, 89, 91, 94, 97, 98]; %! assert (fillmissing (x, "constant", [88:99], 3), y); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [76, 81, 82, 84, 87, 89, 91, 94, 97, 98]; %! assert (fillmissing (x, "constant", [76:99], 99), y); ## next/previous tests %!assert (fillmissing ([1, 2, 3], "previous"), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], "next"), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3]', "previous"), [1, 2, 3]') %!assert (fillmissing ([1, 2, 3]', "next"), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN], "previous"), [1, 2, 2]) %!assert (fillmissing ([1, 2, NaN], "next"), [1, 2, NaN]) %!assert (fillmissing ([NaN, 2, NaN], "previous"), [NaN, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "next"), [2, 2, NaN]) %!assert (fillmissing ([1, NaN, 3], "previous"), [1, 1, 3]) %!assert (fillmissing ([1, NaN, 3], "next"), [1, 3, 3]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "previous", 1), [1, 2, NaN; 4, 2, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "previous", 2), [1, 2, 2; 4, 4, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "previous", 3), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "next", 1), [1, 2, 6; 4, NaN, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "next", 2), [1, 2, NaN; 4, 6, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "next", 3), [1, 2, NaN; 4, NaN, 6]) %!test %! x = reshape ([1:24], 4, 3, 2); %! x([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = NaN; %! y = x; %! y([1, 6, 7, 9, 14, 19, 22, 23]) = [2, 8, 8, 10, 15, 20, 24, 24]; %! assert (fillmissing (x, "next", 1), y); %! y = x; %! y([1, 6, 7, 14, 16]) = [5, 10, 11, 18, 20]; %! assert (fillmissing (x, "next", 2), y); %! y = x; %! y([1, 6, 9, 12]) = [13, 18, 21, 24]; %! assert (fillmissing (x, "next", 3), y); %! assert (fillmissing (x, "next", 99), x); %! y = x; %! y([6, 7, 12, 14, 16, 19, 22, 23]) = [5, 5, 11, 13, 15, 18, 21, 21]; %! assert (fillmissing (x, "previous", 1), y); %! y = x; %! y([6, 7, 9, 12, 19, 22, 23]) = [2, 3, 5, 8, 15, 18, 15]; %! assert (fillmissing (x, "previous", 2), y); %! y = x; %! y([14, 16, 22, 23]) = [2, 4, 10, 11]; %! assert (fillmissing (x, "previous", 3), y); %! assert (fillmissing (x, "previous", 99), x); ## next/previous tests with different endvalue behavior %!assert (fillmissing ([1, 2, 3], "constant", 0, "endvalues", "previous"), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], "constant", 0, "endvalues", "next"), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 0, "endvalues", "previous"), [1, 0, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 0, "endvalues", "next"), [1, 0, 3]) %!assert (fillmissing ([1, 2, NaN], "constant", 0, "endvalues", "previous"), [1, 2, 2]) %!assert (fillmissing ([1, 2, NaN], "constant", 0, "endvalues", "next"), [1, 2, NaN]) %!assert (fillmissing ([1, NaN, NaN], "constant", 0, "endvalues", "previous"), [1, 1, 1]) %!assert (fillmissing ([1, NaN, NaN], "constant", 0, "endvalues", "next"), [1, NaN, NaN]) %!assert (fillmissing ([NaN, 2, 3], "constant", 0, "endvalues", "previous"), [NaN, 2, 3]) %!assert (fillmissing ([NaN, 2, 3], "constant", 0, "endvalues", "next"), [2, 2, 3]) %!assert (fillmissing ([NaN, NaN, 3], "constant", 0, "endvalues", "previous"), [NaN, NaN, 3]) %!assert (fillmissing ([NaN, NaN, 3], "constant", 0, "endvalues", "next"), [3, 3, 3]) %!assert (fillmissing ([NaN, NaN, NaN], "constant", 0, "endvalues", "previous"), [NaN, NaN, NaN]) %!assert (fillmissing ([NaN, NaN, NaN], "constant", 0, "endvalues", "next"), [NaN, NaN, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, "endvalues", "previous"), [NaN, 2, 0, 4, 4]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, "endvalues", "next"), [2, 2, 0, 4, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 1, "endvalues", "previous"), [NaN, 2, NaN, 4, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 1, "endvalues", "next"), [NaN, 2, NaN, 4, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 2, "endvalues", "previous"), [NaN, 2, 0, 4, 4]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 2, "endvalues", "next"), [2, 2, 0, 4, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 3, "endvalues", "previous"), [NaN, 2, NaN, 4, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 3, "endvalues", "next"), [NaN, 2, NaN, 4, NaN]) %!test %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([5, 6, 8, 18]) = [4, 4, 0, 17]; %! assert (fillmissing (x, "constant", 0, "endvalues", "previous"), y); %! assert (fillmissing (x, "constant", 0, 1, "endvalues", "previous"), y); %! y = x; %! y([6, 10, 18, 20, 21]) = [0, 7, 0, 0, 0]; %! assert (fillmissing (x, "constant", 0, 2, "endvalues", "previous"), y); %! y = x; %! y([16, 19, 21]) = [4, 7, 9]; %! assert (fillmissing (x, "constant", 0, 3, "endvalues", "previous"), y); %! assert (fillmissing (x, "constant", 0, 4, "endvalues", "previous"), x); %! assert (fillmissing (x, "constant", 0, 99, "endvalues", "previous"), x); %! y = x; %! y([1, 2, 8, 10, 13, 16, 22]) = [3, 3, 0, 11, 14, 17, 23]; %! assert (fillmissing (x, "constant", 0, "endvalues", "next"), y); %! assert (fillmissing (x, "constant", 0, 1, "endvalues", "next"), y); %! y = x; %! y([1, 2, 5, 6, 8, 18, 20, 21]) = [4, 11, 11, 0, 11, 0, 0, 0]; %! assert (fillmissing (x, "constant", 0, 2, "endvalues", "next"), y); %! y = x; %! y([2, 5]) = [14, 17]; %! assert (fillmissing (x, "constant", 0, 3, "endvalues", "next"), y); %! assert (fillmissing (x, "constant", 0, 4, "endvalues", "next"), x); %! assert (fillmissing (x, "constant", 0, 99, "endvalues", "next"), x); ## Tests for nearest %!assert (fillmissing ([1, 2, 3], "nearest"), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3]', "nearest"), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN], "nearest"), [1, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "nearest"), [2, 2, 2]) %!assert (fillmissing ([1, NaN, 3], "nearest"), [1, 3, 3]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "nearest", 1), [1, 2, 6; 4, 2, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "nearest", 2), [1, 2, 2; 4, 6, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "nearest", 3), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "nearest"), [1, 3, 3, 5, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "nearest", "samplepoints", [0, 1, 2, 3, 4]), [1, 3, 3, 5, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "nearest", "samplepoints", [0.5, 1, 2, 3, 5]), [1, 1, 3, 3, 5]) %!test %! x = reshape ([1:24], 4, 3, 2); %! x([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = NaN; %! y = x; %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [2, 5, 8, 10, 11, 15, 15, 20, 21, 24]; %! assert (fillmissing (x, "nearest", 1), y); %! y = x; %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = [5, 10, 11, 5, 8, 18, 20, 15, 18, 15]; %! assert (fillmissing (x, "nearest", 2), y); %! y = x; %! y([1, 6, 9, 12, 14, 16, 22, 23]) = [13, 18, 21, 24, 2, 4, 10, 11]; %! assert (fillmissing (x, "nearest", 3), y); %! assert (fillmissing (x, "nearest", 99), x); ## Tests for nearest with diff endvalue behavior %!assert (fillmissing ([1, 2, 3], "constant", 0, "endvalues", "nearest"), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 0, "endvalues", "nearest"), [1 0 3]) %!assert (fillmissing ([1, 2, NaN], "constant", 0, "endvalues", "nearest"), [1, 2, 2]) %!assert (fillmissing ([1, NaN, NaN], "constant", 0, "endvalues", "nearest"), [1, 1, 1]) %!assert (fillmissing ([NaN, 2, 3], "constant", 0, "endvalues", "nearest"), [2, 2, 3]) %!assert (fillmissing ([NaN, NaN, 3], "constant", 0, "endvalues", "nearest"), [3, 3, 3]) %!assert (fillmissing ([NaN, NaN, NaN], "constant", 0, "endvalues", "nearest"), [NaN, NaN, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, "endvalues", "nearest"), [2, 2, 0, 4, 4]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 1, "endvalues", "nearest"), [NaN, 2, NaN, 4, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 2, "endvalues", "nearest"), [2, 2, 0, 4, 4]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 0, 3, "endvalues", "nearest"), [NaN, 2, NaN, 4, NaN]) %!test %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([1, 2, 5, 6, 8, 10, 13, 16, 18, 22]) = [3, 3, 4, 4, 0, 11, 14, 17, 17, 23]; %! assert (fillmissing (x, "constant", 0, "endvalues", "nearest"), y); %! assert (fillmissing (x, "constant", 0, 1, "endvalues", "nearest"), y); %! y = x; %! y([1, 2, 5, 6, 8, 10, 18, 20, 21]) = [4, 11, 11, 0, 11, 7, 0, 0, 0]; %! assert (fillmissing (x, "constant", 0, 2, "endvalues", "nearest"), y); %! y = x; %! y([2, 5, 16, 19, 21]) = [14, 17, 4, 7, 9]; %! assert (fillmissing (x, "constant", 0, 3, "endvalues", "nearest"), y); %! assert (fillmissing (x, "constant", 0, 99, "endvalues", "nearest"), x); ## Tests for linear %!assert (fillmissing ([1, 2, 3], "linear"), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3]', "linear"), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN], "linear"), [1, 2, 3]) %!assert (fillmissing ([NaN, 2, NaN], "linear"), [NaN, 2, NaN]) %!assert (fillmissing ([1, NaN, 3], "linear"), [1, 2, 3]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "linear", 1), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "linear", 2), [1, 2, 3; 4, 5, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "linear", 3), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "linear"), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "linear", "samplepoints", [0, 1, 2, 3, 4]), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "linear", "samplepoints", [0, 1.5, 2, 5, 14]), [1, 2.5, 3, 3.5, 5], eps) %!test %! x = reshape ([1:24], 4, 3, 2); %! x([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = NaN; %! assert (fillmissing (x, "linear", 1), reshape ([1:24], 4, 3, 2)); %! y = reshape ([1:24], 4, 3, 2); %! y([1, 9, 14, 19, 22, 23]) = NaN; %! assert (fillmissing (x, "linear", 2), y); %! y = reshape ([1:24], 4, 3, 2); %! y([1, 6, 7, 9, 12, 14, 16, 19, 22, 23]) = NaN; %! assert (fillmissing (x, "linear", 3), y); %! assert (fillmissing (x, "linear", 99), x); ## Tests for linear with diff endvalue behavior %!assert (fillmissing ([1, 2, 3], "linear", "endvalues", 0), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "linear", "endvalues", 0), [1, 2, 3]) %!assert (fillmissing ([1, 2, NaN], "linear", "endvalues", 0), [1, 2, 0]) %!assert (fillmissing ([1, NaN, NaN], "linear", "endvalues", 0), [1, 0, 0]) %!assert (fillmissing ([NaN, 2, 3], "linear", "endvalues", 0), [0, 2, 3]) %!assert (fillmissing ([NaN, NaN, 3], "linear", "endvalues", 0), [0, 0, 3]) %!assert (fillmissing ([NaN, NaN, NaN], "linear", "endvalues", 0), [0, 0, 0]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "linear", "endvalues", 0), [0, 2, 3, 4, 0]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "linear", 1, "endvalues", 0), [0, 2, 0, 4, 0]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "linear", 2, "endvalues", 0), [0, 2, 3, 4, 0]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "linear", 3, "endvalues", 0), [0, 2, 0, 4, 0]) %!test %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([1, 2, 5, 6, 10, 13, 16, 18, 19, 20, 21, 22]) = 0; %! y(8) = 8; %! assert (fillmissing (x, "linear", "endvalues", 0), y); %! assert (fillmissing (x, "linear", 1, "endvalues", 0), y); %! y = x; %! y([1, 2, 5, 8, 10, 13, 16, 19, 22]) = 0; %! y([6, 18, 20, 21]) = [6, 18, 20, 21]; %! assert (fillmissing (x, "linear", 2, "endvalues", 0), y); %! y = x; %! y(isnan(y)) = 0; %! assert (fillmissing (x, "linear", 3, "endvalues", 0), y); %! assert (fillmissing (x, "linear", 99, "endvalues", 0), y); ## Tests with linear only on endvalues %!assert (fillmissing ([1, 2, 3], "constant", 99, "endvalues", "linear"), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 99, "endvalues", "linear"), [1, 99, 3]) %!assert (fillmissing ([1, NaN, 3, NaN], "constant", 99, "endvalues", "linear"), [1, 99, 3, 4]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 99, "endvalues", "linear"), [1, 2, 99, 4, 5]) %!assert (fillmissing ([NaN, 2, NaN, NaN], "constant", 99, "endvalues", "linear"), [NaN, 2, NaN, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 99, "endvalues", "linear", "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 99, 4, 5]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 99, "endvalues", "linear", "samplepoints", [0, 2, 3, 4, 10]), [0, 2, 99, 4, 10]) ## Test other interpolants %!test %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([1, 6, 10, 18, 20, 21]) = [2.5, 5, 8.5, 17.25, 21, 21.75]; %! assert (fillmissing (x, "linear", 2, "samplepoints", [2 4 8 10]), y, eps); %! y([1, 6, 10, 18, 20, 21]) = [2.5, 4.5, 8.5, 17.25, 21.5, 21.75]; %! assert (fillmissing (x, "spline", 2, "samplepoints", [2, 4, 8, 10]), y, eps); %! y([1, 6, 10, 18, 20, 21]) = [2.5, 4.559386973180077, 8.5, 17.25, 21.440613026819925, 21.75]; %! assert (fillmissing (x, "pchip", 2, "samplepoints", [2, 4, 8, 10]), y, 10*eps); ## known fail: makima method not yet implemented in interp1 %!test <60965> %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([1, 6, 10, 18, 20, 21]) = [2.5, 4.609523809523809, 8.5, 17.25, 21.390476190476186, 21.75]; %! assert (fillmissing (x, "makima", 2, "samplepoints", [2, 4, 8, 10]), y, 10*eps); ## Test other interpolants code path on endvalues %!assert (fillmissing ([1, 2, 3], "constant", 99, "endvalues", "spline"), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 99, "endvalues", "spline"), [1, 99, 3]) %!assert (fillmissing ([1, NaN, 3, NaN], "constant", 99, "endvalues", "spline"), [1, 99, 3, 4]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 99, "endvalues", "spline"), [1, 2, 99, 4, 5]) %!assert (fillmissing ([NaN, 2, NaN, NaN], "constant", 99, "endvalues", "spline"), [NaN, 2, NaN, NaN]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 99, "endvalues", "spline", "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 99, 4, 5]) %!assert (fillmissing ([NaN, 2, NaN, 4, NaN], "constant", 99, "endvalues", "spline", "samplepoints", [0, 2, 3, 4, 10]), [0, 2, 99, 4, 10]) ## Test movmean %!assert (fillmissing ([1, 2, 3], "movmean", 1), [1, 2, 3]) %!assert (fillmissing ([1, 2, NaN], "movmean", 1), [1, 2, NaN]) %!assert (fillmissing ([1, 2, 3], "movmean", 2), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], "movmean", [1, 0]), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3]', "movmean", 2), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN], "movmean", 2), [1, 2, 2]) %!assert (fillmissing ([1, 2, NaN], "movmean", [1, 0]), [1, 2, 2]) %!assert (fillmissing ([1, 2, NaN], "movmean", [1, 0]'), [1, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "movmean", 2), [NaN, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "movmean", [1, 0]), [NaN, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "movmean", [0, 1]), [2, 2, NaN]) %!assert (fillmissing ([NaN, 2, NaN], "movmean", [0, 1.1]), [2, 2, NaN]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmean", [3, 0]), [1, 1, 3, 2, 5]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "movmean", 3, 1), [1, 2, 6; 4, 2, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "movmean", 3, 2), [1, 2, 2; 4, 5, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "movmean", 3, 3), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmean", 99), [1, 3, 3, 3, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmean", 99, 1), [1, NaN, 3, NaN, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', "movmean", 99, 1), [1, 3, 3, 3, 5]') %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmean", 99, 2), [1, 3, 3, 3, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', "movmean", 99, 2), [1, NaN, 3, NaN, 5]') %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", 3, "samplepoints", [1, 2, 3, 4, 5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", [1, 1], "samplepoints", [1, 2, 3, 4, 5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", [1.5, 1.5], "samplepoints", [1, 2, 3, 4, 5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", 4, "samplepoints", [1, 2, 3, 4, 5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", [2, 2], "samplepoints", [1, 2, 3, 4, 5]), [1, 1, 3, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", 4.0001, "samplepoints", [1, 2, 3, 4, 5]), [1, 1, 3, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", 3, "samplepoints", [1.5, 2, 3, 4, 5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", 3, "samplepoints", [1 2, 3, 4, 4.5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", 3, "samplepoints", [1.5, 2, 3, 4, 4.5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", [1.5, 1.5], "samplepoints", [1.5, 2, 3, 4, 5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", [1.5, 1.5], "samplepoints", [1, 2, 3, 4, 4.5]), [1, 1, 5, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmean", [1.5, 1.5], "samplepoints", [1.5, 2 3, 4, 4.5]), [1, 1, 3, 5, 5]) %!test %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([2, 5, 8, 10, 13, 16, 18, 22]) = [3, 4, 8, 11, 14, 17, 17, 23]; %! assert (fillmissing (x, "movmean", 3), y); %! assert (fillmissing (x, "movmean", [1, 1]), y); %! assert (fillmissing (x, "movmean", 3, "endvalues", "extrap"), y); %! assert (fillmissing (x, "movmean", 3, "samplepoints", [1, 2, 3]), y); %! y = x; %! y([1, 6, 8, 10, 18, 20, 21]) = [4, 6, 11, 7, 15, 20, 24]; %! assert (fillmissing (x, "movmean", 3, 2), y); %! assert (fillmissing (x, "movmean", [1, 1], 2), y); %! assert (fillmissing (x, "movmean", 3, 2, "endvalues", "extrap"), y); %! assert (fillmissing (x, "movmean", 3, 2, "samplepoints", [1, 2, 3, 4]), y); %! y([1, 18]) = NaN; %! y(6) = 9; %! assert (fillmissing (x, "movmean", 3, 2, "samplepoints", [0, 2, 3, 4]), y); %! y = x; %! y([1, 2, 5, 6, 10, 13, 16, 18, 19, 20, 21, 22]) = 99; %! y(8) = 8; %! assert (fillmissing (x, "movmean", 3, "endvalues", 99), y); %! y = x; %! y([1, 2, 5, 8, 10, 13, 16, 19, 22]) = 99; %! y([6, 18, 20, 21]) = [6, 15, 20, 24]; %! assert (fillmissing (x, "movmean", 3, 2, "endvalues", 99), y); ## Test movmedian %!assert (fillmissing ([1, 2, 3], "movmedian", 1), [1, 2, 3]) %!assert (fillmissing ([1, 2, NaN], "movmedian", 1), [1, 2, NaN]) %!assert (fillmissing ([1, 2, 3], "movmedian", 2), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], "movmedian", [1, 0]), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3]', "movmedian", 2), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN], "movmedian", 2), [1, 2, 2]) %!assert (fillmissing ([1, 2, NaN], "movmedian", [1, 0]), [1, 2, 2]) %!assert (fillmissing ([1, 2, NaN], "movmedian", [1, 0]'), [1, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "movmedian", 2), [NaN, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "movmedian", [1, 0]), [NaN, 2, 2]) %!assert (fillmissing ([NaN, 2, NaN], "movmedian", [0, 1]), [2, 2, NaN]) %!assert (fillmissing ([NaN, 2, NaN], "movmedian", [0, 1.1]), [2, 2, NaN]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmedian", [3, 0]), [1, 1, 3, 2, 5]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "movmedian", 3, 1), [1, 2, 6; 4, 2, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "movmedian", 3, 2), [1, 2, 2; 4, 5, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], "movmedian", 3, 3), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmedian", 99), [1, 3, 3, 3, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmedian", 99, 1), [1, NaN, 3, NaN, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', "movmedian", 99, 1), [1, 3, 3, 3, 5]') %!assert (fillmissing ([1, NaN, 3, NaN, 5], "movmedian", 99, 2), [1, 3, 3, 3, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', "movmedian", 99, 2), [1, NaN, 3, NaN, 5]') %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", 3, "samplepoints", [1, 2, 3, 4, 5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", [1, 1], "samplepoints", [1, 2, 3, 4, 5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", [1.5, 1.5], "samplepoints", [1, 2, 3, 4, 5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", 4, "samplepoints", [1, 2, 3, 4, 5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", [2, 2], "samplepoints", [1, 2, 3, 4, 5]), [1, 1, 3, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", 4.0001, "samplepoints", [1, 2, 3, 4, 5]), [1, 1, 3, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", 3, "samplepoints", [1.5 2 3 4 5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", 3, "samplepoints", [1 2 3 4 4.5]), [1, 1, NaN, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", 3, "samplepoints", [1.5 2 3 4 4.5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", [1.5, 1.5], "samplepoints", [1.5 2 3 4 5]), [1, 1, 1, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", [1.5, 1.5], "samplepoints", [1 2 3 4 4.5]), [1, 1, 5, 5, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], "movmedian", [1.5, 1.5], "samplepoints", [1.5 2 3 4 4.5]), [1, 1, 3, 5, 5]) %!test %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([2, 5, 8, 10, 13, 16, 18, 22]) = [3, 4, 8, 11, 14, 17, 17, 23]; %! assert (fillmissing (x, "movmedian", 3), y); %! assert (fillmissing (x, "movmedian", [1, 1]), y); %! assert (fillmissing (x, "movmedian", 3, "endvalues", "extrap"), y); %! assert (fillmissing (x, "movmedian", 3, "samplepoints", [1, 2, 3]), y); %! y = x; %! y([1, 6, 8, 10, 18, 20, 21]) = [4, 6, 11, 7, 15, 20, 24]; %! assert (fillmissing (x, "movmedian", 3, 2), y); %! assert (fillmissing (x, "movmedian", [1, 1], 2), y); %! assert (fillmissing (x, "movmedian", 3, 2, "endvalues", "extrap"), y); %! assert (fillmissing (x, "movmedian", 3, 2, "samplepoints", [1, 2, 3, 4]), y); %! y([1,18]) = NaN; %! y(6) = 9; %! assert (fillmissing (x, "movmedian", 3, 2, "samplepoints", [0, 2, 3, 4]), y); %! y = x; %! y([1, 2, 5, 6, 10, 13, 16, 18, 19, 20, 21, 22]) = 99; %! y(8) = 8; %! assert (fillmissing (x, "movmedian", 3, "endvalues", 99), y); %! y = x; %! y([1, 2, 5, 8, 10, 13, 16, 19, 22]) = 99; %! y([6, 18, 20, 21]) = [6, 15, 20, 24]; %! assert (fillmissing (x, "movmedian", 3, 2, "endvalues", 99), y); ## Test movfcn %!assert (fillmissing ([1, 2, 3], @(x,y,z) x+y+z, 2), [1, 2, 3]) %!assert (fillmissing ([1, 2, NaN], @(x,y,z) x+y+z, 1), [1, 2, NaN]) %!assert (fillmissing ([1, 2, 3], @(x,y,z) x+y+z, 2), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], @(x,y,z) x+y+z, [1, 0]), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3]', @(x,y,z) x+y+z, 2), [1, 2, 3]') %!assert (fillmissing ([1, 2, NaN], @(x,y,z) x+y+z, 2), [1, 2, 7]) %!assert (fillmissing ([1, 2, NaN], @(x,y,z) x+y+z, [1, 0]), [1, 2, 7]) %!assert (fillmissing ([1, 2, NaN], @(x,y,z) x+y+z, [1, 0]'), [1, 2, 7]) %!assert (fillmissing ([NaN, 2, NaN], @(x,y,z) x+y+z, 2), [5, 2, 7]) %!assert (fillmissing ([NaN, 2, NaN], @(x,y,z) x+y+z, [1, 0]), [NaN, 2, 7]) %!assert (fillmissing ([NaN, 2, NaN], @(x,y,z) x+y+z, [0, 1]), [5, 2, NaN]) %!assert (fillmissing ([NaN, 2, NaN], @(x,y,z) x+y+z, [0, 1.1]), [5, 2, NaN]) %!assert (fillmissing ([1, 2, NaN, NaN, 3, 4], @(x,y,z) x+y+z, 2), [1, 2, 7, 12, 3, 4]) %!assert (fillmissing ([1, 2, NaN, NaN, 3, 4], @(x,y,z) x+y+z, 0.5), [1, 2, NaN, NaN, 3, 4]) %!function A = testfcn (x, y, z) %! if (isempty (y)) %! A = z; %! elseif (numel (y) == 1) %! A = repelem (x(1), numel(z)); %! else %! A = interp1 (y, x, z, "linear", "extrap"); %! endif %!endfunction %!assert (fillmissing ([1, NaN, 3, NaN, 5], @testfcn, [3, 0]), [1, 1, 3, NaN, 5]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], @testfcn, 3, 1), [1, 2, 6; 4, 2, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], @testfcn, 3, 2), [1, 2, 2; 4, 5, 6]) %!assert (fillmissing ([1, 2, NaN; 4, NaN, 6], @testfcn, 3, 3), [1, 2, NaN; 4, NaN, 6]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], @testfcn, 99), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], @testfcn, 99, 1), [1, NaN, 3, NaN, 5]) ##known not-compatible. matlab bug ML2022a: [1, 1, 3, 1, 5] %!assert (fillmissing ([1, NaN, 3, NaN, 5]', @testfcn, 99, 1), [1, 2, 3, 4, 5]') %!assert (fillmissing ([1, NaN, 3, NaN, 5], @testfcn, 99, 2), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', @testfcn, 99, 2), [1, NaN, 3, NaN, 5]') ##known not-compatible. matlab bug ML2022a: [1, 1, 3, 1, 5]' %!assert (fillmissing ([1, NaN, 3, NaN, 5], @testfcn, 99, 3), [1, NaN, 3, NaN, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', @testfcn, 99, 3), [1, NaN, 3, NaN, 5]') %!assert (fillmissing ([1, NaN, NaN, NaN, 5], @testfcn, 3, "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], @testfcn, [1, 1], "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], @testfcn, [1.5, 1.5], "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], @testfcn, 4, "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], @testfcn, [2, 2], "samplepoints", [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) %!assert (fillmissing ([1, NaN, NaN, NaN, 5], @testfcn, 3, "samplepoints", [1, 2, 2.5, 3, 3.5]), [1, 2.6, 3.4, 4.2, 5], 10*eps) %!assert (fillmissing ([NaN, NaN, 3, NaN, 5], @testfcn, 99, 1), [NaN, NaN, 3, NaN, 5]) ##known not-compatible. matlab bug ML2022a: [1, 1, 3, 1, 5] ## Known noncompatible. For move_fcn method, ML2021b (1) ignores windowsize ## for full missing column and processes it anyway, (2) doesn't consider it ## part of endvalues unlike all other methods, (3) ignores samplepoint values ## when calcuating move_fcn results. should review against future versions. %!test %!function A = testfcn (x, y, z) %! if (isempty (y)) %! A = z; %! elseif (numel (y) == 1) %! A = repelem (x(1), numel(z)); %! else %! A = interp1 (y, x, z, "linear", "extrap"); %! endif %!endfunction %! x = reshape ([1:24], 3, 4, 2); %! x([1, 2, 5, 6, 8, 10, 13, 16, 18, 19, 20, 21, 22]) = NaN; %! y = x; %! y([1, 2, 5, 6, 8, 10, 13, 16, 18, 22]) = [3, 3, 4, 4, 8, 11, 14, 17, 17, 23]; %! assert (fillmissing (x, @testfcn, 3), y); %! assert (fillmissing (x, @testfcn, [1, 1]), y); %! assert (fillmissing (x, @testfcn, 3, "endvalues", "extrap"), y); %! assert (fillmissing (x, @testfcn, 3, "samplepoints", [1, 2, 3]), y); %! y= x; %! y(isnan (x)) = 99; %! y(8) = 8; %! assert (fillmissing (x, @testfcn, 3, "endvalues", 99), y) %! y = x; %! y([1, 2, 5, 6, 8, 10, 18, 20, 21]) = [4, 11, 11, 6, 11, 7, 18, 20, 21]; %! assert (fillmissing (x, @testfcn, 3, 2), y); %! assert (fillmissing (x, @testfcn, [1, 1], 2), y); %! assert (fillmissing (x, @testfcn, 3, 2, "endvalues", "extrap"), y); %! assert (fillmissing (x, @testfcn, 3, 2, "samplepoints", [1, 2, 3, 4]), y); %! y(1) = NaN; %! y([6, 18, 21]) = [9, 24, 24]; %! assert (fillmissing (x, @testfcn, 3, 2, "samplepoints", [0, 2, 3, 4]), y); %! y = x; %! y([1, 2, 5, 6, 10, 13, 16, 18, 19, 20, 21, 22]) = 99; %! y(8) = 8; %! assert (fillmissing (x, @testfcn, 3, "endvalues", 99), y); %! y([6, 18, 20, 21]) = [6, 18, 20, 21]; %! y(8) = 99; %! assert (fillmissing (x, @testfcn, 3, 2, "endvalues", 99), y); %! y([6, 18, 20, 21]) = 99; %! assert (fillmissing (x, @testfcn, 3, 3, "endvalues", 99), y); ## Test maxgap for mid and end points %!assert (fillmissing ([1, 2, 3], "constant", 0, "maxgap", 1), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], "constant", 0, "maxgap", 99), [1, 2, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 0, "maxgap", 1), [1, NaN, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 0, "maxgap", 1.999), [1, NaN, 3]) %!assert (fillmissing ([1, NaN, 3], "constant", 0, "maxgap", 2), [1, 0, 3]) %!assert (fillmissing ([1, NaN, NaN, 4], "constant", 0, "maxgap", 2), [1, NaN, NaN, 4]) %!assert (fillmissing ([1, NaN, NaN, 4], "constant", 0, "maxgap", 3), [1, 0, 0, 4]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "constant", 0, "maxgap", 2), [1, 0, 3, 0, 5]) %!assert (fillmissing ([NaN, 2, NaN], "constant", 0, "maxgap", 0.999), [NaN, 2, NaN]) %!assert (fillmissing ([NaN, 2, NaN], "constant", 0, "maxgap", 1), [0, 2, 0]) %!assert (fillmissing ([NaN, 2, NaN, NaN], "constant", 0, "maxgap", 1), [0, 2, NaN, NaN]) %!assert (fillmissing ([NaN, 2, NaN, NaN], "constant", 0, "maxgap", 2), [0, 2, 0, 0]) %!assert (fillmissing ([NaN, NaN, NaN], "constant", 0, "maxgap", 1), [NaN, NaN, NaN]) %!assert (fillmissing ([NaN, NaN, NaN], "constant", 0, "maxgap", 3), [NaN, NaN, NaN]) %!assert (fillmissing ([NaN, NaN, NaN], "constant", 0, "maxgap", 999), [NaN, NaN, NaN]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "constant", 0, "maxgap", 2, "samplepoints", [0, 1, 2, 3, 5]), [1, 0, 3, NaN, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5]', "constant", 0, "maxgap", 2, "samplepoints", [0, 1, 2, 3, 5]), [1, 0, 3, NaN, 5]') %!assert (fillmissing ([1, NaN, 3, NaN, 5], "constant", 0, "maxgap", 2, "samplepoints", [0, 2, 3, 4, 5]), [1, NaN, 3, 0, 5]) %!assert (fillmissing ([1, NaN, 3, NaN, 5; 1, NaN, 3, NaN, 5], "constant", 0, 2, "maxgap", 2, "samplepoints", [0, 2, 3, 4, 5]), [1, NaN, 3, 0, 5; 1, NaN, 3, 0, 5]) %!test %! x = cat (3, [1, 2, NaN; 4, NaN, NaN], [NaN, 2, 3; 4, 5, NaN]); %! assert (fillmissing (x, "constant", 0, "maxgap", 0.1), x); %! y = x; %! y([4, 7, 12]) = 0; %! assert (fillmissing (x, "constant", 0, "maxgap", 1), y); %! assert (fillmissing (x, "constant", 0, 1, "maxgap", 1), y); %! y = x; %! y([5, 7, 12]) = 0; %! assert (fillmissing (x, "constant", 0, 2, "maxgap", 1), y); %! y = x; %! y([4, 5, 7]) = 0; %! assert (fillmissing (x, "constant", 0, 3, "maxgap", 1), y); ## 2nd output ## verify consistent with dim %!test %! x = cat (3, [1, 2, NaN; 4, NaN, NaN], [NaN, 2, 3; 4, 5, NaN]); %! [~, idx] = fillmissing (x, "constant", 0, "maxgap", 1); %! assert (idx, logical (cat (3, [0, 0, 0; 0, 1, 0], [1, 0, 0; 0, 0, 1]))); %! [~, idx] = fillmissing (x, "constant", 0, 1, "maxgap", 1); %! assert (idx, logical (cat (3, [0, 0, 0; 0, 1, 0], [1, 0, 0; 0, 0, 1]))); %! [~, idx] = fillmissing (x, "constant", 0, 2, "maxgap", 1); %! assert (idx, logical (cat (3, [0, 0, 1; 0, 0, 0], [1, 0, 0; 0, 0, 1]))); %! [~, idx] = fillmissing (x, "constant", 0, 3, "maxgap", 1); %! assert (idx, logical (cat (3, [0, 0, 1; 0, 1, 0], [1, 0, 0; 0, 0, 0]))); ## Verify idx matches when methods leave gaps unfilled, or when fill looks ## the same. %!test %! x = [NaN, 2, 3]; %! [~, idx] = fillmissing (x, "previous"); %! assert (idx, logical ([0, 0, 0])); %! [~, idx] = fillmissing (x, "movmean", 1); %! assert (idx, logical ([0, 0, 0])); %! x = [1:3; 4:6; 7:9]; %! x([2, 4, 7, 9]) = NaN; %! [~, idx] = fillmissing (x, "linear"); %! assert (idx, logical ([0, 1, 0; 1, 0, 0; 0, 0, 0])); %! [~, idx] = fillmissing (x, "movmean", 2); %! assert (idx, logical ([0, 0, 0; 1, 0, 0; 0, 0, 1])); %! [A, idx] = fillmissing ([1, 2, 3, NaN, NaN], "movmean",2); %! assert (A, [1, 2, 3, 3, NaN]); %! assert (idx, logical ([0, 0, 0, 1, 0])); %! [A, idx] = fillmissing ([1, 2, 3, NaN, NaN], "movmean",3); %! assert (A, [1, 2, 3, 3, NaN]); %! assert (idx, logical ([0, 0, 0, 1, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, NaN, NaN], "movmedian", 2); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, 3, NaN, NaN], "movmedian", 3); %! assert (A, [1, 2, 3, 3, NaN]); %! assert (idx, logical ([0, 0, 0, 1, 0])); %! [A, idx] = fillmissing ([1, NaN, 1, NaN, 1], @(x,y,z) z, 3); %! assert (A, [1, 2, 1, 4, 1]); %! assert (idx, logical ([0, 1, 0, 1, 0])); %! [A, idx] = fillmissing ([1, NaN, 1, NaN, 1], @(x,y,z) NaN (size (z)), 3); %! assert (A, [1, NaN, 1, NaN, 1]); %! assert (idx, logical ([0, 0, 0, 0, 0])); ## Test missinglocations %!assert (fillmissing ([1, 2, 3], "constant", 99, "missinglocations", logical ([0, 0, 0])), [1, 2, 3]) %!assert (fillmissing ([1, 2, 3], "constant", 99, "missinglocations", logical ([1, 1, 1])), [99, 99, 99]) %!assert (fillmissing ([1, NaN, 2, 3, NaN], "constant", 99, "missinglocations", logical ([1, 0, 1, 0, 1])), [99, NaN, 99, 3, 99]) %!assert (fillmissing ([1, NaN, 3, NaN, 5], "constant", NaN, "missinglocations", logical ([0, 1, 1, 1, 0])), [1, NaN, NaN, NaN, 5]) %!assert (fillmissing (["foo "; " bar"], "constant", "X", "missinglocations", logical ([0, 0, 0, 0; 0, 0, 0, 0])), ["foo "; " bar"]) %!assert (fillmissing (["foo "; " bar"], "constant", "X", "missinglocations", logical ([1, 0, 1, 0; 0, 1, 1, 0])), ["XoX "; " XXr"]) %!assert (fillmissing ({"foo", "", "bar"}, "constant", "X", "missinglocations", logical ([0, 0, 0])), {"foo", "", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "constant", "X", "missinglocations", logical ([1, 1, 0])), {"X", "X", "bar"}) %!test %! [~, idx] = fillmissing ([1, NaN, 3, NaN, 5], "constant", NaN); %! assert (idx, logical ([0, 0, 0, 0, 0])); %! [~, idx] = fillmissing ([1 NaN 3 NaN 5], "constant", NaN, "missinglocations", logical ([0, 1, 1, 1, 0])); %! assert (idx, logical ([0, 1, 1, 1, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, 1, NaN], "movmean", 3.1, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, NaN, NaN], "movmean", 2, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, 1, NaN], "movmean", 3, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, NaN, NaN], "movmean", 3, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, NaN, NaN], "movmedian", 2, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, NaN, NaN], "movmedian", 3, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, 2, NaN, NaN, NaN], "movmedian", 3.1, "missinglocations", logical ([0, 0, 1, 1, 0])); %! assert (A, [1, 2, 2, NaN, NaN]); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [A, idx] = fillmissing ([1, NaN, 1, NaN, 1], @(x,y,z) ones (size (z)), 3, "missinglocations", logical ([0, 1, 0, 1, 1])); %! assert (A, [1, 1, 1, 1, 1]); %! assert (idx, logical ([0, 1, 0, 1, 1])); %! [A, idx] = fillmissing ([1, NaN, 1, NaN, 1], @(x,y,z) NaN (size (z)), 3, "missinglocations", logical ([0, 1, 0, 1, 1])); %! assert (A, [1, NaN, 1, NaN, NaN]); %! assert (idx, logical ([0, 0, 0, 0, 0])); %!test %! [A, idx] = fillmissing ([1, 2, 5], "movmedian", 3, "missinglocations", logical ([0, 1, 0])); %! assert (A, [1, 3, 5]); %! assert (idx, logical ([0, 1, 0])); ## Test char and cellstr %!assert (fillmissing (" foo bar ", "constant", "X"), "XfooXbarX") %!assert (fillmissing ([" foo"; "bar "], "constant", "X"), ["Xfoo"; "barX"]) %!assert (fillmissing ([" foo"; "bar "], "next"), ["bfoo"; "bar "]) %!assert (fillmissing ([" foo"; "bar "], "next", 1), ["bfoo"; "bar "]) %!assert (fillmissing ([" foo"; "bar "], "previous"), [" foo"; "baro"]) %!assert (fillmissing ([" foo"; "bar "], "previous", 1), [" foo"; "baro"]) %!assert (fillmissing ([" foo"; "bar "], "nearest"), ["bfoo"; "baro"]) %!assert (fillmissing ([" foo"; "bar "], "nearest", 1), ["bfoo"; "baro"]) %!assert (fillmissing ([" foo"; "bar "], "next", 2), ["ffoo"; "bar "]) %!assert (fillmissing ([" foo"; "bar "], "previous", 2), [" foo"; "barr"]) %!assert (fillmissing ([" foo"; "bar "], "nearest", 2), ["ffoo"; "barr"]) %!assert (fillmissing ([" foo"; "bar "], "next", 3), [" foo"; "bar "]) %!assert (fillmissing ([" foo"; "bar "], "previous", 3), [" foo"; "bar "]) %!assert (fillmissing ([" foo"; "bar "], "nearest", 3), [" foo"; "bar "]) %!assert (fillmissing ({"foo", "bar"}, "constant", "a"), {"foo", "bar"}) %!assert (fillmissing ({"foo", "bar"}, "constant", {"a"}), {"foo", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "constant", "a"), {"foo", "a", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "constant", {"a"}), {"foo", "a", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "previous"), {"foo", "foo", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "next"), {"foo", "bar", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "nearest"), {"foo", "bar", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "previous", 2), {"foo", "foo", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "next", 2), {"foo", "bar", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "nearest", 2), {"foo", "bar", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "previous", 1), {"foo", "", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "previous", 1), {"foo", "", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "next", 1), {"foo", "", "bar"}) %!assert (fillmissing ({"foo", "", "bar"}, "nearest", 1), {"foo", "", "bar"}) %!assert (fillmissing ("abc ", @(x,y,z) x+y+z, 2), "abcj") %!assert (fillmissing ({"foo", "", "bar"}, @(x,y,z) x(1), 3), {"foo", "foo", "bar"}) %!test %! [A, idx] = fillmissing (" a b c", "constant", " "); %! assert (A, " a b c"); %! assert (idx, logical ([0, 0, 0, 0, 0, 0])); %! [A, idx] = fillmissing ({"foo", "", "bar", ""}, "constant", ""); %! assert (A, {"foo", "", "bar", ""}); %! assert (idx, logical ([0, 0, 0, 0])); %! [A, idx] = fillmissing ({"foo", "", "bar", ""}, "constant", {""}); %! assert (A, {"foo", "", "bar", ""}); %! assert (idx, logical ([0, 0, 0, 0])); %! [A,idx] = fillmissing (" f o o ", @(x,y,z) repelem ("a", numel (z)), 3); %! assert (A, "afaoaoa"); %! assert (idx, logical ([1, 0, 1, 0, 1, 0, 1])); %! [A,idx] = fillmissing (" f o o ", @(x,y,z) repelem (" ", numel (z)), 3); %! assert (A, " f o o "); %! assert (idx, logical ([0, 0, 0, 0, 0, 0, 0])); %! [A,idx] = fillmissing ({"", "foo", ""}, @(x,y,z) repelem ({"a"}, numel (z)), 3); %! assert (A, {"a", "foo", "a"}); %! assert (idx, logical ([1, 0, 1])); %! [A,idx] = fillmissing ({"", "foo", ""}, @(x,y,z) repelem ({""}, numel (z)), 3); %! assert (A, {"", "foo", ""}); %! assert (idx, logical ([0, 0, 0])); ## Types without a defined 'missing' (currently logical, int) that can be filled %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), "constant", true), logical ([1, 0, 1, 0, 1])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), "constant", false, "missinglocations", logical ([1, 0, 1, 0, 1])), logical ([0, 0, 0, 0, 0])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), "previous", "missinglocations", logical ([1, 0, 1, 0, 1])), logical ([1, 0, 0, 0, 0])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), "next", "missinglocations", logical ([1, 0, 1, 0, 1])), logical ([0, 0, 0, 0, 1])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), "nearest", "missinglocations", logical ([1, 0, 1, 0, 1])), logical ([0, 0, 0, 0, 0])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), @(x,y,z) false(size(z)), 3), logical ([1, 0, 1, 0, 1])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), @(x,y,z) false(size(z)), 3, "missinglocations", logical ([1, 0, 1, 0, 1])), logical ([0, 0, 0, 0, 0])) %!assert (fillmissing (logical ([1, 0, 1, 0, 1]), @(x,y,z) false(size(z)), [2, 0], "missinglocations", logical ([1, 0, 1, 0, 1])), logical ([1, 0, 0, 0, 0])) %!test %! x = logical ([1, 0, 1, 0, 1]); %! [~, idx] = fillmissing (x, "constant", true); %! assert (idx, logical ([0, 0, 0, 0, 0])); %! [~, idx] = fillmissing (x, "constant", false, "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, "constant", true, "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, "previous", "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([0, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, "next", "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 0])); %! [~, idx] = fillmissing (x, "nearest", "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, @(x,y,z) false(size(z)), 3); %! assert (idx, logical ([0, 0, 0, 0, 0])) %! [~, idx] = fillmissing (x, @(x,y,z) false(size(z)), 3, "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])) %! [~, idx] = fillmissing (x, @(x,y,z) false(size(z)), [2 0], "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([0, 0, 1, 0, 1])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), "constant", 0), int32 ([1, 2, 3, 4, 5])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), "constant", 0, "missinglocations", logical ([1, 0, 1, 0, 1])), int32 ([0, 2, 0, 4, 0])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), "previous", "missinglocations", logical ([1, 0, 1, 0, 1])), int32 ([1, 2, 2, 4, 4])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), "next", "missinglocations", logical ([1, 0, 1, 0, 1])), int32 ([2, 2, 4, 4, 5])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), "nearest", "missinglocations", logical ([1, 0, 1, 0, 1])), int32 ([2, 2, 4, 4, 4])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), @(x,y,z) z+10, 3), int32 ([1, 2, 3, 4, 5])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), @(x,y,z) z+10, 3, "missinglocations", logical ([1, 0, 1, 0, 1])), int32 ([11, 2, 13, 4, 15])) %!assert (fillmissing (int32 ([1, 2, 3, 4, 5]), @(x,y,z) z+10, [2, 0], "missinglocations", logical ([1, 0, 1, 0, 1])), int32 ([1, 2, 13, 4, 15])) %!test %! x = int32 ([1, 2, 3, 4, 5]); %! [~, idx] = fillmissing (x, "constant", 0); %! assert (idx, logical ([0, 0, 0, 0, 0])); %! [~, idx] = fillmissing (x, "constant", 0, "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, "constant", 3, "missinglocations", logical ([0, 0, 1, 0, 0])); %! assert (idx, logical ([0, 0, 1, 0, 0])); %! [~, idx] = fillmissing (x, "previous", "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([0, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, "next", "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 0])); %! [~, idx] = fillmissing (x, "nearest", "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, @(x,y,z) z+10, 3); %! assert (idx, logical ([0, 0, 0, 0, 0])); %! [~, idx] = fillmissing (x, @(x,y,z) z+10, 3, "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([1, 0, 1, 0, 1])); %! [~, idx] = fillmissing (x, @(x,y,z) z+10, [2 0], "missinglocations", logical ([1, 0, 1, 0, 1])); %! assert (idx, logical ([0, 0, 1, 0, 1])); ## Other data type passthrough %!test %! [A, idx] = fillmissing ([struct, struct], "constant", 1); %! assert (A, [struct, struct]) %! assert (idx, [false, false]) ## Test input validation and error messages %!error fillmissing () %!error fillmissing (1) %!error fillmissing (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) %!error fillmissing (1, 2) %!error fillmissing (1, "foo") %!error fillmissing (1, @(x) x, 1) %!error fillmissing (1, @(x,y) x+y, 1) %!error fillmissing ("a b c", "linear") %!error fillmissing ({"a", "b"}, "linear") %!error <'movmean' and 'movmedian' methods only valid for numeric> fillmissing ("a b c", "movmean", 2) %!error <'movmean' and 'movmedian' methods only valid for numeric> fillmissing ({"a", "b"}, "movmean", 2) %!error <'constant' method must be followed by> fillmissing (1, "constant") %!error fillmissing (1, "constant", []) %!error fillmissing (1, "constant", "a") %!error fillmissing ("a", "constant", 1) %!error fillmissing ("a", "constant", {"foo"}) %!error fillmissing ({"foo"}, "constant", 1) %!error fillmissing (1, "movmean") %!error fillmissing (1, "movmedian") %!error fillmissing (1, "constant", 1, 0) %!error fillmissing (1, "constant", 1, -1) %!error fillmissing (1, "constant", 1, [1, 2]) %!error fillmissing (1, "constant", 1, "samplepoints") %!error fillmissing (1, "constant", 1, "foo") %!error fillmissing (1, "constant", 1, 1, "foo") %!error fillmissing (1, "constant", 1, 2, {1}, 4) %!error fillmissing ([1, 2, 3], "constant", 1, 2, "samplepoints", [1, 2]) %!error fillmissing ([1, 2, 3], "constant", 1, 2, "samplepoints", [3, 1, 2]) %!error fillmissing ([1, 2, 3], "constant", 1, 2, "samplepoints", [1, 1, 2]) %!error fillmissing ([1, 2, 3], "constant", 1, 2, "samplepoints", "abc") %!error fillmissing ([1, 2, 3], "constant", 1, 2, "samplepoints", logical ([1, 1, 1])) %!error fillmissing ([1, 2, 3], "constant", 1, 1, "samplepoints", [1, 2, 3]) %!error fillmissing ("foo", "next", "endvalues", 1) %!error fillmissing (1, "constant", 1, 1, "endvalues", "foo") %!error fillmissing ([1, 2, 3], "constant", 1, 2, "endvalues", [1, 2, 3]) %!error fillmissing ([1, 2, 3], "constant", 1, 1, "endvalues", [1, 2]) %!error fillmissing (randi(5,4,3,2), "constant", 1, 3, "endvalues", [1, 2]) %!error fillmissing (1, "constant", 1, 1, "endvalues", {1}) %!error fillmissing (1, "constant", 1, 2, "foo", 4) %!error fillmissing (struct, "constant", 1, "missinglocations", false) %!error fillmissing (1, "constant", 1, 2, "maxgap", 1, "missinglocations", false) %!error fillmissing (1, "constant", 1, 2, "missinglocations", false, "maxgap", 1) %!error fillmissing (1, "constant", 1, "replacevalues", true) %!error fillmissing (1, "constant", 1, "datavariables", "Varname") %!error fillmissing (1, "constant", 1, 2, "missinglocations", 1) %!error fillmissing (1, "constant", 1, 2, "missinglocations", "a") %!error fillmissing (1, "constant", 1, 2, "missinglocations", [true, false]) %!error fillmissing (true, "linear", "missinglocations", true) %!error fillmissing (int8 (1), "linear", "missinglocations", true) %!error fillmissing (true, "next", "missinglocations", true, "EndValues", "linear") %!error fillmissing (true, "next", "EndValues", "linear", "missinglocations", true) %!error fillmissing (int8 (1), "next", "missinglocations", true, "EndValues", "linear") %!error fillmissing (int8 (1), "next", "EndValues", "linear", "missinglocations", true) %!error fillmissing (1, "constant", 1, 2, "maxgap", true) %!error fillmissing (1, "constant", 1, 2, "maxgap", "a") %!error fillmissing (1, "constant", 1, 2, "maxgap", [1, 2]) %!error fillmissing (1, "constant", 1, 2, "maxgap", 0) %!error fillmissing (1, "constant", 1, 2, "maxgap", -1) %!error fillmissing ([1, 2, 3], "constant", [1, 2, 3]) %!error fillmissing ([1, 2, 3]', "constant", [1, 2, 3]) %!error fillmissing ([1, 2, 3]', "constant", [1, 2, 3], 1) %!error fillmissing ([1, 2, 3], "constant", [1, 2, 3], 2) %!error fillmissing (randi (5, 4, 3, 2), "constant", [1, 2], 1) %!error fillmissing (randi (5, 4, 3, 2), "constant", [1, 2], 2) %!error fillmissing (randi (5, 4, 3, 2), "constant", [1, 2], 3) %!error fillmissing (1, @(x,y,z) x+y+z) %!error fillmissing ([1, NaN, 2], @(x,y,z) [1, 2], 2) statistics-release-1.7.3/inst/fishertest.m000066400000000000000000000215401475240274700206630ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/OR ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, OR (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} fishertest (@var{x}) ## @deftypefnx {statistics} {@var{h} =} fishertest (@var{x}, @var{param1}, @var{value1}, @dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} fishertest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{stats}] =} fishertest (@dots{}) ## ## Fisher's exact test. ## ## @code{@var{h} = fishertest (@var{x})} performs Fisher's exact test on a ## @math{2x2} contingency table given in matrix @var{x}. This is a test of the ## hypothesis that there are no non-random associations between the two 2-level ## categorical variables in @var{x}. @code{fishertest} returns the result of ## the tested hypothsis in @var{h}. @var{h} = 0 indicates that the null ## hypothesis (of no association) cannot be rejected at the 5% significance ## level. @var{h} = 1 indicates that the null hypothesis can be rejected at the ## 5% level. @var{x} must contain only non-negative integers. Use the ## @code{crostab} function to generate the contingency table from samples of two ## categorical variables. Fisher's exact test is not suitable when all integers ## in @var{x} are very large. Use can use the Chi-square test in this case. ## ## @code{[@var{h}, @var{pval}] = fishertest (@var{x})} returns the p-value in ## @var{pval}. That is the probability of observing the given result, or one ## more extreme, by chance if the null hypothesis is true. Small values of ## @var{pval} cast doubt on the validity of the null hypothesis. ## ## @code{[@var{p}, @var{pval}, @var{stats}] = fishertest (@dots{})} returns the ## structure @var{stats} with the following fields: ## ## @multitable @columnfractions 0.05 0.3 0.65 ## @item @tab @qcode{OddsRatio} @tab -- the odds ratio ## @item @tab @qcode{ConfidenceInterval} @tab -- the asymptotic confidence ## interval for the odds ratio. If any of the four entries in the contingency ## table @var{x} is zero, the confidence interval will not be computed, and ## @qcode{[-Inf Inf]} will be displayed. ## @end multitable ## ## @code{[@dots{}] = fishertest (@dots{}, @var{name}, @var{value}, @dots{})} ## specifies one or more of the following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"alpha"} @tab the significance level. Default is 0.05. ## ## @item @tab @qcode{"tail"} @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.1 0.25 0.65 ## @item @tab @qcode{"both"} @tab odds ratio not equal to 1, indicating ## association between two variables (two-tailed test, default) ## @item @tab @qcode{"left"} @tab odds ratio greater than 1 (right-tailed test) ## @item @tab @qcode{"right"} @tab odds ratio is less than 1 (left-tailed test) ## @end multitable ## ## @seealso{crosstab, chi2test, mcnemar_test, ztest2} ## @end deftypefn function [h, p, stats] = fishertest (x, varargin) if (nargin < 1) error ("fishertest: contingency table is missing."); endif if (nargin > 5) error ("fishertest: too many input parameters."); endif ## Check contingency table if (! ismatrix (x) || ndims (x) != 2) error ("fishertest: X must be a 2-dimensional matrix."); endif if (any (x(:) < 0) || any (isnan (x(:))) || any (isinf (x(:))) || ... iscomplex (x) || any (fix (x(:)) != x(:))) error ("fishertest: X must contain only non-negative real integers."); endif if (all (x(:) >= 1e7)) error ("fishertest: cannot handle large entries (>=1e7)."); endif ## Add defaults and parse optional arguments alpha = 0.05; tail = "both"; if (nargin > 1) params = numel (varargin); if ((params / 2) != fix (params / 2)) error ("fishertest: optional arguments must be in Name-Value pairs.") endif for idx = 1:2:params name = varargin{idx}; value = varargin{idx+1}; switch (lower (name)) case "alpha" alpha = value; if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("fishertest: invalid value for alpha."); endif case "tail" tail = value; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("fishertest: invalid value for tail."); endif otherwise error ("fishertest: invalid name for optional arguments."); endswitch endfor endif ## For 2x2 contigency table apply Fisher's exact test ## For larger tables apply the Fisher-Freeman-Halton variance if (all (size (x) == 2)) ## Get margin sums r1 = sum (x(1,:)); r2 = sum (x(2,:)); c1 = sum (x(:,1)); c2 = sum (x(:,2)); sz = sum (x(:)); ## Use try_catch block to avoid memory overflow for large numbers try if (strcmp (tail, "left")) p = hygecdf (x(1,1), sz, r1, c1); else if (min (r1, c1) <= min (r2, c2)) x11 = (0 : min (r1, c1))'; else x22 = (0 : min (r2, c2))'; x12 = c2 - x22; x11 = r1 - x12; endif switch tail case "both" p1 = hygepdf (x11, sz, r1, c1); p2 = hygepdf (x(1,1), sz, r1, c1); p = sum (p1(p1 < p2 + 10 * eps (p2))); case "right" xr = x11(x11 >= x(1,1)); p = sum(hygepdf(xr,sz,r1,c1)); endswitch endif catch error ("fishertest: cannot handle large entries."); end_try_catch ## Return test decision h = (p <= alpha); ## Calculate extra output arguments (if necessary) if (nargout > 2) OR = x(1,1) * x(2,2) / x(1,2) / x(2,1); if (any (x(:) == 0)) CI = [-Inf, Inf]; else SE = sqrt (1 / x(1,1) + 1 / x(1,2) + 1 / x(2,1) + 1 / x(2,2)); LB = OR * exp (-norminv (1 - alpha / 2) * SE); UB = OR * exp (norminv (1 - alpha / 2) * SE); CI = [LB, UB]; endif stats = struct ("OddsRatio", OR, "ConfidenceInterval", CI); endif else error ("fishertest: the Fisher-Freeman-Halton test is not imlemented yet."); endif endfunction %!demo %! ## A Fisher's exact test example %! %! x = [3, 1; 1, 3] %! [h, p, stats] = fishertest(x) ## Test output against MATLAB R2018 %!assert (fishertest ([3, 4; 5, 7]), false); %!assert (isa (fishertest ([3, 4; 5, 7]), "logical"), true); %!test %! [h, pval, stats] = fishertest ([3, 4; 5, 7]); %! assert (pval, 1, 1e-14); %! assert (stats.OddsRatio, 1.05); %! CI = [0.159222057151289, 6.92429189601808]; %! assert (stats.ConfidenceInterval, CI, 1e-14) %!test %! [h, pval, stats] = fishertest ([3, 4; 5, 0]); %! assert (pval, 0.08080808080808080, 1e-14); %! assert (stats.OddsRatio, 0); %! assert (stats.ConfidenceInterval, [-Inf, Inf]) ## Test input validation %!error fishertest (); %!error fishertest (1, 2, 3, 4, 5, 6); %!error ... %! fishertest (ones (2, 2, 2)); %!error ... %! fishertest ([1, 2; -3, 4]); %!error ... %! fishertest ([1, 2; 3, 4+i]); %!error ... %! fishertest ([1, 2; 3, 4.2]); %!error ... %! fishertest ([NaN, 2; 3, 4]); %!error ... %! fishertest ([1, Inf; 3, 4]); %!error ... %! fishertest (ones (2) * 1e8); %!error ... %! fishertest ([1, 2; 3, 4], "alpha", 0); %!error ... %! fishertest ([1, 2; 3, 4], "alpha", 1.2); %!error ... %! fishertest ([1, 2; 3, 4], "alpha", "val"); %!error ... %! fishertest ([1, 2; 3, 4], "tail", "val"); %!error ... %! fishertest ([1, 2; 3, 4], "alpha", 0.01, "tail", "val"); %!error ... %! fishertest ([1, 2; 3, 4], "alpha", 0.01, "badoption", 3); statistics-release-1.7.3/inst/fitcdiscr.m000066400000000000000000000166511475240274700204640ustar00rootroot00000000000000## Copyright (C) 2024 Ruchika Sonagote ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Mdl} =} fitcdiscr (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{Mdl} =} fitcdiscr (@dots{}, @var{name}, @var{value}) ## ## Fit a Linear Discriminant Analysis classification model. ## ## @code{@var{Mdl} = fitcdiscr (@var{X}, @var{Y})} returns a Linear Discriminant ## Analysis (LDA) classification model, @var{Mdl}, with @var{X} being the ## predictor data, and @var{Y} the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of predictor data where rows ## correspond to observations and columns correspond to features or variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can be numerical, logical, ## char array or cell array of character vectors. @var{Y} must have same number ## of rows as @var{X}. ## @end itemize ## ## @code{@var{Mdl} = fitcdiscr (@dots{}, @var{name}, @var{value})} returns a ## Linear Discriminant Analysis model with additional options specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @subheading Model Parameters ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"PredictorNames"} @tab @tab A cell array of character vectors ## specifying the names of the predictors. The length of this array must match ## the number of columns in @var{X}. ## ## @item @qcode{"ResponseName"} @tab @tab A character vector specifying the ## name of the response variable. ## ## @item @qcode{"ClassNames"} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the Discriminant model. @qcode{ClassNames} ## are of the same type as the class labels in @var{Y}. ## ## @item @qcode{"Prior"} @tab @tab A numeric vector specifying the prior ## probabilities for each class. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## Alternatively, you can specify @qcode{"empirical"} to use the empirical ## class probabilities or @qcode{"uniform"} to assume equal class probabilities. ## ## @item @qcode{"Cost"} @tab @tab A @math{NxR} numeric matrix containing ## misclassification cost for the corresponding instances in @var{X} where ## @math{R} is the number of unique categories in @var{Y}. If an instance is ## correctly classified into its category the cost is calculated to be 1, ## otherwise 0. cost matrix can be altered use @code{@var{Mdl.cost} = somecost}. ## default value @qcode{@var{cost} = ones(rows(X),numel(unique(Y)))}. ## ## @item @qcode{"DiscrimType"} @tab @tab A character vector or string scalar ## specifying the type of discriminant analysis to perform. The only supported ## value is @qcode{"linear"}. ## ## @item @qcode{"FillCoeffs"} @tab @tab A character vector or string scalar ## with values @qcode{"on"} or @qcode{"off"} specifying whether to fill the ## coefficients after fitting. If set to @qcode{"on"}, the coefficients are ## computed during model fitting, which can be useful for prediction. ## ## @item @qcode{"Gamma"} @tab @tab A numeric scalar specifying the ## regularization parameter for the covariance matrix. It adjusts the linear ## discriminant analysis to make the model more stable in the presence of ## multicollinearity or small sample sizes. A value of 0 corresponds to no ## regularization, while a value of 1 corresponds to ## a completely regularized model. ## ## @end multitable ## @seealso{ClassificationDiscriminant} ## @end deftypefn function obj = fitcdiscr (X, Y, varargin) ## Check input parameters if (nargin < 2) error ("fitcdiscr: too few arguments."); endif if (mod (nargin, 2) != 0) error ("fitcdiscr: name-value arguments must be in pairs."); endif ## Check predictor data and labels have equal rows if (rows (X) != rows (Y)) error ("fitcdiscr: number of rows in X and Y must be equal."); endif ## Parse arguments to class def function obj = ClassificationDiscriminant (X, Y, varargin{:}); endfunction %!demo %! ## Train a linear discriminant classifier for Gamma = 0.5 %! ## and plot the decision boundaries. %! %! load fisheriris %! idx = ! strcmp (species, "setosa"); %! X = meas(idx,3:4); %! Y = cast (strcmpi (species(idx), "virginica"), "double"); %! obj = fitcdiscr (X, Y, "Gamma", 0.5) %! x1 = [min(X(:,1)):0.03:max(X(:,1))]; %! x2 = [min(X(:,2)):0.02:max(X(:,2))]; %! [x1G, x2G] = meshgrid (x1, x2); %! XGrid = [x1G(:), x2G(:)]; %! pred = predict (obj, XGrid); %! gidx = logical (str2num (cell2mat (pred))); %! %! figure %! scatter (XGrid(gidx,1), XGrid(gidx,2), "markerfacecolor", "magenta"); %! hold on %! scatter (XGrid(!gidx,1), XGrid(!gidx,2), "markerfacecolor", "red"); %! plot (X(Y == 0, 1), X(Y == 0, 2), "ko", X(Y == 1, 1), X(Y == 1, 2), "kx"); %! xlabel ("Petal length (cm)"); %! ylabel ("Petal width (cm)"); %! title ("Linear Discriminant Analysis Decision Boundary"); %! legend ({"Versicolor Region", "Virginica Region", ... %! "Sampled Versicolor", "Sampled Virginica"}, ... %! "location", "northwest") %! axis tight %! hold off ## Tests %!test %! load fisheriris %! Mdl = fitcdiscr (meas, species, "Gamma", 0.5); %! [label, score, cost] = predict (Mdl, [2, 2, 2, 2]); %! assert (label, {'versicolor'}) %! assert (score, [0, 0.9999, 0.0001], 1e-4) %! assert (cost, [1, 0.0001, 0.9999], 1e-4) %! [label, score, cost] = predict (Mdl, [2.5, 2.5, 2.5, 2.5]); %! assert (label, {'versicolor'}) %! assert (score, [0, 0.6368, 0.3632], 1e-4) %! assert (cost, [1, 0.3632, 0.6368], 1e-4) %! assert (class (Mdl), "ClassificationDiscriminant"); %! assert ({Mdl.X, Mdl.Y, Mdl.NumObservations}, {meas, species, 150}) %! assert ({Mdl.DiscrimType, Mdl.ResponseName}, {"linear", "Y"}) %! assert ({Mdl.Gamma, Mdl.MinGamma}, {0.5, 0}) %! assert (Mdl.ClassNames, unique (species)) %! sigma = [0.265008, 0.046361, 0.083757, 0.019201; ... %! 0.046361, 0.115388, 0.027622, 0.016355; ... %! 0.083757, 0.027622, 0.185188, 0.021333; ... %! 0.019201, 0.016355, 0.021333, 0.041882]; %! assert (Mdl.Sigma, sigma, 1e-6) %! mu = [5.0060, 3.4280, 1.4620, 0.2460; ... %! 5.9360, 2.7700, 4.2600, 1.3260; ... %! 6.5880, 2.9740, 5.5520, 2.0260]; %! assert (Mdl.Mu, mu, 1e-14) %! assert (Mdl.LogDetSigma, -8.6884, 1e-4) ## Test input validation %!error fitcdiscr () %!error fitcdiscr (ones (4,1)) %!error %! fitcdiscr (ones (4,2), ones (4, 1), "K") %!error %! fitcdiscr (ones (4,2), ones (3, 1)) %!error %! fitcdiscr (ones (4,2), ones (3, 1), "K", 2) statistics-release-1.7.3/inst/fitcgam.m000066400000000000000000000236441475240274700201240ustar00rootroot00000000000000## Copyright (C) 2024 Ruchika Sonagote ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Mdl} =} fitcgam (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{Mdl} =} fitcgam (@dots{}, @var{name}, @var{value}) ## ## Fit a Generalized Additive Model (GAM) for binary classification. ## ## @code{@var{Mdl} = fitcgam (@var{X}, @var{Y})} returns a a GAM classification ## model, @var{Mdl}, with @var{X} being the predictor data, and @var{Y} the ## binary class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of predictor data where rows ## correspond to observations and columns correspond to features or variables. ## @item ## @code{Y} is @math{Nx1} numeric vector containing binary class labels, ## typically 0 or 1. ## @end itemize ## ## @code{@var{Mdl} = fitcgam (@dots{}, @var{name}, @var{value})} returns a ## GAM classification model with additional options specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @subheading Model Parameters ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"PredictorNames"} @tab @tab A cell array of character vectors ## specifying the names of the predictors. The length of this array must match ## the number of columns in @var{X}. ## ## @item @qcode{"ResponseName"} @tab @tab A character vector specifying the ## name of the response variable. ## ## @item @qcode{"ClassNames"} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the Discriminant model. @qcode{ClassNames} ## are of the same type as the class labels in @var{Y}. ## ## @item @qcode{"Cost"} @tab @tab A @math{NxR} numeric matrix containing ## misclassification cost for the corresponding instances in @var{X} where ## @math{R} is the number of unique categories in @var{Y}. If an instance is ## correctly classified into its category the cost is calculated to be 1, ## otherwise 0. cost matrix can be altered use @code{@var{Mdl.cost} = somecost}. ## default value @qcode{@var{cost} = ones(rows(X),numel(unique(Y)))}. ## ## @item @qcode{"Formula"} @tab @tab A model specification given as a string in ## the form @qcode{"Y ~ terms"} where @qcode{Y} represents the reponse variable ## and @qcode{terms} the predictor variables. The formula can be used to ## specify a subset of variables for training model. For example: ## @qcode{"Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3"} specifies four linear terms ## for the first four columns of for predictor data, and @qcode{x1:x2} and ## @qcode{x2:x3} specify the two interaction terms for 1st-2nd and 3rd-4th ## columns respectively. Only these terms will be used for training the model, ## but @var{X} must have at least as many columns as referenced in the formula. ## If Predictor Variable names have been defined, then the terms in the formula ## must reference to those. When @qcode{"formula"} is specified, all terms used ## for training the model are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object as a matrix containing the column indexes for each ## term including both the predictors and the interactions used. ## ## @item @qcode{"Interactions"} @tab @tab A logical matrix, a positive integer ## scalar, or the string @qcode{"all"} for defining the interactions between ## predictor variables. When given a logical matrix, it must have the same ## number of columns as @var{X} and each row corresponds to a different ## interaction term combining the predictors indexed as @qcode{true}. Each ## interaction term is appended as a column vector after the available predictor ## column in @var{X}. When @qcode{"all"} is defined, then all possible ## combinations of interactions are appended in @var{X} before training. At the ## moment, parsing a positive integer has the same effect as the @qcode{"all"} ## option. When @qcode{"interactions"} is specified, only the interaction terms ## appended to @var{X} are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object. ## ## @item @qcode{"Knots"} @tab @tab A scalar or a row vector with the same ## columns as @var{X}. It defines the knots for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 5, hence expanded to @qcode{ones (1, columns (X)) * 5}. You can ## parse a row vector with different number of knots for each predictor ## variable to be fitted with, although not recommended. ## ## @item @qcode{"Order"} @tab @tab A scalar or a row vector with the same ## columns as @var{X}. It defines the order of the polynomial when training the ## GAM. As a scalar, it is expanded to a row vector. The default values is 3, ## hence expanded to @qcode{ones (1, columns (X)) * 3}. You can parse a row ## vector with different number of polynomial order for each predictor variable ## to be fitted with, although not recommended. ## ## @item @qcode{"DoF"} @tab @tab A scalar or a row vector with the same columns ## as @var{X}. It defines the degrees of freedom for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 8, hence expanded to @qcode{ones (1, columns (X)) * 8}. You can ## parse a row vector with different degrees of freedom for each predictor ## variable to be fitted with, although not recommended. ## ## @end multitable ## You can parse either a @qcode{"Formula"} or an @qcode{"Interactions"} ## optional parameter. Parsing both parameters will result an error. ## Accordingly, you can only pass up to two parameters among @qcode{"Knots"}, ## @qcode{"Order"}, and @qcode{"DoF"} to define the required polynomial for ## training the GAM model. ## ## @seealso{ClassificationGAM} ## @end deftypefn function obj = fitcgam (X, Y, varargin) ## Check input parameters if (nargin < 2) error ("fitcgam: too few arguments."); endif if (mod (nargin, 2) != 0) error ("fitcgam: name-value arguments must be in pairs."); endif ## Check predictor data and labels have equal rows if (rows (X) != rows (Y)) error ("fitcgam: number of rows in X and Y must be equal."); endif ## Parse arguments to class def function obj = ClassificationGAM (X, Y, varargin{:}); endfunction ## Demo %!demo %! ## Train a GAM classifier for binary classification %! ## using specific data and plot the decision boundaries. %! %! ## Define specific data %! X = [1, 2; 2, 3; 3, 3; 4, 5; 5, 5; ... %! 6, 7; 7, 8; 8, 8; 9, 9; 10, 10]; %! Y = [0; 0; 0; 0; 0; ... %! 1; 1; 1; 1; 1]; %! %! ## Train the GAM model %! obj = fitcgam (X, Y, "Interactions", "all"); %! %! ## Create a grid of values for prediction %! x1 = [min(X(:,1)):0.1:max(X(:,1))]; %! x2 = [min(X(:,2)):0.1:max(X(:,2))]; %! [x1G, x2G] = meshgrid (x1, x2); %! XGrid = [x1G(:), x2G(:)]; %! pred = predict (obj, XGrid); %! %! ## Plot decision boundaries and data points %! predNumeric = str2double (pred); %! gidx = predNumeric > 0.5; %! %! figure %! scatter(XGrid(gidx,1), XGrid(gidx,2), "markerfacecolor", "magenta"); %! hold on %! scatter(XGrid(!gidx,1), XGrid(!gidx,2), "markerfacecolor", "red"); %! plot(X(Y == 0, 1), X(Y == 0, 2), "ko", X(Y == 1, 1), X(Y == 1, 2), "kx"); %! xlabel("Feature 1"); %! ylabel("Feature 2"); %! title("Generalized Additive Model (GAM) Decision Boundary"); %! legend({"Class 1 Region", "Class 0 Region", ... %! "Class 1 Samples", "Class 0 Samples"}, ... %! "location", "northwest") %! axis tight %! hold off ## Tests %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [0; 0; 1; 1]; %! PredictorNames = {'Feature1', 'Feature2', 'Feature3'}; %! a = fitcgam (x, y, "PredictorNames", PredictorNames); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {x, y, 4}) %! assert ({a.NumPredictors, a.ResponseName}, {3, "Y"}) %! assert (a.ClassNames, {'0'; '1'}) %! assert (a.PredictorNames, PredictorNames) %! assert (a.BaseModel.Intercept, 0) %!test %! x = [1, 2; 3, 4; 5, 6; 7, 8; 9, 10]; %! y = [1; 0; 1; 0; 1]; %! a = fitcgam (x, y, "interactions", "all"); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {x, y, 5}) %! assert ({a.NumPredictors, a.ResponseName}, {2, "Y"}) %! assert (a.ClassNames, {'1'; '0'}) %! assert (a.PredictorNames, {'x1', 'x2'}) %! assert (a.ModelwInt.Intercept, 0.4055, 1e-1) %!test %! load fisheriris %! inds = strcmp (species,'versicolor') | strcmp (species,'virginica'); %! X = meas(inds, :); %! Y = species(inds, :)'; %! Y = strcmp (Y, 'virginica')'; %! a = fitcgam (X, Y, 'Formula', 'Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3'); %! assert (class (a), "ClassificationGAM"); %! assert ({a.X, a.Y, a.NumObservations}, {X, Y, 100}) %! assert ({a.NumPredictors, a.ResponseName}, {4, "Y"}) %! assert (a.ClassNames, {'0'; '1'}) %! assert (a.Formula, 'Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3') %! assert (a.PredictorNames, {'x1', 'x2', 'x3', 'x4'}) %! assert (a.ModelwInt.Intercept, 0) ## Test input validation %!error fitcgam () %!error fitcgam (ones (4,1)) %!error %! fitcgam (ones (4,2), ones (4, 1), "K") %!error %! fitcgam (ones (4,2), ones (3, 1)) %!error %! fitcgam (ones (4,2), ones (3, 1), "K", 2) statistics-release-1.7.3/inst/fitcknn.m000066400000000000000000000616751475240274700201540ustar00rootroot00000000000000## Copyright (C) 2023 Mohammed Azmat Khan ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Mdl} =} fitcknn (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{Mdl} =} fitcknn (@dots{}, @var{name}, @var{value}) ## ## Fit a k-Nearest Neighbor classification model. ## ## @code{@var{Mdl} = fitcknn (@var{X}, @var{Y})} returns a k-Nearest Neighbor ## classification model, @var{Mdl}, with @var{X} being the predictor data, and ## @var{Y} the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of predictor data where rows ## correspond to observations and columns correspond to features or variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can be numerical, logical, ## char array or cell array of character vectors. @var{Y} must have same number ## of rows as @var{X}. ## @end itemize ## ## @code{@var{Mdl} = fitcknn (@dots{}, @var{name}, @var{value})} returns a ## k-Nearest Neighbor classification model with additional options specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @subheading Model Parameters ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Standardize"} @tab @tab A boolean flag indicating whether ## the data in @var{X} should be standardized prior to training. ## ## @item @qcode{"PredictorNames"} @tab @tab A cell array of character vectors ## specifying the predictor variable names. The variable names are assumed to ## be in the same order as they appear in the training data @var{X}. ## ## @item @qcode{"ResponseName"} @tab @tab A character vector specifying the name ## of the response variable. ## ## @item @qcode{"ClassNames"} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the kNN model. @qcode{ClassNames} are of ## the same type as the class labels in @var{Y}. ## ## @item @qcode{"Prior"} @tab @tab A numeric vector specifying the prior ## probabilities for each class. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{"Cost"} @tab @tab A @math{NxR} numeric matrix containing ## misclassification cost for the corresponding instances in @var{X} where ## @math{R} is the number of unique categories in @var{Y}. If an instance is ## correctly classified into its category the cost is calculated to be 1, ## otherwise 0. cost matrix can be altered use @code{@var{Mdl.cost} = somecost}. ## default value @qcode{@var{cost} = ones(rows(X),numel(unique(Y)))}. ## ## @item @qcode{"ScoreTransform"} @tab @tab A character vector defining one of ## the following functions or a user defined function handle, which is used ## for transforming the prediction scores returned by the @code{predict} and ## @code{resubPredict} methods. Default value is @qcode{'none'}. ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## @item @tab @qcode{"doublelogit"} @tab @math{1 ./ (1 + exp .^ (-2 * x))} ## @item @tab @qcode{"invlogit"} @tab @math{log (x ./ (1 - x))} ## @item @tab @qcode{"ismax"} @tab Sets the score for the class with the largest ## score to 1, and sets the scores for all other classes to 0 ## @item @tab @qcode{"logit"} @tab @math{1 ./ (1 + exp .^ (-x))} ## @item @tab @qcode{"none"} @tab @math{x} (no transformation) ## @item @tab @qcode{"identity"} @tab @math{x} (no transformation) ## @item @tab @qcode{"sign"} @tab @math{-1 for x < 0, 0 for x = 0, 1 for x > 0} ## @item @tab @qcode{"symmetric"} @tab @math{2 * x + 1} ## @item @tab @qcode{"symmetricismax"} @tab Sets the score for the class with ## the largest score to 1, and sets the scores for all other classes to -1 ## @item @tab @qcode{"symmetriclogit"} @tab @math{2 ./ (1 + exp .^ (-x)) - 1} ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"BreakTies"} @tab @tab Tie-breaking algorithm used by predict ## when multiple classes have the same smallest cost. By default, ties occur ## when multiple classes have the same number of nearest points among the ## @math{k} nearest neighbors. The available options are specified by the ## following character arrays: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"smallest"} @tab This is the default and it favors the ## class with the smallest index among the tied groups, i.e. the one that ## appears first in the training labelled data. ## @item @tab @qcode{"nearest"} @tab This favors the class with the nearest ## neighbor among the tied groups, i.e. the class with the closest member point ## according to the distance metric used. ## @item @tab @qcode{"random"} @tab This randomly picks one class among the ## tied groups. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"BucketSize"} @tab @tab The maximum number of data points in the ## leaf node of the Kd-tree and it must be a positive integer. By default, it ## is 50. This argument is meaningful only when the selected search method is ## @qcode{"kdtree"}. ## ## @item @qcode{"NumNeighbors"} @tab @tab A positive integer value specifying ## the number of nearest neighbors to be found in the kNN search. By default, ## it is 1. ## ## @item @qcode{"Exponent"} @tab @tab A positive scalar (usually an integer) ## specifying the Minkowski distance exponent. This argument is only valid when ## the selected distance metric is @qcode{"minkowski"}. By default it is 2. ## ## @item @qcode{"Scale"} @tab @tab A nonnegative numeric vector specifying the ## scale parameters for the standardized Euclidean distance. The vector length ## must be equal to the number of columns in @var{X}. This argument is only ## valid when the selected distance metric is @qcode{"seuclidean"}, in which ## case each coordinate of @var{X} is scaled by the corresponding element of ## @qcode{"scale"}, as is each query point in @var{Y}. By default, the scale ## parameter is the standard deviation of each coordinate in @var{X}. If a ## variable in @var{X} is constant, i.e. zero variance, this value is forced ## to 1 to avoid division by zero. This is the equivalent of this variable not ## being standardized. ## ## @item @qcode{"Cov"} @tab @tab A square matrix with the same number of columns ## as @var{X} specifying the covariance matrix for computing the mahalanobis ## distance. This must be a positive definite matrix matching. This argument ## is only valid when the selected distance metric is @qcode{"mahalanobis"}. ## ## @item @qcode{"Distance"} @tab @tab is the distance metric used by ## @code{knnsearch} as specified below: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"euclidean"} @tab Euclidean distance. ## @item @tab @qcode{"seuclidean"} @tab standardized Euclidean distance. Each ## coordinate difference between the rows in @var{X} and the query matrix ## @var{Y} is scaled by dividing by the corresponding element of the standard ## deviation computed from @var{X}. To specify a different scaling, use the ## @qcode{"Scale"} name-value argument. ## @item @tab @qcode{"cityblock"} @tab City block distance. ## @item @tab @qcode{"chebychev"} @tab Chebychev distance (maximum coordinate ## difference). ## @item @tab @qcode{"minkowski"} @tab Minkowski distance. The default exponent ## is 2. To specify a different exponent, use the @qcode{"P"} name-value ## argument. ## @item @tab @qcode{"mahalanobis"} @tab Mahalanobis distance, computed using a ## positive definite covariance matrix. To change the value of the covariance ## matrix, use the @qcode{"Cov"} name-value argument. ## @item @tab @qcode{"cosine"} @tab Cosine distance. ## @item @tab @qcode{"correlation"} @tab One minus the sample linear correlation ## between observations (treated as sequences of values). ## @item @tab @qcode{"spearman"} @tab One minus the sample Spearman's rank ## correlation between observations (treated as sequences of values). ## @item @tab @qcode{"hamming"} @tab Hamming distance, which is the percentage ## of coordinates that differ. ## @item @tab @qcode{"jaccard"} @tab One minus the Jaccard coefficient, which is ## the percentage of nonzero coordinates that differ. ## @item @tab @var{@@distfun} @tab Custom distance function handle. A distance ## function of the form @code{function @var{D2} = distfun (@var{XI}, @var{YI})}, ## where @var{XI} is a @math{1xP} vector containing a single observation in ## @math{P}-dimensional space, @var{YI} is an @math{NxP} matrix containing an ## arbitrary number of observations in the same @math{P}-dimensional space, and ## @var{D2} is an @math{NxP} vector of distances, where @qcode{(@var{D2}k)} is ## the distance between observations @var{XI} and @qcode{(@var{YI}k,:)}. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"DistanceWeight"} @tab @tab A distance weighting function, ## specified either as a function handle, which accepts a matrix of nonnegative ## distances and returns a matrix the same size containing nonnegative distance ## weights, or one of the following values: @qcode{"equal"}, which corresponds ## to no weighting; @qcode{"inverse"}, which corresponds to a weight equal to ## @math{1/distance}; @qcode{"squaredinverse"}, which corresponds to a weight ## equal to @math{1/distance^2}. ## ## @item @qcode{"IncludeTies"} @tab @tab A boolean flag to indicate if the ## returned values should contain the indices that have same distance as the ## @math{K^th} neighbor. When @qcode{false}, @code{knnsearch} chooses the ## observation with the smallest index among the observations that have the same ## distance from a query point. When @qcode{true}, @code{knnsearch} includes ## all nearest neighbors whose distances are equal to the @math{K^th} smallest ## distance in the output arguments. To specify @math{K}, use the @qcode{"K"} ## name-value pair argument. ## ## @item @qcode{"NSMethod"} @tab @tab is the nearest neighbor search method used ## by @code{knnsearch} as specified below. ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"kdtree"} @tab Creates and uses a Kd-tree to find nearest ## neighbors. @qcode{"kdtree"} is the default value when the number of columns ## in @var{X} is less than or equal to 10, @var{X} is not sparse, and the ## distance metric is @qcode{"euclidean"}, @qcode{"cityblock"}, ## @qcode{"manhattan"}, @qcode{"chebychev"}, or @qcode{"minkowski"}. Otherwise, ## the default value is @qcode{"exhaustive"}. This argument is only valid when ## the distance metric is one of the four aforementioned metrics. ## @item @tab @qcode{"exhaustive"} @tab Uses the exhaustive search algorithm by ## computing the distance values from all the points in @var{X} to each point in ## @var{Y}. ## @end multitable ## ## @subheading Cross Validation Options ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Crossval"} @tab @tab Cross-validation flag specified as ## @qcode{'on'} or @qcode{'off'}. If @qcode{'on'} is specified, a 10-fold ## cross validation is performed and a @code{ClassificationPartitionedModel} is ## returned in @var{Mdl}. To override this cross-validation setting, use only ## one of the following Name-Value pair arguments. ## ## @item @qcode{"CVPartition"} @tab @tab A @code{cvpartition} object that ## specifies the type of cross-validation and the indexing for the training and ## validation sets. A @code{ClassificationPartitionedModel} is returned in ## @var{Mdl} and the trained model is stored in the @code{Trained} property. ## ## @item @qcode{"Holdout"} @tab @tab Fraction of the data used for holdout ## validation, specified as a scalar value in the range @math{[0,1]}. When ## specified, a randomly selected percentage is reserved as validation data and ## the remaining set is used for training. The trained model is stored in the ## @code{Trained} property of the @code{ClassificationPartitionedModel} returned ## in @var{Mdl}. @qcode{"Holdout"} partitioning attempts to ensure that each ## partition represents the classes proportionately. ## ## @item @qcode{"KFold"} @tab @tab Number of folds to use in the cross-validated ## model, specified as a positive integer value greater than 1. When specified, ## then the data is randomly partitioned in @math{k} sets and for each set, the ## set is reserved as validation data while the remaining @math{k-1} sets are ## used for training. The trained models are stored in the @code{Trained} ## property of the @code{ClassificationPartitionedModel} returned in @var{Mdl}. ## @qcode{"KFold"} partitioning attempts to ensure that each partition ## represents the classes proportionately. ## ## @item @qcode{"Leaveout"} @tab @tab Leave-one-out cross-validation flag ## specified as @qcode{'on'} or @qcode{'off'}. If @qcode{'on'} is specified, ## then for each of the @math{n} observations (where @math{n} is the number of ## observations, excluding missing observations, specified in the ## @code{NumObservations} property of the model), one observation is reserved as ## validation data while the remaining observations are used for training. The ## trained models are stored in the @code{Trained} property of the ## @code{ClassificationPartitionedModel} returned in @var{Mdl}. ## @end multitable ## ## @seealso{ClassificationKNN, ClassificationPartitionedModel, knnsearch, ## rangesearch, pdist2} ## @end deftypefn function Mdl = fitcknn (X, Y, varargin) ## Check input parameters if (nargin < 2) error ("fitcknn: too few arguments."); endif if (mod (nargin, 2) != 0) error ("fitcknn: Name-Value arguments must be in pairs."); endif ## Check predictor data and labels have equal rows if (rows (X) != rows (Y)) error ("fitcknn: number of rows in X and Y must be equal."); endif ## Check optional input parameters for cross-validation options cv_opt = false; cv_arg = 0; args = {}; while (numel (varargin) > 0) switch (tolower (varargin{1})) case 'crossval' CrossVal = varargin{2}; if (! any (strcmp (CrossVal, {'off', 'on'}))) error ("fitcknn: 'CrossVal' must be either 'off' or 'on'."); endif if (strcmp (CrossVal, 'on')) cv_opt = true; endif case 'kfold' Name = 'KFold'; Value = varargin{2}; cv_arg += 1; cv_opt = true; case 'holdout' Name = 'Holdout'; Value = varargin{2}; cv_arg += 1; cv_opt = true; case 'leaveout' Name = 'Holdout'; Value = varargin{2}; cv_arg += 1; cv_opt = true; case 'cvpartition' Name = 'CVPartition'; Value = varargin{2}; cv_arg += 1; cv_opt = true; otherwise args = [args, {varargin{1}, varargin{2}}]; endswitch varargin (1:2) = []; endwhile ## Check for multiple cross-validation paired arguments if (cv_arg > 1) error (strcat (["fitcknn: You can use only one cross-validation"], ... [" name-value pair argument at a time to create a"], ... [" cross-validated model."])); endif ## Parse arguments to class def function Mdl = ClassificationKNN (X, Y, args{:}); ## If cross validation has been requested, ## return a ClassificationPartitionedModel if (cv_opt) if (cv_arg) Mdl = crossval (Mdl, Name, Value); else Mdl = crossval (Mdl); endif endif endfunction %!demo %! ## Train a k-nearest neighbor classifier for k = 10 %! ## and plot the decision boundaries. %! %! load fisheriris %! idx = ! strcmp (species, "setosa"); %! X = meas(idx,3:4); %! Y = cast (strcmpi (species(idx), "virginica"), "double"); %! obj = fitcknn (X, Y, "Standardize", 1, "NumNeighbors", 10, "NSMethod", "exhaustive") %! x1 = [min(X(:,1)):0.03:max(X(:,1))]; %! x2 = [min(X(:,2)):0.02:max(X(:,2))]; %! [x1G, x2G] = meshgrid (x1, x2); %! XGrid = [x1G(:), x2G(:)]; %! pred = predict (obj, XGrid); %! gidx = logical (str2num (cell2mat (pred))); %! %! figure %! scatter (XGrid(gidx,1), XGrid(gidx,2), "markerfacecolor", "magenta"); %! hold on %! scatter (XGrid(!gidx,1), XGrid(!gidx,2), "markerfacecolor", "red"); %! plot (X(Y == 0, 1), X(Y == 0, 2), "ko", X(Y == 1, 1), X(Y == 1, 2), "kx"); %! xlabel ("Petal length (cm)"); %! ylabel ("Petal width (cm)"); %! title ("5-Nearest Neighbor Classifier Decision Boundary"); %! legend ({"Versicolor Region", "Virginica Region", ... %! "Sampled Versicolor", "Sampled Virginica"}, ... %! "location", "northwest") %! axis tight %! hold off ## Test Output %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "NSMethod", "exhaustive"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = fitcknn (x, y, "NumNeighbors" ,k); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = ones (4, 11); %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = fitcknn (x, y, "NumNeighbors" ,k); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = fitcknn (x, y, "NumNeighbors" ,k, "NSMethod", "exhaustive"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! k = 10; %! a = fitcknn (x, y, "NumNeighbors" ,k, "Distance", "hamming"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 10}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "hamming"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! weights = ones (4,1); %! a = fitcknn (x, y, "Standardize", 1); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.Standardize}, {true}) %! assert ({a.Sigma}, {std(x, [], 1)}) %! assert ({a.Mu}, {[3.75, 4.25, 4.75]}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! weights = ones (4,1); %! a = fitcknn (x, y, "Standardize", false); %! assert (class (a), "ClassificationKNN"); %! assert ({a.X, a.Y, a.NumNeighbors}, {x, y, 1}) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.Standardize}, {false}) %! assert ({a.Sigma}, {[]}) %! assert ({a.Mu}, {[]}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! s = ones (1, 3); %! a = fitcknn (x, y, "Scale" , s, "Distance", "seuclidean"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.DistParameter}, {s}) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "seuclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "Exponent" , 5, "Distance", "minkowski"); %! assert (class (a), "ClassificationKNN"); %! assert (a.DistParameter, 5) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "minkowski"}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "Exponent" , 5, "Distance", "minkowski", ... %! "NSMethod", "exhaustive"); %! assert (class (a), "ClassificationKNN"); %! assert (a.DistParameter, 5) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "minkowski"}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "BucketSize" , 20, "distance", "mahalanobis"); %! assert (class (a), "ClassificationKNN"); %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "mahalanobis"}) %! assert ({a.BucketSize}, {20}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "IncludeTies", true); %! assert (class (a), "ClassificationKNN"); %! assert (a.IncludeTies, true); %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y); %! assert (class (a), "ClassificationKNN"); %! assert (a.IncludeTies, false); %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, [0.5; 0.5]) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! prior = [0.5; 0.5]; %! a = fitcknn (x, y, "Prior", "empirical"); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, prior) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "a"; "b"]; %! prior = [0.75; 0.25]; %! a = fitcknn (x, y, "Prior", "empirical"); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, prior) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "a"; "b"]; %! prior = [0.5; 0.5]; %! a = fitcknn (x, y, "Prior", "uniform"); %! assert (class (a), "ClassificationKNN") %! assert (a.Prior, prior) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! cost = eye (2); %! a = fitcknn (x, y, "Cost", cost); %! assert (class (a), "ClassificationKNN") %! assert (a.Cost, [1, 0; 0, 1]) %! assert ({a.NSMethod, a.Distance}, {"kdtree", "euclidean"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! cost = eye (2); %! a = fitcknn (x, y, "Cost", cost, "Distance", "hamming" ); %! assert (class (a), "ClassificationKNN") %! assert (a.Cost, [1, 0; 0, 1]) %! assert ({a.NSMethod, a.Distance}, {"exhaustive", "hamming"}) %! assert ({a.BucketSize}, {50}) %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = ["a"; "a"; "b"; "b"]; %! a = fitcknn (x, y, "NSMethod", "exhaustive", "CrossVal", "on"); %! assert (class (a), "ClassificationPartitionedModel"); %! assert ({a.X, a.Y, a.Trained{1}.NumNeighbors}, {x, y, 1}) %! assert (a.ModelParameters.NSMethod, "exhaustive") %! assert (a.ModelParameters.Distance, "euclidean") %! assert ({a.Trained{1}.BucketSize}, {50}) ## Test input validation %!error fitcknn () %!error fitcknn (ones (4,1)) %!error %! fitcknn (ones (4,2), ones (4, 1), "K") %!error %! fitcknn (ones (4,2), ones (3, 1)) %!error %! fitcknn (ones (4,2), ones (3, 1), "K", 2) %!error %! fitcknn (ones (4,2), ones (4, 1), "CrossVal", 2) %!error %! fitcknn (ones (4,2), ones (4, 1), "CrossVal", 'a') %!error ... %! fitcknn (ones (4,2), ones (4, 1), "KFold", 10, "Holdout", 0.3) statistics-release-1.7.3/inst/fitcnet.m000066400000000000000000000163741475240274700201500ustar00rootroot00000000000000## Copyright (C) 2024 Pallav Purbia ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Mdl} =} fitcnet (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{Mdl} =} fitcnet (@dots{}, @var{name}, @var{value}) ## ## Fit a Neural Network classification model. ## ## @code{@var{Mdl} = fitcnet (@var{X}, @var{Y})} returns a Neural Network ## classification model, @var{Mdl}, with @var{X} being the predictor data, and ## @var{Y} the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of predictor data where rows ## correspond to observations and columns correspond to features or variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can contain any type of ## categorical data. @var{Y} must have same numbers of rows as @var{X}. ## @end itemize ## ## @code{@var{Mdl} = fitcnet (@dots{}, @var{name}, @var{value})} returns a ## Neural Network classification model with additional options specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @subheading Model Parameters ## ## @multitable @columnfractions 0.32 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Standardize"} @tab @tab A boolean flag indicating whether ## the data in @var{X} should be standardized prior to training. ## ## @item @qcode{"PredictorNames"} @tab @tab A cell array of character vectors ## specifying the predictor variable names. The variable names are assumed to ## be in the same order as they appear in the training data @var{X}. ## ## @item @qcode{"ResponseName"} @tab @tab A character vector specifying the name ## of the response variable. ## ## @item @qcode{"ClassNames"} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the Neural Network model. ## @qcode{ClassNames} are of the same type as the class labels in @var{Y}. ## ## @item @qcode{"Prior"} @tab @tab A numeric vector specifying the prior ## probabilities for each class. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{"LayerSizes"} @tab @tab A vector of positive integers that ## defines the sizes of the fully connected layers in the neural network model. ## Each element in LayerSizes corresponds to the number of outputs for the ## respective fully connected layer in the neural network model. ## The default value is 10. ## ## @item @qcode{"LearningRate"} @tab @tab A positive scalar value that defines ## the learning rate during the gradient descent. Default value is 0.01. ## ## @item @qcode{"Activations"} @tab @tab A character vector or a cellstr vector ## specifying the activation functions for the hidden layers of the neural ## network (excluding the output layer). The available activation functions are ## @qcode{'linear'}, @qcode{'sigmoid'}, @qcode{'tanh'}, @qcode{'sigmoid'}, and ## @qcode{'none'}. The default value is @qcode{'sigmoid'}. ## ## @item @qcode{"OutputLayerActivation"} @tab @tab A character vector specifying ## the activation function for the output layer of the neural network. The ## available activation functions are @qcode{'linear'}, @qcode{'sigmoid'}, ## @qcode{'tanh'}, @qcode{'sigmoid'}, and @qcode{'none'}. The default value is ## @qcode{'sigmoid'}. ## ## @item @qcode{"IterationLimit"} @tab @tab A positive integer scalar that ## specifies the maximum number of training iterations. The default value is ## 1000. ## ## @item @qcode{"DisplayInfo"} @tab @tab A boolean flag indicating whether to ## print information during training. Default is @qcode{false}. ## ## @item @qcode{"ScoreTransform"} @tab @tab A character vector defining one of ## the following functions or a user defined function handle, which is used ## for transforming the prediction scores returned by the @code{predict} and ## @code{resubPredict} methods. Default value is @qcode{'none'}. ## @end multitable ## ## @multitable @columnfractions 0.05 0.3 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## @item @tab @qcode{"doublelogit"} @tab @math{1 ./ (1 + exp .^ (-2 * x))} ## @item @tab @qcode{"invlogit"} @tab @math{log (x ./ (1 - x))} ## @item @tab @qcode{"ismax"} @tab Sets the score for the class with the largest ## score to 1, and sets the scores for all other classes to 0 ## @item @tab @qcode{"logit"} @tab @math{1 ./ (1 + exp .^ (-x))} ## @item @tab @qcode{"none"} @tab @math{x} (no transformation) ## @item @tab @qcode{"identity"} @tab @math{x} (no transformation) ## @item @tab @qcode{"sign"} @tab @math{-1 for x < 0, 0 for x = 0, 1 for x > 0} ## @item @tab @qcode{"symmetric"} @tab @math{2 * x + 1} ## @item @tab @qcode{"symmetricismax"} @tab Sets the score for the class with ## the largest score to 1, and sets the scores for all other classes to -1 ## @item @tab @qcode{"symmetriclogit"} @tab @math{2 ./ (1 + exp .^ (-x)) - 1} ## @end multitable ## ## @seealso{ClassificationNeuralNetwork} ## @end deftypefn function obj = fitcnet (X, Y, varargin) ## Check input parameters if (nargin < 2) error ("fitcnet: too few arguments."); endif if (mod (nargin, 2) != 0) error ("fitcnet: Name-Value arguments must be in pairs."); endif ## Check predictor data and labels have equal rows if (rows (X) != rows (Y)) error ("fitcnet: number of rows in X and Y must be equal."); endif ## Parse arguments to classdef constructor obj = ClassificationNeuralNetwork (X, Y, varargin{:}); endfunction %!demo %! ## Train a Neural Network on the Fisher's Iris data set and display %! ## a confusion chart with the classification results. %! %! load fisheriris %! Mdl = fitcnet (meas, species); %! pred_species = resubPredict (Mdl); %! confusionchart (species, pred_species); ## Test constructor %!test %! load fisheriris %! x = meas; %! y = grp2idx (species); %! Mdl = fitcnet (x, y, "IterationLimit", 50); %! assert (class (Mdl), "ClassificationNeuralNetwork"); %! assert (numel (Mdl.ModelParameters.LayerWeights), 2); %! assert (size (Mdl.ModelParameters.LayerWeights{1}), [10, 5]); %! assert (size (Mdl.ModelParameters.LayerWeights{2}), [3, 11]); ## Test input validation %!error fitcnet () %!error fitcnet (ones (4,1)) %!error %! fitcnet (ones (4,2), ones (4, 1), 'LayerSizes') %!error %! fitcnet (ones (4,2), ones (3, 1)) %!error %! fitcnet (ones (4,2), ones (3, 1), 'LayerSizes', 2) statistics-release-1.7.3/inst/fitcsvm.m000066400000000000000000000414641475240274700201650ustar00rootroot00000000000000## Copyright (C) 2024 Pallav Purbia ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Mdl} =} fitcsvm (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{Mdl} =} fitcsvm (@dots{}, @var{name}, @var{value}) ## ## Fit a Support Vector Machine classification model. ## ## @code{@var{Mdl} = fitcsvm (@var{X}, @var{Y})} returns a Support Vector ## Machine classification model, @var{Mdl}, with @var{X} being the predictor ## data, and @var{Y} the class labels of observations in @var{X}. ## ## @itemize ## @item ## @code{X} must be a @math{NxP} numeric matrix of predictor data where rows ## correspond to observations and columns correspond to features or variables. ## @item ## @code{Y} is @math{Nx1} matrix or cell matrix containing the class labels of ## corresponding predictor data in @var{X}. @var{Y} can be numerical, logical, ## char array or cell array of character vectors. @var{Y} must have same number ## of rows as @var{X}. ## @end itemize ## ## @code{@var{Mdl} = fitcsvm (@dots{}, @var{name}, @var{value})} returns a ## Support Vector Machine model with additional options specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @subheading Model Parameters ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Standardize"} @tab @tab A boolean flag indicating whether ## the data in @var{X} should be standardized prior to training. ## ## @item @qcode{"PredictorNames"} @tab @tab A cell array of character vectors ## specifying the predictor variable names. The variable names are assumed to ## be in the same order as they appear in the training data @var{X}. ## ## @item @qcode{"ResponseName"} @tab @tab A character vector specifying the name ## of the response variable. ## ## @item @qcode{"ClassNames"} @tab @tab Names of the classes in the class ## labels, @var{Y}, used for fitting the kNN model. @qcode{ClassNames} are of ## the same type as the class labels in @var{Y}. ## ## @item @qcode{"Prior"} @tab @tab A numeric vector specifying the prior ## probabilities for each class. The order of the elements in @qcode{Prior} ## corresponds to the order of the classes in @qcode{ClassNames}. ## ## @item @qcode{"Cost"} @tab @tab A @math{NxR} numeric matrix containing ## misclassification cost for the corresponding instances in @var{X} where ## @math{R} is the number of unique categories in @var{Y}. If an instance is ## correctly classified into its category the cost is calculated to be 1, ## otherwise 0. cost matrix can be altered use @code{@var{Mdl.cost} = somecost}. ## default value @qcode{@var{cost} = ones(rows(X),numel(unique(Y)))}. ## ## @item @qcode{"SVMtype"} @tab @tab Specifies the type of SVM used for training ## the @code{ClassificationSVM} model. By default, the type of SVM is defined ## by setting other parameters and/or by the data itself. Setting the ## @qcode{"SVMtype"} parameter overrides the default behavior and it accepts the ## following options: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## @item @tab @qcode{"C_SVC"} @tab It is the standard SVM formulation for ## classification tasks. It aims to find the optimal hyperplane that separates ## different classes by maximizing the margin between them while allowing some ## misclassifications. The parameter @qcode{"C"} controls the trade-off between ## maximizing the margin and minimizing the classification error. It is the ## default type, unless otherwise specified. ## @item @tab @qcode{"nu_SVC"} @tab It is a variation of the standard SVM that ## introduces a parameter @math{ν} (nu) as an upper bound on the fraction of ## margin errors and a lower bound on the fraction of support vectors. This ## formulation provides more control over the number of support vectors and the ## margin errors, making it useful for specific classification scenarios. It is ## the default type, when the @qcode{"OutlierFraction"} parameter is set. ## @item @tab @qcode{"one_class_SVM"} @tab It is used for anomaly detection and ## novelty detection tasks. It aims to separate the data points of a single ## class from the origin in a high-dimensional feature space. This method is ## particularly useful for identifying outliers or unusual patterns in the data. ## It is the default type, when the @qcode{"Nu"} parameter is set or when there ## is a single class in @var{Y}. When @qcode{"one_class_SVM"} is set by the ## @qcode{"SVMtype"} pair argument, @var{Y} has no effect and any classes are ## ignored. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"OutlierFraction"} @tab @tab The expected proportion of outliers ## in the training data, specified as a scalar value in the range @math{[0,1]}. ## When specified, the type of SVM model is switched to @qcode{"nu_SVC"} and ## @qcode{"OutlierFraction"} defines the @math{ν} (nu) parameter. ## ## @item @qcode{"KernelFunction"} @tab @tab A character vector specifying the ## method for computing elements of the Gram matrix. The available kernel ## functions are @qcode{'gaussian'} or @qcode{'rbf'}, @qcode{'linear'}, ## @qcode{'polynomial'}, and @qcode{'sigmoid'}. For one-class learning, the ## default Kernel function is @qcode{'rbf'}. For two-class learning the default ## is @qcode{'linear'}. ## ## @item @qcode{"PolynomialOrder"} @tab @tab A positive integer that specifies ## the order of polynomial in kernel function. The default value is 3. Unless ## the @qcode{"KernelFunction"} is set to @qcode{'polynomial'}, this parameter ## is ignored. ## ## @item @qcode{"KernelScale"} @tab @tab A positive scalar that specifies a ## scaling factor for the @math{γ} (gamma) parameter, which can be seen as the ## inverse of the radius of influence of samples selected by the model as ## support vectors. The @math{γ} (gamma) parameter is computed as ## @math{gamma = @qcode{KernelScale} / (number of features)}. The default value ## for @qcode{"KernelScale"} is 1. ## ## @item @qcode{"KernelOffset"} @tab @tab A nonnegative scalar that specifies ## the @math{coef0} in kernel function. For the polynomial kernel, it influences ## the polynomial's shift, and for the sigmoid kernel, it affects the hyperbolic ## tangent's shift. The default value for @qcode{"KernelOffset"} is 0. ## ## @item @qcode{"BoxConstraint"} @tab @tab A positive scalar that specifies the ## upper bound of the Lagrange multipliers, i.e. the parameter C, which is used ## for training @qcode{"C_SVC"} and @qcode{"one_class_SVM"} type of models. It ## determines the trade-off between maximizing the margin and minimizing the ## classification error. The default value for @qcode{"BoxConstraint"} is 1. ## ## @item @qcode{"Nu"} @tab @tab A positive scalar, in the range @math{(0,1]} ## that specifies the parameter @math{ν} (nu) for training @qcode{"nu_SVC"} and ## @qcode{"one_class_SVM"} type of models. Unless overriden by setting the ## @qcode{"SVMtype"} parameter, setting the @qcode{"Nu"} parameter always forces ## the training model type to @qcode{"one_class_SVM"}, in which case, the number ## of classes in @var{Y} is ignored. The default value for @qcode{"Nu"} is 1. ## ## @item @qcode{"CacheSize"} @tab @tab A positive scalar that specifies the ## memory requirements (in MB) for storing the Gram matrix. The default is 1000. ## ## @item @qcode{"Tolerance"} @tab @tab A nonnegative scalar that specifies ## the tolerance of termination criterion. The default value is 1e-6. ## ## @item @qcode{"Shrinking"} @tab @tab Specifies whether to use shrinking ## heuristics. It accepts either 0 or 1. The default value is 1. ## @end multitable ## ## @subheading Cross Validation Options ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"Crossval"} @tab @tab Cross-validation flag specified as ## @qcode{'on'} or @qcode{'off'}. If @qcode{'on'} is specified, a 10-fold ## cross validation is performed and a @code{ClassificationPartitionedModel} is ## returned in @var{Mdl}. To override this cross-validation setting, use only ## one of the following Name-Value pair arguments. ## ## @item @qcode{"CVPartition"} @tab @tab A @code{cvpartition} object that ## specifies the type of cross-validation and the indexing for the training and ## validation sets. A @code{ClassificationPartitionedModel} is returned in ## @var{Mdl} and the trained model is stored in the @code{Trained} property. ## ## @item @qcode{"Holdout"} @tab @tab Fraction of the data used for holdout ## validation, specified as a scalar value in the range @math{[0,1]}. When ## specified, a randomly selected percentage is reserved as validation data and ## the remaining set is used for training. The trained model is stored in the ## @code{Trained} property of the @code{ClassificationPartitionedModel} returned ## in @var{Mdl}. @qcode{"Holdout"} partitioning attempts to ensure that each ## partition represents the classes proportionately. ## ## @item @qcode{"KFold"} @tab @tab Number of folds to use in the cross-validated ## model, specified as a positive integer value greater than 1. When specified, ## then the data is randomly partitioned in @math{k} sets and for each set, the ## set is reserved as validation data while the remaining @math{k-1} sets are ## used for training. The trained models are stored in the @code{Trained} ## property of the @code{ClassificationPartitionedModel} returned in @var{Mdl}. ## @qcode{"KFold"} partitioning attempts to ensure that each partition ## represents the classes proportionately. ## ## @item @qcode{"Leaveout"} @tab @tab Leave-one-out cross-validation flag ## specified as @qcode{'on'} or @qcode{'off'}. If @qcode{'on'} is specified, ## then for each of the @math{n} observations (where @math{n} is the number of ## observations, excluding missing observations, specified in the ## @code{NumObservations} property of the model), one observation is reserved as ## validation data while the remaining observations are used for training. The ## trained models are stored in the @code{Trained} property of the ## @code{ClassificationPartitionedModel} returned in @var{Mdl}. ## @end multitable ## ## @seealso{ClassificationSVM, ClassificationPartitionedModel, svmtrain, ## svmpredict} ## @end deftypefn function Mdl = fitcsvm (X, Y, varargin) ## Check input parameters if (nargin < 2) error ("fitcsvm: too few arguments."); endif if (mod (nargin, 2) != 0) error ("fitcsvm: Name-Value arguments must be in pairs."); endif ## Check predictor data and labels have equal rows if (rows (X) != rows (Y)) error ("fitcsvm: number of rows in X and Y must be equal."); endif ## Check optional input parameters for cross-validation options cv_opt = false; cv_arg = 0; args = {}; while (numel (varargin) > 0) switch (tolower (varargin{1})) case 'crossval' CrossVal = varargin{2}; if (! any (strcmp (CrossVal, {'off', 'on'}))) error ("fitcsvm: 'CrossVal' must be either 'off' or 'on'."); endif if (strcmp (CrossVal, 'on')) cv_opt = true; endif case 'kfold' Name = 'KFold'; Value = varargin{2}; cv_arg += 1; cv_opt = true; case 'holdout' Name = 'Holdout'; Value = varargin{2}; cv_arg += 1; cv_opt = true; case 'leaveout' Name = 'Holdout'; Value = varargin{2}; cv_arg += 1; cv_opt = true; case 'cvpartition' Name = 'CVPartition'; Value = varargin{2}; cv_arg += 1; cv_opt = true; otherwise args = [args, {varargin{1}, varargin{2}}]; endswitch varargin (1:2) = []; endwhile ## Check for multiple cross-validation paired arguments if (cv_arg > 1) error (strcat (["fitcsvm: You can use only one cross-validation"], ... [" name-value pair argument at a time to create a"], ... [" cross-validated model."])); endif ## Parse arguments to classdef constructor Mdl = ClassificationSVM (X, Y, args{:}); ## If cross validation has been requested, ## return a ClassificationPartitionedModel if (cv_opt) if (cv_arg) Mdl = crossval (Mdl, Name, Value); else Mdl = crossval (Mdl); endif endif endfunction %!demo %! ## Use a subset of Fisher's iris data set %! %! load fisheriris %! inds = ! strcmp (species, 'setosa'); %! X = meas(inds, [3,4]); %! Y = species(inds); %! %! ## Train a linear SVM classifier %! SVMModel = fitcsvm (X, Y) %! %! ## Plot a scatter diagram of the data and circle the support vectors. %! sv = SVMModel.SupportVectors; %! figure %! gscatter (X(:,1), X(:,2), Y) %! hold on %! plot (sv(:,1), sv(:,2), 'ko', 'MarkerSize', 10) %! legend ('versicolor', 'virginica', 'Support Vector') %! hold off ## Test constructor %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = {"a"; "a"; "b"; "b"}; %! a = fitcsvm (x, y); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y}, {x, y}) %! assert (a.NumObservations, 4) %! assert ({a.ResponseName, a.PredictorNames}, {"Y", {"x1", "x2", "x3"}}) %! assert (a.ModelParameters.SVMtype, "c_svc") %! assert (a.ClassNames, {"a"; "b"}) ## Test Output %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = fitcsvm (x, y); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "linear"}) %! assert (a.ModelParameters.BoxConstraint, 1) %! assert (a.ModelParameters.KernelOffset, 0) %! assert (a.ClassNames, [1; -1]) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = fitcsvm (x, y, "KernelFunction", "rbf", "BoxConstraint", 2, ... %! "KernelOffset", 2); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "rbf"}) %! assert (a.ModelParameters.BoxConstraint, 2) %! assert (a.ModelParameters.KernelOffset, 2) %! assert (isempty (a.Alpha), true) %! assert (isempty (a.Beta), false) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = fitcsvm (x, y, "KernelFunction", "polynomial", "PolynomialOrder", 3); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "polynomial"}) %! assert (a.ModelParameters.PolynomialOrder, 3) %! assert (isempty (a.Alpha), true) %! assert (isempty (a.Beta), false) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = fitcsvm (x, y, "KernelFunction", "linear", "PolynomialOrder", 3); %! assert (class (a), "ClassificationSVM"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "linear"}) %! assert (a.ModelParameters.PolynomialOrder, 3) %! assert (isempty (a.Alpha), false) %! assert (isempty (a.Beta), true) %!test %! x = [1, 2; 2, 3; 3, 4; 4, 5; 2, 3; 3, 4; 2, 3; 3, 4; 2, 3; 3, 4]; %! y = [1; 1; -1; -1; 1; -1; -1; -1; -1; -1]; %! a = fitcsvm (x, y, "KernelFunction", "linear", "CrossVal", 'on'); %! assert (class (a), "ClassificationPartitionedModel"); %! assert ({a.X, a.Y, a.ModelParameters.KernelFunction}, {x, y, "linear"}) %! assert (a.ModelParameters.PolynomialOrder, 3) %! assert (isempty (a.Trained{1}.Alpha), false) %! assert (isempty (a.Trained{1}.Beta), true) ## Test input validation %!error fitcsvm () %!error fitcsvm (ones (4,1)) %!error %! fitcsvm (ones (4,2), ones (4, 1), 'KFold') %!error %! fitcsvm (ones (4,2), ones (3, 1)) %!error %! fitcsvm (ones (4,2), ones (3, 1), 'KFold', 2) %!error %! fitcsvm (ones (4,2), ones (4, 1), "CrossVal", 2) %!error %! fitcsvm (ones (4,2), ones (4, 1), "CrossVal", 'a') %!error ... %! fitcsvm (ones (4,2), ones (4, 1), "KFold", 10, "Holdout", 0.3) statistics-release-1.7.3/inst/fitgmdist.m000066400000000000000000000455551475240274700205110ustar00rootroot00000000000000## Copyright (C) 2015 Lachlan Andrew ## Copyright (C) 2018 John Donoghue ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{GMdist} =} fitgmdist (@var{data}, @var{k}, @var{param1}, @var{value1}, @dots{}) ## ## Fit a Gaussian mixture model with @var{k} components to @var{data}. ## Each row of @var{data} is a data sample. Each column is a variable. ## ## Optional parameters are: ## @itemize ## @item @qcode{"start"}: Initialization conditions. Possible values are: ## @itemize ## @item @qcode{"randSample"} (default) Takes means uniformly from rows of data. ## @item @qcode{"plus"} Use k-means++ to initialize means. ## @item @qcode{"cluster"} Performs an initial clustering with 10% of the data. ## @item @var{vector} A vector whose length is the number of rows in data, and ## whose values are 1 to k specify the components each row is initially ## allocated to. The mean, variance, and weight of each component is calculated ## from that. ## @item @var{structure} A structure with fields @qcode{mu}, @qcode{Sigma} and ## @qcode{ComponentProportion}. ## @end itemize ## For @qcode{"randSample"}, @qcode{"plus"}, and @qcode{"cluster"}, the initial ## variance of each component is the variance of the entire data sample. ## ## @item @qcode{"Replicates"}: Number of random restarts to perform. ## ## @item @qcode{"RegularizationValue"} or @qcode{"Regularize"}: A small number ## added to the diagonal entries of the covariance to prevent singular ## covariances. ## ## @item @qcode{"SharedCovariance"} or @qcode{"SharedCov"} (logical). True if ## all components must share the same variance, to reduce the number of free ## parameters ## ## @item @qcode{"CovarianceType"} or @qcode{"CovType"} (string). Possible values ## are: ## @itemize ## @item @qcode{"full"} (default) Allow arbitrary covariance matrices. ## @item @qcode{"diagonal"} Force covariances to be diagonal, to reduce the ## number of free parameters. ## @end itemize ## ## @item @qcode{"Options"}: A structure with all of the following fields: ## @itemize ## @item @qcode{MaxIter} Maximum number of EM iterations (default 100). ## @item @qcode{TolFun} Threshold increase in likelihood to terminate EM ## (default 1e-6). ## @item @qcode{Display} Possible values are: ## @itemize ## @item @qcode{"off"} (default): Display nothing. ## @item @qcode{"final"}: Display the total number of iterations and likelihood ## once the execution completes. ## @item @qcode{"iter"}: Display the number of iteration and likelihood after ## each iteration. ## @end itemize ## @end itemize ## @item @qcode{"Weight"}: A column vector or @math{Nx2} matrix. The first ## column consists of non-negative weights given to the samples. If these are ## all integers, this is equivalent to specifying @qcode{@var{weight}(i)} copies ## of row @qcode{i} of @var{data}, but potentially faster. If a row of ## @var{data} is used to represent samples that are similar but not identical, ## then the second column of @var{weight} indicates the variance of those ## original samples. Specifically, in the EM algorithm, the contribution of row ## @qcode{i} towards the variance is set to at least @qcode{@var{weight}(i,2)}, ## to prevent spurious components with zero variance. ## @end itemize ## ## @seealso{gmdistribution, kmeans} ## @end deftypefn function obj = fitgmdist (data, k, varargin) if (nargin < 2 || mod (nargin, 2) == 1) print_usage; endif [~, prop] = parseparams (varargin); ## defaults for options diagonalCovar = false; # "full". (true is "diagonal") sharedCovar = false; start = "randSample"; replicates = 1; option.MaxIter = 100; option.TolFun = 1e-6; option.Display = "off"; # "off" (1 is "final", 2 is "iter") Regularizer = 0; weights = []; # Each row i counts as "weights(i,1)" rows ## Remove rows containing NaN / NA data = data(! any (isnan (data), 2), :); ## Used for getting the number of samples nRows = rows (data); nCols = columns (data); ## Parse options while (! isempty (prop)) try switch (lower (prop{1})) case {"sharedcovariance", "sharedcov"} sharedCovar = prop{2}; case {"covariancetype", "covartype"} diagonalCovar = prop{2}; case {"regularizationvalue", "regularize"} Regularizer = prop{2}; case "replicates" replicates = prop{2}; case "start" start = prop{2}; case "weights" weights = prop{2}; case "options" option.MaxIter = prop{2}.MaxIter; option.TolFun = prop{2}.TolFun; option.Display = prop{2}.Display; otherwise error ("fitgmdist: Unknown option %s.", prop{1}); endswitch catch ME if (length (prop) < 2) error ("fitgmdist: Option '%s' has no argument.", prop{1}); else rethrow (ME) endif end_try_catch prop = prop(3:end); endwhile ## Process options ## Check for the "replicates" property try if isempty (1:replicates) error ("fitgmdist: replicates must be positive."); endif catch error ("fitgmdist: invalid number of replicates."); end_try_catch ## check for the "option" property MaxIter = option.MaxIter; TolFun = option.TolFun; switch (lower (option.Display)) case "off" Display = 0; case "final" Display = 1; case "iter" Display = 2; case "notify" Display = 0; otherwise error ("fitgmdist: Unknown Display option %s.", option.Display); endswitch try p = ones(1, k) / k; # Default is uniform component proportions catch ME if (! isscalar (k) || ! isnumeric (k)) error ("fitgmdist: The second argument must be a numeric scalar."); else rethrow (ME) endif end_try_catch ## Check for the "start" property if (ischar (start)) start = lower (start); switch (start) case {"randsample", "plus", "cluster", "randsamplep", "plusp", "clusterp"} otherwise error ("fitgmdist: Unknown Start value %s\n.", start); endswitch component_order_free = true; else component_order_free = false; if (! ismatrix (start) || ! isnumeric (start)) try mu = start.mu; Sigma = start.Sigma; if (isfield (start, 'ComponentProportion')) p = start.ComponentProportion(:)'; end if (any (size (data, 2) != [size(mu, 2), size(Sigma, 1)]) || ... any (k != [size(mu,1), size(p,2)])) error ("fitgmdist: Start parameter has mismatched dimensions."); endif catch error ("fitgmdist: invalid start parameter."); end_try_catch else validIndices = 0; mu = zeros (k, nRows); Sigma = zeros (nRows, nRows, k); for i = 1:k idx = (start == i); validIndices = validIndices + sum (idx); mu(i,:) = mean (data(idx,:)); Sigma(:,:,i) = cov (data(idx,:)) + Regularizer * eye (nCols); endfor if (validIndices < nRows) error (strcat (["fitgmdist: Start is numeric, but is not"], ... [" integers between 1 and k."])); endif endif start = []; # so that variance isn't recalculated later replicates = 1; # Will be the same each time anyway endif ## Check for the "SharedCovariance" property if (! islogical (sharedCovar)) error ("fitgmdist: SharedCoveriance must be logical true or false."); endif ## Check for the "CovarianceType" property if (! islogical (diagonalCovar)) try if (strcmpi (diagonalCovar, "diagonal")) diagonalCovar = true; elseif (strcmpi (diagonalCovar, "full")) diagonalCovar = false; else error ("fitgmdist: CovarianceType must be Full or Diagonal."); endif catch error ("fitgmdist: CovarianceType must be 'Full' or 'Diagonal'."); end_try_catch endif ## Check for the "Regularizer" property try if (Regularizer < 0) error ("fitgmdist: Regularizer must be non-negative"); endif catch ME if (! isscalar (Regularizer) || ! isnumeric (Regularizer)) error ("fitgmdist: Regularizer must be a numeric scalar"); else rethrow (ME) endif end_try_catch ## Check for the "Weights" property and the matrix try if (! isempty (weights)) if (columns (weights) > 2 || any (weights(:) < 0)) error (strcat (["fitgmdist: weights must be a nonnegative"], ... [" numeric dx1 or dx2 matrix."])); endif if (rows (weights) != nRows) error (strcat (["fitgmdist: number of weights %d must match"], ... [" number of samples %d."]), rows (weights), nRows); endif non_zero = (weights(:,1) > 0); weights = weights(non_zero,:); data = data (non_zero,:); nRows = rows (data); raw_samples = sum (weights(:,1)); else raw_samples = nRows; endif ## Validate the matrix if (! isreal (data(k,1))) error ("fitgmdist: first input argument must be a DxN real data matrix."); endif catch ME if (! isnumeric (data) || ! ismatrix (data) || ! isreal (data)) error ("fitgmdist: first input argument must be a DxN real data matrix."); elseif (k > nRows || k < 0) if (exists ("non_zero", "var") && k <= length (non_zero)) error (strcat (["fitgmdist: The number of non-zero weights (%d)"], ... [" must be at least the number of components"], ... [" (%d)."]), nRows, k); else error (strcat (["fitgmdist: The number of components (%d) must be"], ... [" a positive number less than the number of data"], ... [" rows (%d)."]), k, nRows); endif elseif (! ismatrix (weights) || ! isnumeric (weights)) error (strcat (["fitgmdist: weights must be a nonnegative numeric"], ... [" dx1 or dx2 matrix."])); else rethrow (ME) endif end_try_catch ## Done processing options ####################################### ## #sed to hold the probability of each class, given each data vector try p_x_l = zeros (nRows, k); # probability of observation x given class l best = -realmax; best_params = []; diag_slice = 1:(nCols+1):(nCols)^2; ## Create index slices to calculate symmetric ## completion of upper triangular Mx lower_half = zeros (nCols * (nCols - 1) / 2, 1); upper_half = zeros (nCols * (nCols - 1) / 2, 1); i = 1; for rw = 1:nCols for cl = rw+1:nCols upper_half(i) = sub2ind ([nCols, nCols], rw, cl); lower_half(i) = sub2ind ([nCols, nCols], cl, rw); i = i + 1; endfor endfor for rep = 1:replicates if (! isempty (start)) ## Initialize the means switch (start) case {"randsample"} if (isempty (weights)) idx = randperm (nRows, k); else idx = randsample (nRows, k, false, weights); endif mu = data(idx, :); case {"plus"} # k-means++, by Arthur and Vassilios mu(1,:) = data(randi (nRows),:); d = inf (nRows, 1); # Distance to nearest centroid so far for i = 2:k d = min (d, sum (bsxfun (@minus, data, mu(i-1, :)).^2, 2)); # pick next sample with prob. prop to dist.*weights if (isempty (weights)) cs = cumsum (d); else cs = cumsum (d .* weights(:,1)); endif mu(i,:) = data(find (cs > rand * cs(end), 1), :); endfor case {"cluster"} subsamp = max (k, ceil (nRows/10)); if (isempty (weights)) idx = randperm (nRows, subsamp); else idx = randsample (nRows, subsamp, false, weights); endif [~, mu] = kmeans (data(idx), k, "start", "sample"); endswitch ## Initialize the variance, unless set explicitly Sigma = var (data) + Regularizer; if (! diagonalCovar) Sigma = diag (Sigma); endif if (! sharedCovar) Sigma = repmat (Sigma, [1, 1, k]); endif endif ## Run the algorithm iter = 1; log_likeli = -inf; incr = 1; while (incr > TolFun && iter <= MaxIter) iter = iter + 1; ####################################### ## "E step" ## Calculate probability of class l given observations for i = 1:k if (sharedCovar) sig = Sigma; else sig = Sigma(:,:,i); endif if (diagonalCovar) sig = diag(sig); endif try p_x_l (:, i) = mvnpdf (data, mu(i, :), sig); catch ME if (strfind (ME.message, "positive definite")) error (strcat (["fitgmdist: Covariance is not positive"], ... [" definite. Increase RegularizationValue."])); else rethrow (ME) endif end_try_catch endfor ## Bayes' rule p_x_l = bsxfun (@times, p_x_l, p); # weight by priors p_l_x = bsxfun (@rdivide, p_x_l, sum (p_x_l, 2)); # Normalize ####################################### ## "M step" ## Calculate new parameters if (! isempty (weights)) p_l_x = bsxfun (@times, p_l_x, weights(:,1)); endif sum_p_l_x = sum (p_l_x); # row vec of \sum_{data} p(class|data,params) p = sum_p_l_x / raw_samples; # new proportions mu = bsxfun (@rdivide, p_l_x' * data, sum_p_l_x'); # new means if (sharedCovar) sumSigma = zeros (size (Sigma(:,:,1))); # diagonalCovar gives size endif for i = 1:k ## Sigma deviation = bsxfun(@minus, data, mu(i,:)); lhs = bsxfun(@times, p_l_x(:,i), deviation); ## Calculate covariance ## Iterate either over elements of the covariance matrix, ## since there should be fewer of those than rows of data. for rw = 1:nCols for cl = rw:nCols sig(rw,cl) = lhs(:,rw)' * deviation(:,cl); endfor endfor sig(lower_half) = sig(upper_half); sig = sig/sum_p_l_x(i) + Regularizer*eye (nCols); if (columns (weights) > 1) # don't give "singleton" clusters low var sig(diag_slice) = max (sig(diag_slice), weights(i,2)); endif if (diagonalCovar) sig = diag(sig)'; endif if (sharedCovar) sumSigma = sumSigma + sig * p(i); # Heuristic. Should it use else # old p? Something else? Sigma(:,:,i) = sig; endif endfor if (sharedCovar) Sigma = sumSigma; endif ####################################### ## Calculate the new (and relative change in) log-likelihood if (isempty (weights)) new_log_likeli = sum (log (sum (p_x_l, 2))); else new_log_likeli = sum (weights(:,1) .* log (sum (p_x_l, 2))); endif incr = (new_log_likeli - log_likeli)/max(1,abs(new_log_likeli)); if (Display == 2) fprintf("iter %d log-likelihood %g\n", iter-1, new_log_likeli); endif log_likeli = new_log_likeli; endwhile if (log_likeli > best) best = log_likeli; best_params.mu = mu; best_params.Sigma = Sigma; best_params.p = p; endif endfor catch ME try if (1 < MaxIter), end catch error ("fitgmdist: invalid MaxIter."); end_try_catch rethrow (ME) end_try_catch ## List components in descending order of proportion, ## unless the order was implicitly specified by "start" if (component_order_free) [~, idx] = sort (-best_params.p); best_params.p = best_params.p (idx); best_params.mu = best_params.mu(idx,:); if (! sharedCovar) best_params.Sigma = best_params.Sigma(:,:,idx); endif endif ## Calculate number of parameters if (diagonalCovar) params = nCols; else params = nCols * (nCols+1) / 2; endif params = params*size (Sigma, 3) + 2*rows (mu) - 1; ## This works in Octave, but not in Matlab #obj = gmdistribution (best_params.mu, best_params.Sigma, best_params.p', extra); obj = gmdistribution (best_params.mu, best_params.Sigma, best_params.p'); obj.NegativeLogLikelihood = -best; obj.AIC = -2*(best - params); obj.BIC = -2*best + params * log (raw_samples); obj.Converged = (incr <= TolFun); obj.NumIterations = iter-1; obj.RegularizationValue = Regularizer; if (Display == 1) fprintf (" %d iterations log-likelihood = %g\n", ... obj.NumIterations, -obj.NegativeLogLikelihood); endif endfunction %!demo %! ## Generate a two-cluster problem %! C1 = randn (100, 2) + 2; %! C2 = randn (100, 2) - 2; %! data = [C1; C2]; %! %! ## Perform clustering %! GMModel = fitgmdist (data, 2); %! %! ## Plot the result %! figure %! [heights, bins] = hist3([C1; C2]); %! [xx, yy] = meshgrid(bins{1}, bins{2}); %! bbins = [xx(:), yy(:)]; %! contour (reshape (GMModel.pdf (bbins), size (heights))); %!demo %! Angle_Theta = [ 30 + 10 * randn(1, 10), 60 + 10 * randn(1, 10) ]'; %! nbOrientations = 2; %! initial_orientations = [38.0; 18.0]; %! initial_weights = ones (1, nbOrientations) / nbOrientations; %! initial_Sigma = 10 * ones (1, 1, nbOrientations); %! start = struct ("mu", initial_orientations, "Sigma", initial_Sigma, ... %! "ComponentProportion", initial_weights); %! GMModel_Theta = fitgmdist (Angle_Theta, nbOrientations, "Start", start , ... %! "RegularizationValue", 0.0001) ## Test results against MATLAB example %!test %! load fisheriris %! classes = unique (species); %! [~, score] = pca (meas, "NumComponents", 2); %! options.MaxIter = 1000; %! options.TolFun = 1e-6; %! options.Display = "off"; %! GMModel = fitgmdist (score, 2, "Options", options); %! assert (isa (GMModel, "gmdistribution"), true); %! assert (GMModel.mu, [1.3212, -0.0954; -2.6424, 0.1909], 1e-4); statistics-release-1.7.3/inst/fitlm.m000066400000000000000000000404371475240274700176240ustar00rootroot00000000000000## Copyright (C) 2022 Andrew Penn ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{tab} =} fitlm (@var{X}, @var{y}) ## @deftypefnx {statistics} {@var{tab} =} fitlm (@var{X}, @var{y}, @var{name}, @var{value}) ## @deftypefnx {statistics} {@var{tab} =} fitlm (@var{X}, @var{y}, @var{modelspec}) ## @deftypefnx {statistics} {@var{tab} =} fitlm (@var{X}, @var{y}, @var{modelspec}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{tab}] =} fitlm (@dots{}) ## @deftypefnx {statistics} {[@var{tab}, @var{stats}] =} fitlm (@dots{}) ## @deftypefnx {statistics} {[@var{tab}, @var{stats}] =} fitlm (@dots{}) ## ## Regress the continuous outcome (i.e. dependent variable) @var{y} on ## continuous or categorical predictors (i.e. independent variables) @var{X} ## by minimizing the sum-of-squared residuals. Unless requested otherwise, ## @qcode{fitlm} prints the model formula, the regression coefficients (i.e. ## parameters/contrasts) and an ANOVA table. Note that unlike @qcode{anovan}, ## @qcode{fitlm} treats all factors as continuous by default. A bootstrap ## resampling variant of this function, @code{bootlm}, is available in the ## statistics-resampling package and has similar usage. ## ## @var{X} must be a column major matrix or cell array consisting of the ## predictors. A constant term (intercept) should not be included in X - it ## is automatically added to the model. @var{y} must be a column vector ## corresponding to the outcome variable. @var{modelspec} can specified as ## one of the following: ## ## @itemize ## @item ## "constant" : model contains only a constant (intercept) term. ## ## @item ## "linear" (default) : model contains an intercept and linear term for each ## predictor. ## ## @item ## "interactions" : model contains an intercept, linear term for each predictor ## and all products of pairs of distinct predictors. ## ## @item ## "full" : model contains an intercept, linear term for each predictor and ## all combinations of the predictors. ## ## @item ## a matrix of term definitions : an t-by-(N+1) matrix specifying terms in ## a model, where t is the number of terms, N is the number of predictor ## variables, and +1 accounts for the outcome variable. The outcome variable ## is the last column in the terms matrix and must be a column of zeros. ## An intercept must be specified in the first row of the terms matrix and ## must be a row of zeros. ## @end itemize ## ## @qcode{fitlm} can take a number of optional parameters as name-value pairs. ## ## @code{[@dots{}] = fitlm (..., "CategoricalVars", @var{categorical})} ## ## @itemize ## @item ## @var{categorical} is a vector of indices indicating which of the columns ## (i.e. variables) in @var{X} should be treated as categorical predictors ## rather than as continuous predictors. ## @end itemize ## ## @qcode{fitlm} also accepts optional @qcode{anovan} parameters as name-value ## pairs (except for the "model" parameter). The accepted parameter names from ## @qcode{anovan} and their default values in @qcode{fitlm} are: ## ## @itemize ## @item ## @var{CONTRASTS} : "treatment" ## ## @item ## @var{SSTYPE}: 2 ## ## @item ## @var{ALPHA}: 0.05 ## ## @item ## @var{DISPLAY}: "on" ## ## @item ## @var{WEIGHTS}: [] (empty) ## ## @item ## @var{RANDOM}: [] (empty) ## ## @item ## @var{CONTINUOUS}: [1:N] ## ## @item ## @var{VARNAMES}: [] (empty) ## @end itemize ## ## Type '@qcode{help anovan}' to find out more about what these options do. ## ## @qcode{fitlm} can return up to two output arguments: ## ## [@var{tab}] = fitlm (@dots{}) returns a cell array containing a ## table of model parameters ## ## [@var{tab}, @var{stats}] = fitlm (@dots{}) returns a structure ## containing additional statistics, including degrees of freedom and effect ## sizes for each term in the linear model, the design matrix, the ## variance-covariance matrix, (weighted) model residuals, and the mean squared ## error. The columns of @var{stats}.coeffs (from left-to-right) report the ## model coefficients, standard errors, lower and upper 100*(1-alpha)% ## confidence interval bounds, t-statistics, and p-values relating to the ## contrasts. The number appended to each term name in @var{stats}.coeffnames ## corresponds to the column number in the relevant contrast matrix for that ## factor. The @var{stats} structure can be used as input for @qcode{multcompare}. ## Note that if the model contains a continuous variable and you wish to use ## the @var{STATS} output as input to @qcode{multcompare}, then the model needs ## to be refit with the "contrast" parameter set to a sum-to-zero contrast ## coding scheme, e.g."simple". ## ## @seealso{anovan, multcompare} ## @end deftypefn function [T, STATS] = fitlm (X, y, varargin) ## Check input and output arguments if (nargin < 2) error (strcat (["fitlm usage: ""fitlm (X, y, varargin)""; "], ... [" atleast 2 input arguments required"])); endif if (nargout > 3) error ("fitlm: invalid number of output arguments requested"); endif ## Evaluate input data [n, N] = size (X); msg = strcat (["fitlm: do not include the intercept column in X"], ... [" - it will be added automatically"]); if (iscell (X)) if (~ iscell (X{:,1})) if (all (X{:,1} == 1)) error (msg) endif endif else if (all (X(:,1) == 1)) error (msg) endif endif ## Fetch anovan options options = varargin; if (isempty(options)) options{1} = "linear"; endif ## Check if MODELSPEC was provided. If not create it. if (ischar (options{1})) if (! ismember (lower (options{1}), {"sstype", "varnames", "contrasts", ... "weights", "alpha", "display", "continuous", ... "categorical", "categoricalvars", "random", "model"})) MODELSPEC = options{1}; options(1) = []; CONTINUOUS = []; else ## If MODELSPEC is not provided, set it for an additive linear model MODELSPEC = zeros (N + 1); MODELSPEC(2:N+1, 1:N) = eye (N); end else MODELSPEC = options{1}; options(1) = []; endif ## Evaluate MODELSPEC if (ischar (MODELSPEC)) MODELSPEC = lower (MODELSPEC); if (! isempty (regexp (MODELSPEC, "~"))) error ("fitlm: model formulae are not a supported format for MODELSPEC") endif if (! ismember (MODELSPEC, {"constant", "linear", "interaction", ... "interactions", "full"})) error ("fitlm: character vector for model specification not recognised") endif if strcmp (MODELSPEC, "constant") X = []; MODELSPEC = "linear"; N = 0; endif else if (size (MODELSPEC, 1) < N + 1) error ("fitlm: number of rows in MODELSPEC must 1 + number of columns in X"); endif if (size (MODELSPEC, 2) != N + 1) error ("fitlm: number of columns in MODELSPEC must = 1 + number of columns in X"); endif if (! all (ismember (MODELSPEC(:), [0,1]))) error (strcat (["fitlm: elements of the model terms matrix must be "], ... [" either 0 or 1. Higher order terms are not supported"])); endif MODELSPEC = logical (MODELSPEC(2:N+1,1:N)); endif ## Check for unsupported options used by anovan if (any (strcmpi ("MODEL", options))) error (strcat(["fitlm: modelspec should be specified in the third"], ... [" input argument of fitlm (if at all)"])); endif ## Check and set variable types idx = find (any (cat (1, strcmpi ("categorical", options), ... strcmpi ("categoricalvars", options)))); if (! isempty (idx)) CONTINUOUS = [1:N]; CONTINUOUS(ismember(CONTINUOUS,options{idx+1})) = []; options(idx:idx+1) = []; else CONTINUOUS = [1:N]; endif idx = find (strcmpi ("continuous", options)); if (! isempty (idx)) ## Note that setting continuous parameter will override settings made ## to "categorical" CONTINUOUS = options{idx+1}; endif ## Check if anovan CONTRASTS option was used idx = find (strcmpi ("contrasts", options)); if (isempty (idx)) CONTRASTS = "treatment"; else CONTRASTS = options{idx+1}; if (ischar(CONTRASTS)) contr_str = CONTRASTS; CONTRASTS = cell (1, N); CONTRASTS(:) = {contr_str}; endif if (! iscell (CONTRASTS)) CONTRASTS = {CONTRASTS}; endif for i = 1:N if (! isnumeric(CONTRASTS{i})) if (! isempty (CONTRASTS{i})) if (! ismember (CONTRASTS{i}, ... {"simple","poly","helmert","effect","treatment"})) error (strcat(["fitlm: the choices for built-in contrasts are"], ... [" ""simple"", ""poly"", ""helmert"", ""effect"", or ""treatment"""])); endif endif endif endfor endif ## Check if anovan SSTYPE option was used idx = find (strcmpi ("sstype", options)); if (isempty (idx)) SSTYPE = 2; else SSTYPE = options{idx+1}; endif ## Perform model fit and ANOVA [jnk, jnk, STATS] = anovan (y, X, options{:}, ... "model", MODELSPEC, ... "contrasts", CONTRASTS, ... "continuous", CONTINUOUS, ... "sstype", SSTYPE); ## Create table of regression coefficients ncoeff = sum (STATS.df); T = cell (2 + ncoeff, 7); T(1,:) = {"Parameter", "Estimate", "SE", "Lower.CI", "Upper.CI", "t", "Prob>|t|"}; T(2:end,1) = STATS.coeffnames; T(2:end,2:7) = num2cell (STATS.coeffs); ## Update STATS structure STATS.source = "fitlm"; endfunction %!demo %! y = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! X = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! %! [TAB,STATS] = fitlm (X,y,"linear","CategoricalVars",1,"display","on"); %!demo %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! brands = {'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'}; %! popper = {'oil', 'oil', 'oil'; 'oil', 'oil', 'oil'; 'oil', 'oil', 'oil'; ... %! 'air', 'air', 'air'; 'air', 'air', 'air'; 'air', 'air', 'air'}; %! %! [TAB, STATS] = fitlm ({brands(:),popper(:)},popcorn(:),"interactions",... %! "CategoricalVars",[1,2],"display","on"); %!test %! y = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! X = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! [TAB,STATS] = fitlm (X,y,"continuous",[],"display","off"); %! [TAB,STATS] = fitlm (X,y,"CategoricalVars",1,"display","off"); %! [TAB,STATS] = fitlm (X,y,"constant","categorical",1,"display","off"); %! [TAB,STATS] = fitlm (X,y,"linear","categorical",1,"display","off"); %! [TAB,STATS] = fitlm (X,y,[0,0;1,0],"categorical",1,"display","off"); %! assert (TAB{2,2}, 10, 1e-04); %! assert (TAB{3,2}, 7.99999999999999, 1e-09); %! assert (TAB{4,2}, 8.99999999999999, 1e-09); %! assert (TAB{5,2}, 11.0001428571429, 1e-09); %! assert (TAB{6,2}, 19.0001111111111, 1e-09); %! assert (TAB{2,3}, 1.01775379540949, 1e-09); %! assert (TAB{3,3}, 1.64107868458008, 1e-09); %! assert (TAB{4,3}, 1.43932122062479, 1e-09); %! assert (TAB{5,3}, 1.48983900477565, 1e-09); %! assert (TAB{6,3}, 1.3987687997822, 1e-09); %! assert (TAB{2,6}, 9.82555903510687, 1e-09); %! assert (TAB{3,6}, 4.87484242844031, 1e-09); %! assert (TAB{4,6}, 6.25294748040552, 1e-09); %! assert (TAB{5,6}, 7.38344399756088, 1e-09); %! assert (TAB{6,6}, 13.5834536158296, 1e-09); %! assert (TAB{3,7}, 2.85812420217862e-05, 1e-12); %! assert (TAB{4,7}, 5.22936741204002e-07, 1e-06); %! assert (TAB{5,7}, 2.12794763209106e-08, 1e-07); %! assert (TAB{6,7}, 7.82091664406755e-15, 1e-08); %!test %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! brands = bsxfun (@times, ones(6,1), [1,2,3]); %! popper = bsxfun (@times, [1;1;1;2;2;2], ones(1,3)); %! %! [TAB, STATS] = fitlm ({brands(:),popper(:)},popcorn(:),"interactions",... %! "categoricalvars",[1,2],"display","off"); %! assert (TAB{2,2}, 5.66666666666667, 1e-09); %! assert (TAB{3,2}, -1.33333333333333, 1e-09); %! assert (TAB{4,2}, -2.16666666666667, 1e-09); %! assert (TAB{5,2}, 1.16666666666667, 1e-09); %! assert (TAB{6,2}, -0.333333333333334, 1e-09); %! assert (TAB{7,2}, -0.166666666666667, 1e-09); %! assert (TAB{2,3}, 0.215165741455965, 1e-09); %! assert (TAB{3,3}, 0.304290309725089, 1e-09); %! assert (TAB{4,3}, 0.304290309725089, 1e-09); %! assert (TAB{5,3}, 0.304290309725089, 1e-09); %! assert (TAB{6,3}, 0.43033148291193, 1e-09); %! assert (TAB{7,3}, 0.43033148291193, 1e-09); %! assert (TAB{2,6}, 26.3362867542108, 1e-09); %! assert (TAB{3,6}, -4.38178046004138, 1e-09); %! assert (TAB{4,6}, -7.12039324756724, 1e-09); %! assert (TAB{5,6}, 3.83405790253621, 1e-09); %! assert (TAB{6,6}, -0.774596669241495, 1e-09); %! assert (TAB{7,6}, -0.387298334620748, 1e-09); %! assert (TAB{2,7}, 5.49841502258254e-12, 1e-09); %! assert (TAB{3,7}, 0.000893505495903642, 1e-09); %! assert (TAB{4,7}, 1.21291454302428e-05, 1e-09); %! assert (TAB{5,7}, 0.00237798044119407, 1e-09); %! assert (TAB{6,7}, 0.453570536021938, 1e-09); %! assert (TAB{7,7}, 0.705316781644046, 1e-09); %! ## Test with string ids for categorical variables %! brands = {'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'; ... %! 'Gourmet', 'National', 'Generic'}; %! popper = {'oil', 'oil', 'oil'; 'oil', 'oil', 'oil'; 'oil', 'oil', 'oil'; ... %! 'air', 'air', 'air'; 'air', 'air', 'air'; 'air', 'air', 'air'}; %! [TAB, STATS] = fitlm ({brands(:),popper(:)},popcorn(:),"interactions",... %! "categoricalvars",[1,2],"display","off"); %!test %! load carsmall %! X = [Weight,Horsepower,Acceleration]; %! [TAB, STATS] = fitlm (X, MPG,"constant","display","off"); %! [TAB, STATS] = fitlm (X, MPG,"linear","display","off"); %! assert (TAB{2,2}, 47.9767628118615, 1e-09); %! assert (TAB{3,2}, -0.00654155878851796, 1e-09); %! assert (TAB{4,2}, -0.0429433065881864, 1e-09); %! assert (TAB{5,2}, -0.0115826516894871, 1e-09); %! assert (TAB{2,3}, 3.87851641748551, 1e-09); %! assert (TAB{3,3}, 0.00112741016370336, 1e-09); %! assert (TAB{4,3}, 0.0243130608813806, 1e-09); %! assert (TAB{5,3}, 0.193325043113178, 1e-09); %! assert (TAB{2,6}, 12.369874881944, 1e-09); %! assert (TAB{3,6}, -5.80228828790225, 1e-09); %! assert (TAB{4,6}, -1.76626492228599, 1e-09); %! assert (TAB{5,6}, -0.0599128364487485, 1e-09); %! assert (TAB{2,7}, 4.89570341688996e-21, 1e-09); %! assert (TAB{3,7}, 9.87424814144e-08, 1e-09); %! assert (TAB{4,7}, 0.0807803098213114, 1e-09); %! assert (TAB{5,7}, 0.952359384151778, 1e-09); statistics-release-1.7.3/inst/fitrgam.m000066400000000000000000000204121475240274700201310ustar00rootroot00000000000000## Copyright (C) 2023 Mohammed Azmat Khan ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{obj} =} fitrgam (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{obj} =} fitrgam (@var{X}, @var{Y}, @var{name}, @var{value}) ## ## Fit a Generalised Additive Model (GAM) for regression. ## ## @code{@var{obj} = fitrgam (@var{X}, @var{Y})} returns an object of ## class RegressionGAM, with matrix @var{X} containing the predictor data and ## vector @var{Y} containing the continuous response data. ## ## @itemize ## @item ## @var{X} must be a @math{NxP} numeric matrix of input data where rows ## correspond to observations and columns correspond to features or variables. ## @var{X} will be used to train the GAM model. ## @item ## @var{Y} must be @math{Nx1} numeric vector containing the response data ## corresponding to the predictor data in @var{X}. @var{Y} must have same ## number of rows as @var{X}. ## @end itemize ## ## @code{@var{obj} = fitrgam (@dots{}, @var{name}, @var{value})} returns ## an object of class RegressionGAM with additional properties specified by ## @qcode{Name-Value} pair arguments listed below. ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Name} @tab @var{Value} ## ## @item @tab @qcode{"predictors"} @tab Predictor Variable names, specified as ## a row vector cell of strings with the same length as the columns in @var{X}. ## If omitted, the program will generate default variable names ## @qcode{(x1, x2, ..., xn)} for each column in @var{X}. ## ## @item @tab @qcode{"responsename"} @tab Response Variable Name, specified as ## a string. If omitted, the default value is @qcode{"Y"}. ## ## @item @tab @qcode{"formula"} @tab a model specification given as a string in ## the form @qcode{"Y ~ terms"} where @qcode{Y} represents the reponse variable ## and @qcode{terms} the predictor variables. The formula can be used to ## specify a subset of variables for training model. For example: ## @qcode{"Y ~ x1 + x2 + x3 + x4 + x1:x2 + x2:x3"} specifies four linear terms ## for the first four columns of for predictor data, and @qcode{x1:x2} and ## @qcode{x2:x3} specify the two interaction terms for 1st-2nd and 3rd-4th ## columns respectively. Only these terms will be used for training the model, ## but @var{X} must have at least as many columns as referenced in the formula. ## If Predictor Variable names have been defined, then the terms in the formula ## must reference to those. When @qcode{"formula"} is specified, all terms used ## for training the model are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object as a matrix containing the column indexes for each ## term including both the predictors and the interactions used. ## ## @item @tab @qcode{"interactions"} @tab a logical matrix, a positive integer ## scalar, or the string @qcode{"all"} for defining the interactions between ## predictor variables. When given a logical matrix, it must have the same ## number of columns as @var{X} and each row corresponds to a different ## interaction term combining the predictors indexed as @qcode{true}. Each ## interaction term is appended as a column vector after the available predictor ## column in @var{X}. When @qcode{"all"} is defined, then all possible ## combinations of interactions are appended in @var{X} before training. At the ## moment, parsing a positive integer has the same effect as the @qcode{"all"} ## option. When @qcode{"interactions"} is specified, only the interaction terms ## appended to @var{X} are referenced in the @qcode{IntMatrix} field of the ## @var{obj} class object. ## ## @item @tab @qcode{"knots"} @tab a scalar or a row vector with the same ## columns as @var{X}. It defines the knots for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 5, hence expanded to @qcode{ones (1, columns (X)) * 5}. You can ## parse a row vector with different number of knots for each predictor ## variable to be fitted with, although not recommended. ## ## @item @tab @qcode{"order"} @tab a scalar or a row vector with the same ## columns as @var{X}. It defines the order of the polynomial when training the ## GAM. As a scalar, it is expanded to a row vector. The default values is 3, ## hence expanded to @qcode{ones (1, columns (X)) * 3}. You can parse a row ## vector with different number of polynomial order for each predictor variable ## to be fitted with, although not recommended. ## ## @item @tab @qcode{"dof"} @tab a scalar or a row vector with the same columns ## as @var{X}. It defines the degrees of freedom for fitting a polynomial when ## training the GAM. As a scalar, it is expanded to a row vector. The default ## value is 8, hence expanded to @qcode{ones (1, columns (X)) * 8}. You can ## parse a row vector with different degrees of freedom for each predictor ## variable to be fitted with, although not recommended. ## ## @item @tab @qcode{"tol"} @tab a positive scalar to set the tolerance for ## covergence during training. By defaul, it is set to @qcode{1e-3}. ## @end multitable ## ## You can parse either a @qcode{"formula"} or an @qcode{"interactions"} ## optional parameter. Parsing both parameters will result an error. ## Accordingly, you can only pass up to two parameters among @qcode{"knots"}, ## @qcode{"order"}, and @qcode{"dof"} to define the required polynomial for ## training the GAM model. ## ## @seealso{RegressionGAM, regress, regress_gp} ## @end deftypefn function obj = fitrgam (X, Y, varargin) ## Check input parameters if (nargin < 2) error ("fitrgam: too few arguments."); endif if (mod (nargin, 2) != 0) error ("fitrgam: Name-Value arguments must be in pairs."); endif ## Check predictor data and labels have equal rows if (rows (X) != rows (Y)) error ("fitrgam: number of rows in X and Y must be equal."); endif ## Parse arguments to class def function obj = RegressionGAM (X, Y, varargin{:}); endfunction %!demo %! # Train a RegressionGAM Model for synthetic values %! %! f1 = @(x) cos (3 *x); %! f2 = @(x) x .^ 3; %! %! # generate x1 and x2 for f1 and f2 %! x1 = 2 * rand (50, 1) - 1; %! x2 = 2 * rand (50, 1) - 1; %! %! # calculate y %! y = f1(x1) + f2(x2); %! %! # add noise %! y = y + y .* 0.2 .* rand (50,1); %! X = [x1, x2]; %! %! # create an object %! a = fitrgam (X, y, "tol", 1e-3) ## Test constructor %!test %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [1; 2; 3; 4]; %! a = fitrgam (x, y); %! assert ({a.X, a.Y}, {x, y}) %! assert ({a.BaseModel.Intercept}, {2.5000}) %! assert ({a.Knots, a.Order, a.DoF}, {[5, 5, 5], [3, 3, 3], [8, 8, 8]}) %! assert ({a.NumObservations, a.NumPredictors}, {4, 3}) %! assert ({a.ResponseName, a.PredictorNames}, {"Y", {"x1", "x2", "x3"}}) %! assert ({a.Formula}, {[]}) %!test %! x = [1, 2, 3, 4; 4, 5, 6, 7; 7, 8, 9, 1; 3, 2, 1, 2]; %! y = [1; 2; 3; 4]; %! pnames = {"A", "B", "C", "D"}; %! formula = "Y ~ A + B + C + D + A:C"; %! intMat = logical ([1,0,0,0;0,1,0,0;0,0,1,0;0,0,0,1;1,0,1,0]); %! a = fitrgam (x, y, "predictors", pnames, "formula", formula); %! assert ({a.IntMatrix}, {intMat}) %! assert ({a.ResponseName, a.PredictorNames}, {"Y", pnames}) %! assert ({a.Formula}, {formula}) ## Test input validation %!error fitrgam () %!error fitrgam (ones(10,2)) %!error %! fitrgam (ones (4,2), ones (4, 1), "K") %!error %! fitrgam (ones (4,2), ones (3, 1)) %!error %! fitrgam (ones (4,2), ones (3, 1), "K", 2) statistics-release-1.7.3/inst/friedman.m000066400000000000000000000151241475240274700202710ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} friedman (@var{x}) ## @deftypefnx {statistics} {@var{p} =} friedman (@var{x}, @var{reps}) ## @deftypefnx {statistics} {@var{p} =} friedman (@var{x}, @var{reps}, @var{displayopt}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}] =} friedman (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{atab}, @var{stats}] =} friedman (@dots{}) ## ## Performs the nonparametric Friedman's test to compare column effects in a ## two-way layout. @qcode{friedman} tests the null hypothesis that the column ## effects are all the same against the alternative that they are not all the ## same. ## ## @qcode{friedman} requires one up to three input arguments: ## ## @itemize ## @item ## @var{x} contains the data and it must be a matrix of at least two columns and ## two rows. ## @item ## @var{reps} is the number of replicates for each combination of factor groups. ## If not provided, no replicates are assumed. ## @item ## @var{displayopt} is an optional parameter for displaying the Friedman's ANOVA ## table, when it is 'on' (default) and suppressing the display when it is 'off'. ## @end itemize ## ## @qcode{friedman} returns up to three output arguments: ## ## @itemize ## @item ## @var{p} is the p-value of the null hypothesis that all group means are equal. ## @item ## @var{atab} is a cell array containing the results in a Friedman's ANOVA table. ## @item ## @var{stats} is a structure containing statistics useful for performing ## a multiple comparison of medians with the MULTCOMPARE function. ## @end itemize ## ## If friedman is called without any output arguments, then it prints the results ## in a one-way ANOVA table to the standard output as if @var{displayopt} is ## 'on'. ## ## Examples: ## ## @example ## load popcorn; ## friedman (popcorn, 3); ## @end example ## ## ## @example ## [p, anovatab, stats] = friedman (popcorn, 3, "off"); ## disp (p); ## @end example ## ## @seealso{anova2, kruskalwallis, multcompare} ## @end deftypefn function [p, table, stats] = friedman (x, reps, displayopt) ## Check for valid number of input arguments narginchk (1, 3); ## Check for NaN values in X if (any (isnan( x(:)))) error ("NaN values in input are not allowed."); endif ## Add defaults if (nargin == 1) reps = 1; endif ## Check for correct size of input matrix [r, c] = size (x); if (r <= 1 || c <= 1) error ("Bad size of input matrix."); endif if (reps > 1) r = r / reps; if (floor (r) != r) error ("Repetitions and observations do not match."); endif endif ## Check for displayopt if (nargin < 3) displayopt = 'on'; elseif ! (strcmp (displayopt, "off") || strcmp (displayopt, "on")) error ("displayopt must be either 'on' (default) or 'off'."); endif plotdata = ~(strcmp (displayopt, "off")); ## Prepare a matrix of ranks. Replicates are ranked together. m = x; sum_R = 0; for j = 1:r jrows = reps * (j-1) + (1:reps); v = x(jrows,:); [R, tieadj] = tiedrank (v(:)); m(jrows,:) = reshape (R, reps, c);; sum_R = sum_R + 2 * tieadj; endfor ## Perform 2-way anova silently [p0, table] = anova2 (m, reps, 'off'); ## Compute Friedman test statistic and p-value chi_r = table{2,2}; sigmasq = c * reps * (reps * c + 1) / 12; if (sum_R > 0) sigmasq = sigmasq - sum_R / (12 * r * (reps * c - 1)); endif if (chi_r > 0) chi_r = chi_r / sigmasq; endif p = 1 - chi2cdf (chi_r, c - 1); ## Remove row info from ANOVA2 table table(3,:) = []; ## Remove interaction chi-sq and p-value, if there are repetitive measuments if (reps > 1) table{3,5} = []; table{3,6} = []; endif ## Fix test statistic names table{1,5} = "Chi-sq"; table{1,6} = "Prob>Chi-sq\n"; ## Fix test statistic values table{2,5} = chi_r; table{2,6} = p; ## Create stats structure (if requested) for MULTCOMPARE if (nargout > 2) stats.source = 'friedman'; stats.n = r; stats.meanranks = mean (m); stats.sigma = sqrt (sigmasq); endif ## Print results table on screen if no output argument was requested if (nargout == 0 || plotdata) printf(" Friedman's ANOVA Table\n"); printf("Source SS df MS Chi-sq Prob>Chi-sq\n"); printf("---------------------------------------------------------------\n"); printf("Columns %10.4f %5.0f %10.4f %8.2f %9.4f\n", ... table{2,2}, table{2,3}, table{2,4}, table{2,5}, table{2,6}); if reps > 1 printf("Interaction %10.4f %5.0f %10.4f %8.2f %9.4f\n", ... table{3,2}, table{3,3}, table{3,4}, table{3,5}, table{3,6}); endif printf("Error %10.4f %5.0f %10.4f\n", ... table{end-1,2}, table{end-1,3}, table{end-1,4}); printf("Total %10.4f %5.0f\n", table{end,2}, table{end,3}); endif endfunction %!demo %! load popcorn; %! friedman (popcorn, 3); %!demo %! load popcorn; %! [p, atab] = friedman (popcorn, 3, "off"); %! disp (p); ## testing against popcorn data and results from Matlab %!test %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! [p, atab] = friedman (popcorn, 3, "off"); %! assert (p, 0.001028853354594794, 1e-14); %! assert (atab{2,2}, 99.75, 1e-14); %! assert (atab{2,3}, 2, 0); %! assert (atab{2,4}, 49.875, 1e-14); %! assert (atab{2,5}, 13.75862068965517, 1e-14); %! assert (atab{2,6}, 0.001028853354594794, 1e-14); %! assert (atab{3,2}, 0.08333333333333215, 1e-14); %! assert (atab{3,4}, 0.04166666666666607, 1e-14); %! assert (atab{4,3}, 12, 0); %!test %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! [p, atab, stats] = friedman (popcorn, 3, "off"); %! assert (atab{5,2}, 116, 0); %! assert (atab{5,3}, 17, 0); %! assert (stats.source, "friedman"); %! assert (stats.n, 2); %! assert (stats.meanranks, [8, 4.75, 2.25], 0); %! assert (stats.sigma, 2.692582403567252, 1e-14); statistics-release-1.7.3/inst/fullfact.m000066400000000000000000000053671475240274700203140ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## based on public domain work by Paul Kienzle ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{A} =} fullfact (@var{N}) ## ## Full factorial design. ## ## If @var{N} is a scalar, return the full factorial design with @var{N} binary ## choices, 0 and 1. ## ## If @var{N} is a vector, return the full factorial design with ordinal choices ## 1 through @var{n_i} for each factor @var{i}. ## ## Values in @var{N} must be positive integers. ## ## @seealso {ff2n} ## @end deftypefn function A = fullfact(n) if (nargin != 1) error ("fullfact: wrong number of input arguments."); endif if length(n) == 1 if (floor (n) != n || n < 1 || ! isfinite (n) || ! isreal (n)) error ("fullfact: @var{N} must be a positive integer."); endif ## Combinatorial design with n binary choices A = fullfact(2*ones(1,n))-1; else if (any (floor (n) != n) || any (n < 1) || any (! isfinite (n)) ... || any (! isreal (n))) error ("fullfact: values in @var{N} must be positive integers."); endif ## Combinatorial design with n(i) ordinal choices A = [1:n(end)]'; for i=length(n)-1:-1:1 A = [kron([1:n(i)]',ones(rows(A),1)), repmat(A,n(i),1)]; end end endfunction %!demo %! ## Full factorial design with 3 binary variables %! fullfact (3) %!demo %! ## Full factorial design with 3 ordinal variables %! fullfact ([2, 3, 4]) %!error fullfact (); %!error fullfact (2, 5); %!error fullfact (2.5); %!error fullfact (0); %!error fullfact (-3); %!error fullfact (3+2i); %!error fullfact (Inf); %!error fullfact (NaN); %!error fullfact ([1, 2, -3]); %!error fullfact ([0, 1, 2]); %!error fullfact ([1, 2, NaN]); %!error fullfact ([1, 2, Inf]); %!test %! A = fullfact (2); %! assert (A, [0, 0; 0, 1; 1, 0; 1, 1]); %!test %! A = fullfact ([1, 2]); %! assert (A, [1, 1; 1, 2]); %!test %! A = fullfact ([1, 2, 4]); %! A_out = [1, 1, 1; 1, 1, 2; 1, 1, 3; 1, 1, 4; ... %! 1, 2, 1; 1, 2, 2; 1, 2, 3; 1, 2, 4]; %! assert (A, A_out); statistics-release-1.7.3/inst/geomean.m000066400000000000000000000233111475240274700201140ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{m} =} geomean (@var{x}) ## @deftypefnx {statistics} {@var{m} =} geomean (@var{x}, "all") ## @deftypefnx {statistics} {@var{m} =} geomean (@var{x}, @var{dim}) ## @deftypefnx {statistics} {@var{m} =} geomean (@var{x}, @var{vecdim}) ## @deftypefnx {statistics} {@var{m} =} geomean (@dots{}, @var{nanflag}) ## ## Compute the geometric mean of @var{x}. ## ## @itemize ## @item If @var{x} is a vector, then @code{geomean(@var{x})} returns the ## geometric mean of the elements in @var{x} defined as ## @tex ## $$ {\rm geomean}(x) = \left( \prod_{i=1}^N x_i \right)^\frac{1}{N} ## = exp \left({1\over N} \sum_{i=1}^N log x_i \right) $$ ## ## @end tex ## @ifnottex ## ## @example ## geomean (@var{x}) = PROD_i @var{x}(i) ^ (1/N) ## @end example ## ## @end ifnottex ## @noindent ## where @math{N} is the length of the @var{x} vector. ## ## @item If @var{x} is a matrix, then @code{geomean(@var{x})} returns a row ## vector with the geometric mean of each columns in @var{x}. ## ## @item If @var{x} is a multidimensional array, then @code{geomean(@var{x})} ## operates along the first nonsingleton dimension of @var{x}. ## ## @item @var{x} must not contain any negative or complex values. ## @end itemize ## ## @code{geomean(@var{x}, "all")} returns the geometric mean of all the elements ## in @var{x}. If @var{x} contains any 0, then the returned value is 0. ## ## @code{geomean(@var{x}, @var{dim})} returns the geometric mean along the ## operating dimension @var{dim} of @var{x}. Calculating the harmonic mean of ## any subarray containing any 0 will return 0. ## ## @code{geomean(@var{x}, @var{vecdim})} returns the geometric mean over the ## dimensions specified in the vector @var{vecdim}. For example, if @var{x} is ## a 2-by-3-by-4 array, then @code{geomean(@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the geometric mean of ## the elements on the corresponding page of @var{x}. If @var{vecdim} indexes ## all dimensions of @var{x}, then it is equivalent to @code{geomean (@var{x}, ## "all")}. Any dimension in @var{vecdim} greater than @code{ndims (@var{x})} ## is ignored. ## ## @code{geomean(@dots{}, @var{nanflag})} specifies whether to exclude NaN ## values from the calculation, using any of the input argument combinations in ## previous syntaxes. By default, geomean includes NaN values in the calculation ## (@var{nanflag} has the value "includenan"). To exclude NaN values, set the ## value of @var{nanflag} to "omitnan". ## ## @seealso{harmmean, mean} ## @end deftypefn function m = geomean (x, varargin) if (nargin < 1 || nargin > 3) print_usage (); endif if (! isnumeric (x) || ! isreal (x) || ! all (x(! isnan (x))(:) >= 0)) error ("geomean: X must contain real nonnegative values."); endif ## Set initial conditions all_flag = false; omitnan = false; nvarg = numel (varargin); varg_chars = cellfun ("ischar", varargin); szx = size (x); ndx = ndims (x); if (nvarg > 1 && ! varg_chars(2:end)) ## Only first varargin can be numeric print_usage (); endif ## Process any other char arguments. if (any (varg_chars)) for i = varargin(varg_chars) switch (lower (i{:})) case "all" all_flag = true; case "omitnan" omitnan = true; case "includenan" omitnan = false; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif ## Single numeric input argument, no dimensions given. if (nvarg == 0) if (all_flag) x = x(:); if (omitnan) x = x(! isnan (x)); endif if (any (x == 0)) m = 0; return; endif m = exp (sum (log (x), 1) ./ length (x)); else ## Find the first non-singleton dimension. (dim = find (szx != 1, 1)) || (dim = 1); n = szx(dim); if (omitnan) idx = isnan (x); n = sum (! idx, dim); x(idx) = 1; # log (1) = 0 endif m = exp (sum (log (x), dim) ./ n); m(m == -Inf) = 0; # handle zeros in X endif else ## Two numeric input arguments, dimensions given. Note scalar is vector! vecdim = varargin{1}; if (isempty (vecdim) || ! (isvector (vecdim) && all (vecdim > 0)) ... || any (rem (vecdim, 1))) error ("geomean: DIM must be a positive integer scalar or vector."); endif if (ndx == 2 && isempty (x) && szx == [0,0]) ## FIXME: this special case handling could be removed once sum ## compatibly handles all sizes of empty inputs sz_out = szx; sz_out (vecdim(vecdim <= ndx)) = 1; m = NaN (sz_out); else if (isscalar (vecdim)) if (vecdim > ndx) m = x; else n = szx(vecdim); if (omitnan) nanx = isnan (x); n = sum (! nanx, vecdim); x(nanx) = 1; # log (1) = 0 endif m = exp (sum (log (x), vecdim) ./ n); m(m == -Inf) = 0; # handle zeros in X endif else vecdim = sort (vecdim); if (! all (diff (vecdim))) error (strcat (["geomean: VECDIM must contain non-repeating"], ... [" positive integers."])); endif ## Ignore exceeding dimensions in VECDIM vecdim(find (vecdim > ndims (x))) = []; if (isempty (vecdim)) m = x; else ## Move vecdims to dim 1. ## Calculate permutation vector remdims = 1 : ndx; # All dimensions remdims(vecdim) = []; # Delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is similar to all flag if (nremd == 0) x = x(:); if (omitnan) x = x(! isnan (x)); endif if (any (x == 0)) m = 0; return; endif m = exp (sum (log (x), 1) ./ length (x)); m(m == -Inf) = 0; # handle zeros in X else ## Permute to bring vecdims to front perm = [vecdim, remdims]; x = permute (x, perm); ## Reshape to squash all vecdims in dim1 num_dim = prod (szx(vecdim)); szx(vecdim) = []; szx = [ones(1, length(vecdim)), szx]; szx(1) = num_dim; x = reshape (x, szx); ## Calculate mean on dim1 if (omitnan) nanx = isnan (x); n = sum (! nanx, 1); x(nanx) = 1; # log (1) = 0 else n = szx(1); endif m = exp (sum (log (x), 1) ./ n); m(m == -Inf) = 0; # handle zeros in X ## Inverse permute back to correct dimensions m = ipermute (m, perm); endif endif endif endif endif endfunction ## Test single input and optional arguments "all", DIM, "omitnan") %!test %! x = [0:10]; %! y = [x;x+5;x+10]; %! assert (geomean (x), 0); %! m = [0 9.462942809849169 14.65658770861967]; %! assert (geomean (y, 2), m', 4e-14); %! assert (geomean (y, "all"), 0); %! y(2,4) = NaN; %! m(2) = 9.623207231679554; %! assert (geomean (y, 2), [0 NaN m(3)]', 4e-14); %! assert (geomean (y', "omitnan"), m, 4e-14); %! z = y + 20; %! assert (geomean (z, "all"), NaN); %! assert (geomean (z, "all", "includenan"), NaN); %! assert (geomean (z, "all", "omitnan"), 29.59298474535024, 4e-14); %! m = [24.79790781765634 NaN 34.85638839503932]; %! assert (geomean (z'), m, 4e-14); %! assert (geomean (z', "includenan"), m, 4e-14); %! m(2) = 30.02181156156319; %! assert (geomean (z', "omitnan"), m, 4e-14); %! assert (geomean (z, 2, "omitnan"), m', 4e-14); ## Test dimension indexing with vecdim in n-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (geomean (x, [3 2])), [10 1 1 3]); %! assert (size (geomean (x, [1 2])), [1 1 6 3]); %! assert (size (geomean (x, [1 2 4])), [1 1 6]); %! assert (size (geomean (x, [1 4 3])), [1 40]); %! assert (size (geomean (x, [1 2 3 4])), [1 1]); ## Test results with vecdim in n-dimensional arrays and "omitnan" %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! m = repmat ([8.304361203739333;14.3078118884256], [5 1 1 3]); %! assert (geomean (x, [3 2]), m, 4e-13); %! x(2,5,6,3) = NaN; %! m(2,3) = NaN; %! assert (geomean (x, [3 2]), m, 4e-13); %! m(2,3) = 14.3292729579901; %! assert (geomean (x, [3 2], "omitnan"), m, 4e-13); ## Test errors %!error geomean ("char") %!error geomean ([1 -1 3]) %!error ... %! geomean (repmat ([1:20;6:25], [5 2 6 3 5]), -1) %!error ... %! geomean (repmat ([1:20;6:25], [5 2 6 3 5]), 0) %!error ... %! geomean (repmat ([1:20;6:25], [5 2 6 3 5]), [1 1]) statistics-release-1.7.3/inst/glmfit.m000066400000000000000000000732751475240274700200010ustar00rootroot00000000000000## Copyright (C) 2024 Ruchika Sonagote ## Copyright (C) 2025 Swayam Shah ## Copyright (C) 2024-2025 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{b} =} glmfit (@var{X}, @var{y}, @var{distribution}) ## @deftypefnx {statistics} {@var{b} =} glmfit (@var{X}, @var{y}, @var{distribution}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{b}, @var{dev}] =} glmfit (@dots{}) ## @deftypefnx {statistics} {[@var{b}, @var{dev}, @var{stats}] =} glmfit (@dots{}) ## ## Perform generalized linear model fitting. ## ## @code{@var{b} = glmfit (@var{X}, @var{y}, @var{distribution})} returns a ## vector @var{b} of coefficient estimates for a generalized linear regression ## model of the responses in @var{y} on the predictors in @var{X}, using the ## distribution defined in @var{distribution}. ## ## @itemize ## @item @var{X} is an @math{nxp} numeric matrix of predictor variables with ## @math{n} observations and @math{p} predictors. ## @item @var{y} is an @math{nx1} numeric vector of responses for all supported ## distributions, except for the 'binomial' distribution in which case @var{y} ## can be either a numeric or logical @math{nx1} vector or an @math{nx2} ## matrix, where the first column contains the number of successes and the ## second column contains the number of trials. ## @item @var{distribution} is a character vector specifying the distribution of ## the response variable. Supported distributions are @qcode{"normal"}, ## @qcode{"binomial"}, @qcode{"poisson"}, @qcode{"gamma"}, and @qcode{"inverse ## gaussian"}. ## @end itemize ## ## @code{@var{b} = glmfit (@dots{}, @var{Name}, @var{Value})} specifies ## additional options using @qcode{Name-Value} pair arguments. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"B0"} @tab @tab A numeric vector specifying initial values for ## the coefficient estimates. By default, the initial values are fitted values ## fitted from the data. ## ## @item @qcode{"Constant"} @tab @tab A character vector specifying whether to ## include a constant term in the model. Valid options are @var{"on"} (default) ## and @var{"off"}. ## ## @item @qcode{"EstDisp"} @tab @tab A character vector specifying whether to ## compute dispersion parameter. Valid options are @var{"on"} and @var{"off"}. ## For @qcode{"binomial"} and @qcode{"poisson"} distributions the default is ## @var{"off"}, whereas for the @qcode{"normal"}, @qcode{"gamma"}, and ## @qcode{"inverse gaussian"} distributions the default is @var{"on"}. ## ## @item @qcode{"link"} @tab @tab A character vector specifying the name of a ## canonical link function or a numeric scalar for specifying a @qcode{"power"} ## link function. Supported canonical link functions include @qcode{"identity"} ## (default for @qcode{"normal"} distribution), @qcode{"log"} (default for ## @qcode{"poisson"} distribution), @qcode{"logit"} (default for ## @qcode{"binomial"} distribution), @qcode{"probit"}, @qcode{"loglog"}, ## @qcode{"comploglog"}, and @qcode{"reciprocal"} (default for the ## @qcode{"gamma"} distribution). The @qcode{"power"} link function is the ## default for the @qcode{"inverse gaussian"} distribution with @math{p = -2}. ## For custom link functions, the user can provide cell array with three ## function handles: the link function, its derivative, and its inverse, or ## alternatively a structure @var{S} with three fields: @qcode{S.Link}, ## @qcode{S.Derivative}, and @qcode{S.Inverse}. Each field can either contain a ## function handle or a character vector with the name of an existing function. ## All custom link functions must accept a vector of inputs and return a vector ## of the same size. ## ## @item @qcode{"Offset"} @tab @tab A numeric vector of the same length as the ## response @var{y} specifying an offset variable in the fit. It is used as an ## additional predictor with a coefficient value fixed at 1. ## ## @item @qcode{"Options"} @tab @tab A scalar structure containing the fields ## @qcode{MaxIter} and @qcode{TolX}. @qcode{MaxIter} must be a scalar positive ## integer specifying the maximum number of iteration allowed for fitting the ## model, and @qcode{TolX} must be a positive scalar value specifying the ## termination tolerance. ## ## @item @qcode{"Weights"} @tab @tab An @math{nx1} numeric vector of nonnegative ## values, where @math{n} is the number of observations in @var{X}. By default, ## it is @code{ones (n, 1)}. ## @end multitable ## ## @code{[@var{b}, @var{dev}] = glmfit (@dots{})} also returns the deviance of ## the fit as a numeric value in @var{dev}. Deviance is a generalization of the ## residual sum of squares. It measures the goodness of fit compared to a ## saturated model. ## ## @code{[@var{b}, @var{dev}, @var{stats}] = glmfit (@dots{})} also returns the ## structure @var{stats}, which contains the model statistics in the following ## fields: ## ## @itemize ## @item @qcode{beta} - Coefficient estimates @var{b} ## @item @qcode{dfe} - Degrees of freedom for error ## @item @qcode{sfit} - Estimated dispersion parameter ## @item @qcode{s} - Theoretical or estimated dispersion parameter ## @item @qcode{estdisp} - @code{false} when @qcode{"EstDisp"} is @qcode{"off"} ## and @code{true} when @qcode{"EstDisp"} is @qcode{"on"} ## @item @qcode{covb} - Estimated covariance matrix for @var{b} ## @item @qcode{se} - Vector of standard errors of the coefficient estimates ## @var{b} ## @item @qcode{coeffcorr} - Correlation matrix for @var{b} ## @item @qcode{t} - @math{t} statistics for @var{b} ## @item @qcode{p} - @math{p}-values for @var{b} ## @item @qcode{resid} - Vector of residuals ## @item @qcode{residp} - Vector of Pearson residuals ## @item @qcode{residd} - Vector of deviance residuals ## @item @qcode{resida} - Vector of Anscombe residuals ## @end itemize ## ## @seealso{glmval} ## @end deftypefn function [b, dev, stats] = glmfit (X, y, distribution, varargin) ## Check input arguments if (nargin < 3) error ("glmfit: too few input arguments."); elseif (mod (nargin - 3, 2) != 0) error ("glmfit: Name-Value arguments must be in pairs."); elseif (! isnumeric (X) || isempty (X)) error ("glmfit: X must be a numeric matrix."); elseif (! (isnumeric (y) || islogical (y)) || isempty (y)) error ("glmfit: Y must be either a numeric matrix or a logical vector."); elseif (size (X, 1) != size (y, 1)) error ("glmfit: X and Y must have the same number of observations."); elseif (! ischar (distribution)) error ("glmfit: DISTRIBUTION must be a character vector."); endif ## Remove missing values xymissing = any (isnan (y), 2) | any (isnan (X), 2); y(xymissing) = []; X(xymissing,:) = []; [ny, cy] = size (y); [nx, cx] = size (X); ## Check y dimensions based on distribution if (strcmpi (distribution, 'binomial')) if (cy > 2) error (["glmfit: for a 'binomial' distribution,", ... " Y must be an n-by-1 or n-by-2 matrix."]); ## Get y and N for binomial distribution elseif (cy == 2) if (! isnumeric (y)) error (["glmfit: n-by-2 matrix Y for 'binomial' distribution", ... " must be numeric."]); endif N = y(:, 2); y = y(:, 1) ./ N; else if (islogical (y)) y = double (y); endif N = ones (size (y)); endif else if (cy != 1) error (["glmfit: for distributions other than 'binomial',", ... " Y must be an n-by-1 column vector."]); endif endif ## Set default link, variance, and deviance functions ## Set defaults for estimating dispersion parameter and limiting mu switch (tolower (distribution)) case "normal" [flink, dlink, ilink] = getlinkfunctions ("identity"); varFun = @(mu) ones (size (mu)); devFun = @(mu, y) (y - mu) .^ 2; estDisp = true; case "binomial" [flink, dlink, ilink] = getlinkfunctions ("logit"); varFun = @(mu, N) sqrt (mu) .* sqrt (1 - mu) ./ sqrt (N); devFun = @(mu, y, N) 2 * N .* (y .* log ((y + (y == 0)) ./ mu) + ... (1 - y) .* log ((1 - y + (y == 1)) ./ (1 - mu))); estDisp = false; muLimits = [eps, 1-eps]; case "poisson" [flink, dlink, ilink] = getlinkfunctions ("log"); varFun = @(mu) sqrt (mu); devFun = @(mu, y) 2 * (y .* (log ((y + (y == 0)) ./ mu)) - (y - mu)); estDisp = false; muLimits = realmin; case "gamma" [flink, dlink, ilink] = getlinkfunctions ("reciprocal"); varFun = @(mu) mu; devFun = @(mu, y) 2 * (-log (y ./ mu) + (y - mu) ./ mu); estDisp = true; muLimits = realmin; case "inverse gaussian" [flink, dlink, ilink] = getlinkfunctions (-2); varFun = @(mu) mu .^ (3 / 2); devFun = @(mu, y) (((y - mu) ./ mu) .^ 2) ./ y; estDisp = true; muLimits = realmin; otherwise error ("glmfit: unsupported distribution."); endswitch ## Set defaults B0 = []; constant = true; offset = zeros (nx, 1); weight = ones (nx, 1); MaxIter = 100; TolX = 1e-6; ## Parse extra parameters while (numel (varargin) > 0) switch (tolower (varargin {1})) case "b0" B0 = varargin {2}; if (! (isnumeric (B0) && isequal (size (B0), size (xymissing)))) error ("glmfit: 'B0' must be a numeric vector of the same size as Y."); endif B0(xymissing) = []; case "constant" constant = tolower (varargin {2}); if (strcmpi (constant, "on")) constant = true; elseif (strcmpi (constant, "off")) constant = false; else error ("glmfit: 'Constant' should be either 'on' or 'off'."); endif case "estdisp" estDisp = tolower (varargin {2}); if (strcmpi (estDisp, "on")) estDisp = true; elseif (strcmpi (estDisp, "off")) estDisp = false; else error ("glmfit: 'EstDisp' should be either 'on' or 'off'."); endif case "link" linkArg = varargin {2}; ## Input validation is performed in private function [flink, dlink, ilink, errmsg] = getlinkfunctions (linkArg); if (! isempty (errmsg)) error ("glmfit: %s", errmsg); endif case "options" options = varargin {2}; rf = {"MaxIter", "TolX"}; if (! (isstruct (options) && all (ismember (rf, fieldnames (options))))) error (["glmfit: 'Options' must be a structure containing", ... " the fields 'MaxIter', and 'TolX'."]); endif MaxIter = options.MaxIter; TolX = options.TolX; if (! isscalar (MaxIter) || MaxIter <= 0 || fix (MaxIter) != MaxIter) error (["glmfit: 'MaxIter' in 'Options' structure must", ... " be a positive integer."]); endif if (! isscalar (TolX) || TolX <= 0) error (["glmfit: 'TolX' in 'Options' structure must", ... " be a positive scalar."]); endif case "offset" offset = varargin {2}; if (! (isnumeric (offset) && isequal (size (offset), size (xymissing)))) error (["glmfit: 'Offset' must be a numeric vector", ... " of the same size as Y."]); endif offset(xymissing) = []; case "weights" weight = varargin {2}; if (! (isnumeric (weight) && isequal (size (weight), size (xymissing)))) error (["glmfit: 'Weights' must be a numeric vector", ... " of the same size as Y."]); endif weight(xymissing) = []; otherwise error ("glmfit: unknown parameter name."); endswitch varargin (1:2) = []; endwhile ## Adjust X based on constant if (constant) X = [ones(nx, 1), X]; cx += 1; endif ## Check X for rank deficiency [~, R, P] = qr (X .* weight(:, ones (1, cx)), 0); if (isempty (R)) rankX = 0; else rankX = sum (abs (diag (R)) > abs (R(1)) * max (nx, cx) * eps); endif if (rankX < cx) warning ("glmfit: X is ill-conditioned."); P = P(1:rankx); X = X(:,P); else P = [1:cx]; endif ## Adjust number of observations for zero weights if (any (weight == 0)) nx = nx - sum (weight == 0); endif ## Initialize mu and eta if (isempty (B0)) # from y switch (distribution) case "binomial" mu = (N .* y + 0.5) ./ (N + 1); case "poisson" mu = y + 0.25; case {"gamma", "inverse gaussian"} mu = max (y, eps); otherwise mu = y; endswitch eta = flink (mu); else # from coefficient estimates eta = offset + X * B0(:); mu = ilink (eta); endif ## Initialize coefficient vector and iterations numc = size (X, 2); Bnew = zeros (numc, 1); seps = sqrt (eps); iter = 0; while (iter < MaxIter) iter += 1; Bold = Bnew; ## Compute iteratively reweighted least squares weights d_eta = dlink (mu); if (strcmpi (distribution, "binomial")) IRLS = abs (d_eta) .* varFun (mu, N); else IRLS = abs (d_eta) .* varFun (mu); endif squaredWeight = sqrt (weight) ./ IRLS; ## Estimate coefficients z_off = eta + (y - mu) .* d_eta - offset; yweighted = (z_off) .* squaredWeight; Xweighted = X .* squaredWeight(:, ones (1, numc)); [Q, R] = qr (Xweighted, 0); Bnew = R \ (Q' * yweighted); ## Compute predicted mean using current linear predictor eta = offset + X * Bnew; mu = ilink (eta); ## Force predicted mean within distribution support limits if (strcmpi (distribution, "normal")) mu = mu; elseif (strcmpi (distribution, "binomial")) mu = max (min (mu, muLimits(2)), muLimits(1)); else # for "poisson", "gamma", and "inverse gaussian" distributions mu = max (mu, muLimits(1)); endif ## Break if TolX is reached if (! any (abs (Bnew - Bold) > TolX * max (seps, abs (Bold)))) break; endif endwhile ## Warn if iteration limit is reached if (iter == MaxIter) warning ("glmfit: maximum number of iterations has been reached."); endif ## Return estimated coefficients if (rankX < cx) b = zeros (cx, 1); b(P) = Bnew; else b = Bnew; endif ## Compute deviance if (nargout > 1) if (strcmpi (distribution, "binomial")) devn = devFun (mu, y, N); dev = sum (weight .* devn); else devn = devFun (mu, y); dev = sum (weight .* devn); endif endif ## Compute stats if (nargout > 2) ## Store coefficient estimates stats.beta = bb; ## Compute degrees of freedom for error stats.dfe = max (nx - numc, 0); ## Compute estimated dispersion parameter if (stats.dfe > 0) switch (tolower (distribution)) case "normal" stats.sfit = sum (weight .* (y - mu) .^ 2) / stats.dfe; case "binomial" stats.sfit = sum (weight .* (y - mu) .^ 2 ./ ... (mu .* (1 - mu) ./ N)) / stats.dfe; case "poisson" stats.sfit = sum (weight .* (y - mu) .^ 2 ./ mu) / stats.dfe; case "gamma" stats.sfit = sum (weight .* ((y - mu) ./ mu) .^ 2) / stats.dfe; case "inverse gaussian" stats.sfit = sum (weight .* ((y - mu) ./ mu .^ (3 / 2)) .^ 2) / ... stats.dfe; endswitch else stats.sfit = NaN; endif ## Store theoretical or estimated dispersion parameter if (estDisp) stats.s = stats.sfit; stats.estdisp = estDisp; else stats.s = 1; stats.estdisp = estDisp; endif ## Compute covariance matrix, standard errors, correlation matrix, ## t-statistic, and p-value for coefficient estimates if (isnan (stats.s)) stats.covb = NaN (numel (b)); stats.se = NaN (size (b)); stats.coeffcorr = NaN (numel (b)); stats.t = NaN (size (b)); stats.p = NaN (size (b)); else stats.covb = zeros (cx, cx); stats.se = zeros (cx, 1); stats.coeffcorr = zeros (cx, cx); stats.t = NaN (cx, 1); stats.p = NaN (cx, 1); RI = R \ eye (numc); C = RI * RI'; if (estDisp) C = C * stats.s ^ 2; endif stats.covb(P,P) = C; se = sqrt (diag (C)); se = se(:); stats.se(P) = se; C = C ./ (se * se'); stats.coeffcorr(P,P) = C; stats.t(P) = b ./ se; if (estDisp) stats.p = 2 * tcdf (-abs (stats.t), dfe); else stats.p = 2 * normcdf (-abs (stats.t)); endif endif ## Compute residuals stats.resid = NaN (size (xymissing)); # Vector of residuals stats.residp = NaN (size (xymissing)); # Vector of Pearson residuals stats.residd = NaN (size (xymissing)); # Vector of deviance residuals stats.resida = NaN (size (xymissing)); # Vector of Anscombe residuals if (isequal (distribution, 'binomial')) stats.resid(! xymissing) = (y - mu) .* N; stats.residp(! xymissing) = (y - mu) ./ (varFun (mu, N) + (y == mu)); else stats.resid(! xymissing) = y - mu; stats.residp(! xymissing) = (y - mu) ./ (varFun (mu) + (y == mu)); end stats.residd(! xymissing) = sign (y - mu) .* sqrt (max (0, divn)); switch (tolower (distribution)) case "normal" stats.resida(! xymissing) = y - mu; case "binomial" a = b = 2 / 3; stats.resida(! xymissing) = beta(a, b) ... * (betainc (y, a, b) - betainc (mu, a, b)) ... ./ ((mu .* (1 - mu)) .^ (1 / 6) ./ sqrt (N)); case "poisson" stats.resida(! xymissing) = 1.5 * ((y .^ (2 / 3) - mu .^ (2 / 3)) ... ./ mu .^ (1 / 6)); case "gamma" pwr = 1 / 3; stats.resida(! xymissing) = 3 * (y .^ pwr - mu .^ pwr) ./ mu .^ pwr; case "inverse gaussian" stats.resida(! xymissing) = (log (y) - log (mu)) ./ mu; endswitch endif endfunction %!demo %! x = [210, 230, 250, 270, 290, 310, 330, 350, 370, 390, 410, 430]'; %! n = [48, 42, 31, 34, 31, 21, 23, 23, 21, 16, 17, 21]'; %! y = [1, 2, 0, 3, 8, 8, 14, 17, 19, 15, 17, 21]'; %! b = glmfit (x, [y n], "binomial", "Link", "probit"); %! yfit = glmval (b, x, "probit", "Size", n); %! plot (x, y./n, 'o', x, yfit ./ n, '-') %!demo %! load fisheriris %! X = meas (51:end, :); %! y = strcmp ("versicolor", species(51:end)); %! b = glmfit (X, y, "binomial", "link", "logit") ## Test output %!test %! load fisheriris; %! X = meas(51:end,:); %! y = strcmp ("versicolor", species(51:end)); %! b = glmfit (X, y, "binomial", "link", "logit"); %! assert (b, [42.6379; 2.4652; 6.6809; -9.4294; -18.2861], 1e-4); %!test %! X = [1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0, 10.1]'; %! y = [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4]'; %! [Bnew, dev] = glmfit (X, y, "gamma", "link", "log"); %! b_matlab = [-0.7631; 0.1113]; %! dev_matlab = 0.0111; %! assert (Bnew, b_matlab, 0.001); %! assert (dev, dev_matlab, 0.001); %!test %! X = [1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0, 10.1]'; %! y = [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4]'; %! p_input = 1; %! [Bnew, dev] = glmfit (X, y, "inverse gaussian", "link", p_input); %! b_matlab = [0.3813; 0.0950]; %! dev_matlab = 0.0051; %! assert (Bnew, b_matlab, 0.001); %! assert (dev, dev_matlab, 0.001); ## Test input validation %!error glmfit () %!error glmfit (1) %!error glmfit (1, 2) %!error ... %! glmfit (rand (6, 1), rand (6, 1), 'poisson', 'link') %!error ... %! glmfit ('abc', rand (6, 1), 'poisson') %!error ... %! glmfit ([], rand (6, 1), 'poisson') %!error ... %! glmfit (rand (5, 2), 'abc', 'poisson') %!error ... %! glmfit (rand (5, 2), [], 'poisson') %!error ... %! glmfit (rand (5, 2), rand (6, 1), 'poisson') %!error ... %! glmfit (rand (6, 2), rand (6, 1), 3) %!error ... %! glmfit (rand (6, 2), rand (6, 1), {'poisson'}) %!error ... %! glmfit (rand (5, 2), rand (5, 3), 'binomial') %!error ... %! glmfit (rand (2, 2), [true, true; false, false], 'binomial') %!error ... %! glmfit (rand (5, 2), rand (5, 2), 'normal') %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'chebychev') %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'B0', [1; 2; 3; 4]) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'constant', 1) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'constant', 'o') %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'constant', true) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'estdisp', 1) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'estdisp', 'o') %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'estdisp', true) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", {1, 2})) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", "norminv")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", "some", "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", 1, "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x) [x, x], "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", "what", "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", "some", "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", 1, "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", @(x) [x, x], "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", "what", "Inverse", "normcdf")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", "some")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", 1)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", @(x) [x, x])) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", "what")) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {'log'}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {'log', 'hijy'}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {1, 2, 3, 4}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {"log", "dfv", "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) [x, x], "dfv", "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) what (x), "dfv", "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) x, "dfv", "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) x, @(x) [x, x], "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) x, @(x) what (x), "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) x, @(x) x, "dfgvd"}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) x, @(x) x, @(x) [x, x]}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', {@(x) x, @(x) x, @(x) what (x)}) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', NaN) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', [1, 2]) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', [1i]) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', ["log"; "log1"]) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', 'somelinkfunction') %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'link', true) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', true) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", 100)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", 4.5, "TolX", 1e-6)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", 0, "TolX", 1e-6)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", -100, "TolX", 1e-6)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", [50 ,50], "TolX", 1e-6)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", 100, "TolX", 0)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", 100, "TolX", -1e-6)) %!error ... %! glmfit (rand(5,2), rand(5,1), 'poisson', 'options', struct ("MaxIter", 100, "TolX", [1e-6, 1e-6])) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'offset', [1; 2; 3; 4]) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'offset', 'asdfg') %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'weights', [1; 2; 3; 4]) %!error ... %! glmfit (rand (5, 2), rand (5, 1), 'normal', 'weights', 'asdfg') statistics-release-1.7.3/inst/glmval.m000066400000000000000000000357211475240274700177730ustar00rootroot00000000000000## Copyright (C) 2025 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{yhat} =} glmval (@var{b}, @var{X}, @var{link}) ## @deftypefnx {statistics} {[@var{yhat}, @var{y_lo}, @var{y_hi}] =} glmval (@var{b}, @var{X}, @var{link}, @var{stats}) ## @deftypefnx {statistics} {[@dots{}] =} glmval (@dots{}, @var{Name}, @var{Value}) ## ## Predict values for a generalized linear model. ## ## @code{@var{yhat} = glmval (@var{b}, @var{X}, @var{link})} returns the ## predicted values for the generalized linear model with a vector of ## coefficient estimates @var{b}, a matrix of predictors @var{X}, in which each ## column corresponds to a distinct predictor variable, and a link function ## @var{link}, which can be any of the character vectors, numeric scalar, or ## custom-defined link functions used as values for the @qcode{"link"} ## name-value pair argument in the @code{glmfit} function. ## ## @code{[@var{yhat}, @var{y_lo}, @var{y_hi}] = glmval (@var{b}, @var{X}, ## @var{link}, @var{stats})} also returns the 95% confidence intervals for the ## predicted values according to the model's statistics contained in the ## @var{stats} structure, which is the output of the @code{glmfit} function. ## By default, the confidence intervals are nonsimultaneous, and apply to the ## fitted curve instead of new observations. ## ## @code{[@dots{}] = glmval (@dots{}, @var{Name}, @var{Value})} specifies ## additional options using @qcode{Name-Value} pair arguments. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"confidence"} @tab @tab A scalar value between 0 and 1 ## specifying the confidence level for the confidence bounds. ## ## @item @qcode{"Constant"} @tab @tab A character vector specifying whether to ## include a constant term in the model. Valid options are @var{"on"} (default) ## and @var{"off"}. ## ## @item @qcode{"simultaneous"} @tab @tab Specifies whether to ## include a constant term in the model. Options are ## @var{"on"} (default) or @var{"off"}. ## ## @item @qcode{"size"} @tab @tab A numeric scalar or a vector with one value ## for each row of @var{X} specifying the size parameter @math{N} for a binomial ## model. ## @end multitable ## ## @seealso{glmfit} ## @end deftypefn function [yhat, y_lo, y_hi] = glmval (b, X, link, varargin) ## Check input arguments if (nargin < 3) error ("glmval: too few input arguments."); elseif (! (isnumeric (b) && isvector (b)) || isempty (b)) error ("glmval: B must be a numeric vector of coefficient estimates."); elseif (! isnumeric (X) || isempty (X)) error ("glmval: X must be a numeric matrix."); endif ## Get inverse link (input validation is performed in private function) [~, ~, ilink, errmsg] = getlinkfunctions (link); if (! isempty (errmsg)) error ("glmval: %s", errmsg); endif ## Check if fourth input argument is a STATS structure stats = []; if (nargin > 3) if (isstruct (varargin{1})) stats = varargin{1}; rf = {"s", "se", "coeffcorr", "estdisp", "dfe"}; if (! all (ismember (rf, fieldnames (stats)))) error ("glmval: invalid 'stats' structure."); endif varargin(1) = []; endif endif ## Set defaults confidence = 0.95; constant = true; offset = zeros (size (X, 1), 1); simultaneous = false; N = 1; ## Parse extra parameters if (mod (numel (varargin), 2) != 0) error ("glmval: Name-Value arguments must be in pairs."); endif while (numel (varargin) > 0) switch (tolower (varargin {1})) case "confidence" confidence = varargin {2}; if (! (isscalar (confidence) && isnumeric (confidence) && confidence > 0 && confidence < 1)) error ("glmval: 'Confidence' must be a scalar between 0 and 1."); endif case "constant" constant = tolower (varargin {2}); if (strcmpi (constant, "on")) constant = true; elseif (strcmpi (constant, "off")) constant = false; else error ("glmval: 'Constant' should be either 'on' or 'off'."); endif case "offset" offset = varargin {2}; if (! (isnumeric (offset) && isequal (numel (offset), size (X, 1)))) error (["glmval: 'Offset' must be a numeric vector", ... " of the same length as the rows in X."]); endif offset = offset(:); case "simultaneous" simultaneous = varargin {2}; if (! (islogical (simultaneous) && isscalar (simultaneous))) error ("glmval: 'simultaneous' must be a boolean scalar."); endif case "size" N = varargin {2}; if (! isnumeric (N) || ! (isscalar (N) || isvector (N) && isequal (numel (N), size (X, 1)))) error (["glmval: 'size' must be a scalar or a vector with", ... " one value for each row of X."]); endif N = N(:); otherwise error ("glmval: unknown parameter name."); endswitch varargin (1:2) = []; endwhile ## Adjust X based on constant if (constant) X = [ones(size (X, 1), 1), X]; endif ## Predict yhat eta = X * b + offset; yhat = N .* ilink (eta); ## Compute lower and upper bounds if (nargout > 1) if (isempty (stats)) error (["glmval: cannot compute confidence", ... " intervals without STATS structure."]); endif if (isnan (stats.s)) y_lo = NaN (size (yhat)); y_hi = NaN (size (yhat)); else V = (stats.se * stats.se') .* stats.coeffcorr; XVX = sum ((X * V) .* X, 2)'; if (simultaneous) dof = length (b); if (stats.estdisp) Xcv = sqrt (dof * finv (confidence, dof, stats.dfe)); else Xcv = sqrt (chi2inv (confidence, dof)); endif else Xcv = tinv ((1 + confidence) / 2, stats.dfe); endif interval = Xcv * sqrt (XVX); int_hilo = [N.*ilink(eta-interval) N.*ilink(eta+interval)]; y_lo = yhat - min (int_hilo, [], 2); y_hi = max (int_hilo, [], 2) - yhat; endif endif endfunction %!demo %! x = [210, 230, 250, 270, 290, 310, 330, 350, 370, 390, 410, 430]'; %! n = [48, 42, 31, 34, 31, 21, 23, 23, 21, 16, 17, 21]'; %! y = [1, 2, 0, 3, 8, 8, 14, 17, 19, 15, 17, 21]'; %! b = glmfit (x, [y n], "binomial", "Link", "probit"); %! yfit = glmval (b, x, "probit", "Size", n); %! plot (x, y./n, 'o', x, yfit ./ n, '-') ## Test input validation %!error glmval () %!error glmval (1) %!error glmval (1, 2) %!error ... %! glmval ("asd", [1; 1; 1], 'probit') %!error ... %! glmval ([], [1; 1; 1], 'probit') %!error ... %! glmval ([0.1; 0.3; 0.4], [], 'probit') %!error ... %! glmval ([0.1; 0.3; 0.4], "asd", 'probit') %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", {1, 2})) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", "norminv")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", "some", "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", 1, "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x) [x, x], "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", "what", "Derivative", @(x)x, "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", "some", "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", 1, "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", @(x) [x, x], "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", "what", "Inverse", "normcdf")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", "some")) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", 1)) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", @(x) [x, x])) %!error ... %! glmval (rand (3,1), rand (5,2), struct ("Link", @(x)x, "Derivative", "normcdf", "Inverse", "what")) %!error ... %! glmval (rand (3,1), rand (5,2), {'log'}) %!error ... %! glmval (rand (3,1), rand (5,2), {'log', 'hijy'}) %!error ... %! glmval (rand (3,1), rand (5,2), {1, 2, 3, 4}) %!error ... %! glmval (rand (3,1), rand (5,2), {"log", "dfv", "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) [x, x], "dfv", "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) what (x), "dfv", "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) x, "dfv", "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) x, @(x) [x, x], "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) x, @(x) what (x), "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) x, @(x) x, "dfgvd"}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) x, @(x) x, @(x) [x, x]}) %!error ... %! glmval (rand (3,1), rand (5,2), {@(x) x, @(x) x, @(x) what (x)}) %!error ... %! glmval (rand (3,1), rand (5,2), NaN) %!error ... %! glmval (rand (3,1), rand (5,2), [1, 2]) %!error ... %! glmval (rand (3,1), rand (5,2), [1i]) %!error ... %! glmval (rand (3,1), rand (5,2), ["log"; "log1"]) %!error ... %! glmval (rand (3,1), rand (5,2), 'somelinkfunction') %!error ... %! glmval (rand (3,1), rand (5,2), true) %!error ... %! glmval (rand (3,1), rand (5,2), 'probit', struct ("s", 1)) %!error ... %! glmval (rand (3,1), rand (5,2), 'probit', 'confidence') %!error ... %! glmval (rand (3,1), rand (5,2), 'probit', 'confidence', 0) %!error ... %! glmval (rand (3,1), rand (5,2), 'probit', 'confidence', 1.2) %!error ... %! glmval (rand (3,1), rand (5,2), 'probit', 'confidence', [0.9, 0.95]) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'constant', 1) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'constant', 'o') %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'constant', true) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'offset', [1; 2; 3; 4]) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'offset', 'asdfg') %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'simultaneous', 'asdfg') %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'simultaneous', [true, false]) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'size', "asd") %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'size', [2, 3, 4]) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'size', [2; 3; 4]) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'size', ones (3)) %!error ... %! glmval (rand (3, 1), rand (5, 2), 'probit', 'someparam', 4) %!error ... %! [y,lo,hi] = glmval (rand (3, 1), rand (5, 2), 'probit') statistics-release-1.7.3/inst/gmdistribution.m000066400000000000000000000313701475240274700215500ustar00rootroot00000000000000## Copyright (C) 2015 Lachlan Andrew ## Copyright (C) 2018-2020 John Donoghue ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . classdef gmdistribution ## -*- texinfo -*- ## @deftypefn {statistics} {@var{GMdist} =} gmdistribution (@var{mu}, @var{Sigma}) ## @deftypefnx {statistics} {@var{GMdist} =} gmdistribution (@var{mu}, @var{Sigma}, @var{p}) ## @deftypefnx {statistics} {@var{GMdist} =} gmdistribution (@var{mu}, @var{Sigma}, @var{p}, @var{extra}) ## ## Create an object of the gmdistribution class which represents a Gaussian ## mixture model with k components of n-dimensional Gaussians. ## ## Input @var{mu} is a k-by-n matrix specifying the n-dimensional mean of ## each of the k components of the distribution. ## ## Input @var{Sigma} is an array that specifies the variances of the ## distributions, in one of four forms depending on its dimension. ## @itemize ## @item n-by-n-by-k: Slice @var{Sigma}(:,:,i) is the variance of the ## i'th component ## @item 1-by-n-by-k: Slice diag(@var{Sigma}(1,:,i)) is the variance of the ## i'th component ## @item n-by-n: @var{Sigma} is the variance of every component ## @item 1-by-n-by-k: Slice diag(@var{Sigma}) is the variance of every ## component ## @end itemize ## ## If @var{p} is specified, it is a vector of length k specifying the ## proportion of each component. If it is omitted or empty, each component ## has an equal proportion. ## ## Input @var{extra} is used by fitgmdist to indicate the parameters of the ## fitting process. ## @seealso{fitgmdist} ## @end deftypefn properties mu ## means Sigma ## covariances ComponentProportion ## mixing proportions DistributionName ## "gaussian mixture distribution" NumComponents ## Number of mixture components NumVariables ## Dimension d of each Gaussian component CovarianceType ## 'diagonal' if DiagonalCovariance, 'full' othw SharedCovariance ## true if all components have equal covariance ## Set by a call to gmdistribution.fit or fitgmdist AIC ## Akaike Information Criterion BIC ## Bayes Information Criterion Converged ## true if algorithm converged by MaxIter NegativeLogLikelihood ## Negative of log-likelihood NlogL ## Negative of log-likelihood NumIterations ## Number of iterations RegularizationValue ## const added to diag of cov to make +ve def endproperties properties (Access = private) DiagonalCovariance ## bool summary of "CovarianceType" endproperties methods ######################################## ## Constructor function obj = gmdistribution (mu,sigma,p = [],extra = []) obj.DistributionName = "gaussian mixture distribution"; obj.mu = mu; obj.Sigma = sigma; obj.NumComponents = rows (mu); obj.NumVariables = columns (mu); if (isempty (p)) obj.ComponentProportion = ones (1,obj.NumComponents) / ... obj.NumComponents; else if any (p < 0) error ("gmmdistribution: component weights must be non-negative"); endif s = sum(p); if (s == 0) error ("gmmdistribution: component weights must not be all zero"); elseif (s != 1) p = p / s; endif obj.ComponentProportion = p(:)'; endif if (length (size (sigma)) == 3) obj.SharedCovariance = false; else obj.SharedCovariance = true; endif if (rows (sigma) == 1 && columns (mu) > 1) obj.DiagonalCovariance = true; obj.CovarianceType = 'diagonal'; else obj.DiagonalCovariance = false; ## full obj.CovarianceType = 'full'; endif if (! isempty (extra)) obj.AIC = extra.AIC; obj.BIC = extra.BIC; obj.Converged = extra.Converged; obj.NegativeLogLikelihood = extra.NegativeLogLikelihood; obj.NlogL = extra.NegativeLogLikelihood; obj.NumIterations = extra.NumIterations; obj.RegularizationValue = extra.RegularizationValue; endif endfunction ######################################## ## Cumulative distribution function for Gaussian mixture distribution function c = cdf (obj, X) X = checkX (obj, X, "cdf"); p_x_l = zeros (rows (X), obj.NumComponents); if (obj.SharedCovariance) if (obj.DiagonalCovariance) sig = diag (obj.Sigma); else sig = obj.Sigma; endif endif for i = 1:obj.NumComponents if (! obj.SharedCovariance) if (obj.DiagonalCovariance) sig = diag (obj.Sigma(:,:,i)); else sig = obj.Sigma(:,:,i); endif endif p_x_l(:,i) = mvncdf (X,obj.mu(i,:),sig)*obj.ComponentProportion(i); endfor c = sum (p_x_l, 2); endfunction ######################################## ## Construct clusters from Gaussian mixture distribution function [idx, nlogl, P, logpdf, M] = cluster (obj,X) X = checkX (obj, X, "cluster"); [p_x_l, M] = componentProb (obj, X); [~, idx] = max (p_x_l, [], 2); if (nargout >= 2) PDF = sum (p_x_l, 2); logpdf = log (PDF); nlogl = -sum (logpdf); if (nargout >= 3) P = bsxfun (@rdivide, p_x_l, PDF); endif endif endfunction ######################################## ## Display Gaussian mixture distribution object function c = disp (obj) msg = ["Gaussian mixture distribution with %d ", ... "components in %d dimension(s)\n"]; fprintf (msg, obj.NumComponents, columns (obj.mu)); for i = 1:obj.NumComponents fprintf ("Clust %d: weight %d\n\tMean: ", ... i, obj.ComponentProportion(i)); fprintf ("%g ", obj.mu(i,:)); fprintf ("\n"); if (! obj.SharedCovariance) fprintf ("\tVariance:"); if (! obj.DiagonalCovariance) if (columns (obj.mu) > 1) fprintf ("\n"); endif disp (squeeze (obj.Sigma(:,:,i))) else fprintf (" diag("); fprintf ("%g ", obj.Sigma(:,:,i)); fprintf (")\n"); endif endif endfor if (obj.SharedCovariance) fprintf ("Shared variance\n"); if (! obj.DiagonalCovariance) obj.Sigma else fprintf (" diag("); fprintf ("%g ", obj.Sigma); fprintf (")\n"); endif endif if (! isempty (obj.AIC)) fprintf ("AIC=%g BIC=%g NLogL=%g Iter=%d Cged=%d Reg=%g\n", ... obj.AIC, obj.BIC, obj.NegativeLogLikelihood, ... obj.NumIterations, obj.Converged, obj.RegularizationValue); endif endfunction ######################################## ## Display Gaussian mixture distribution object function c = display (obj) disp(obj); endfunction ######################################## ## Mahalanobis distance to component means function D = mahal (obj,X) X = checkX (obj, X, "mahal"); [~, D] = componentProb (obj,X); endfunction ######################################## ## Probability density function for Gaussian mixture distribution function c = pdf (obj,X) X = checkX (obj, X, "pdf"); p_x_l = componentProb (obj, X); c = sum (p_x_l, 2); endfunction ######################################## ## Posterior probabilities of components function c = posterior (obj,X) X = checkX (obj, X, "posterior"); p_x_l = componentProb (obj, X); c = bsxfun(@rdivide, p_x_l, sum (p_x_l, 2)); endfunction ######################################## ## Random numbers from Gaussian mixture distribution function c = random (obj,n) if nargin == 1 n = 1; endif c = zeros (n, obj.NumVariables); classes = randsample (obj.NumComponents, n, true, ... obj.ComponentProportion); if (obj.SharedCovariance) if (obj.DiagonalCovariance) sig = diag (obj.Sigma); else sig = obj.Sigma; endif endif for i = 1:obj.NumComponents idx = (classes == i); k = sum(idx); if (k > 0) if (! obj.SharedCovariance) if (obj.DiagonalCovariance) sig = diag (obj.Sigma(:,:,i)); else sig = obj.Sigma(:,:,i); endif endif # [sig] forces [sig] not to have class "diagonal", # since mvnrnd uses automatic broadcast, # which fails on structured matrices c(idx,:) = mvnrnd ([obj.mu(i,:)], [sig], k); endif endfor endfunction endmethods ######################################## methods (Static) ## Gaussian mixture parameter estimates function c = fit (X, k, varargin) c = fitgmdist (X, k, varargin{:}); endfunction endmethods ######################################## methods (Access = private) ## Probability density of (row of) X *and* component l ## Second argument is an array of the Mahalonis distances function [p_x_l, M] = componentProb (obj, X) M = zeros (rows (X), obj.NumComponents); dets = zeros (1, obj.NumComponents); % sqrt(determinant) if (obj.SharedCovariance) if (obj.DiagonalCovariance) r = diag (sqrt(obj.Sigma)); else r = chol (obj.Sigma); endif endif for i = 1:obj.NumComponents dev = bsxfun (@minus, X, obj.mu(i,:)); if (! obj.SharedCovariance) if (obj.DiagonalCovariance) r = diag (sqrt (obj.Sigma(:,:,i))); else r = chol (obj.Sigma(:,:,i)); endif endif M(:,i) = sumsq (dev / r, 2); dets(i) = prod (diag (r)); endfor p_x_l = exp (-M/2); coeff = obj.ComponentProportion ./ ... ((2 * pi) ^ (obj.NumVariables / 2) .* dets); p_x_l = bsxfun (@times, p_x_l, coeff); endfunction ######################################## ## Check format of argument X function X = checkX (obj, X, name) if (columns (X) != obj.NumVariables) if (columns (X) == 1 && rows (X) == obj.NumVariables) X = X'; else error ("gmdistribution.%s: X has %d columns instead of %d\n", ... name, columns (X), obj.NumVariables); end endif endfunction endmethods endclassdef %!test %! mu = eye(2); %! Sigma = eye(2); %! GM = gmdistribution (mu, Sigma); %! density = GM.pdf ([0 0; 1 1]); %! assert (density(1) - density(2), 0, 1e-6); %! %! [idx, nlogl, P, logpdf,M] = cluster (GM, eye(2)); %! assert (idx, [1; 2]); %! [idx2,nlogl2,P2,logpdf2] = GM.cluster (eye(2)); %! assert (nlogl - nlogl2, 0, 1e-6); %! [idx3,nlogl3,P3] = cluster (GM, eye(2)); %! assert (P - P3, zeros (2), 1e-6); %! [idx4,nlogl4] = cluster (GM, eye(2)); %! assert (size (nlogl4), [1 1]); %! idx5 = cluster (GM, eye(2)); %! assert (idx - idx5, zeros (2,1)); %! %! D = GM.mahal ([1;0]); %! assert (D - M(1,:), zeros (1,2), 1e-6); %! %! P = GM.posterior ([0 1]); %! assert (P - P2(2,:), zeros (1,2), 1e-6); %! %! R = GM.random(20); %! assert (size(R), [20, 2]); %! %! R = GM.random(); %! assert (size(R), [1, 2]); statistics-release-1.7.3/inst/grp2idx.m000066400000000000000000000114201475240274700200560ustar00rootroot00000000000000## Copyright (C) 2015 Carnë Draug ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{g}, @var{gn}, @var{gl}] =} grp2idx (@var{s}) ## ## Get index for group variables. ## ## For variable @var{s}, returns the indices @var{g}, into the variable ## groups @var{gn} and @var{gl}. The first has a string representation of ## the groups while the later has its actual values. The group indices are ## allocated in order of appearance in @var{s}. ## ## NaNs and empty strings in @var{s} appear as NaN in @var{g} and are ## not present on either @var{gn} and @var{gl}. ## ## @seealso{grpstats} ## @end deftypefn function [g, gn, gl] = grp2idx (s) if (nargin != 1) print_usage (); endif s_was_char = false; if (ischar (s)) s_was_char = true; s = cellstr (s); elseif (! isvector (s)) error ("grp2idx: S must be a vector, cell array of strings, or char matrix"); endif [gl, I, g] = unique (s(:)); ## Fix order in here, since unique does not support this yet if (iscellstr (s)) I = sort(I); for i = 1:length (gl) gl_s(i) = gl(g(I(i))); idx(i,:) = (g == g(I(i))); endfor for i = 1:length (gl) g(idx(i,:)) = i; endfor gl = gl_s; gl = gl'; else I = sort(I); for i = 1:length (gl) gl_s(i) = gl(g(I(i))); idx(i,:) = (g == g(I(i))); endfor for i = 1:length (gl) g(idx(i,:)) = i; endfor gl = gl_s; gl = gl'; endif ## handle NaNs and empty strings if (iscellstr (s)) empties = cellfun (@isempty, s); if (any (empties)) g(empties) = NaN; while (min (g) > 1) g--; endwhile endif empties = cellfun (@isempty, gl); if (any (empties)) gl(empties) = []; endif else ## This works fine because NaN come at the end after sorting, we don't ## have to worry about change on the indices. g(isnan (s)) = NaN; gl(isnan (gl)) = []; endif if (nargout > 1) if (iscellstr (gl)) gn = gl; elseif (iscell (gl)) gn = cellfun (@num2str, gl, "UniformOutput", false); else gn = arrayfun (@num2str, gl, "UniformOutput", false); endif endif if (nargout > 2 && s_was_char) gl = char (gl); endif endfunction ## test boolean input and note that row or column vector makes no difference %!test %! in = [true false false true]; %! out = {[1; 2; 2; 1] {"1"; "0"} [true; false]}; %! assert (nthargout (1:3, @grp2idx, in), out) %! assert (nthargout (1:3, @grp2idx, in), nthargout (1:3, @grp2idx, in')) ## test that boolean groups are ordered in order of appearance %!test %! assert (nthargout (1:3, @grp2idx, [false, true]), %! {[1; 2] {"0"; "1"} [false; true]}); %! assert (nthargout (1:3, @grp2idx, [true, false]), %! {[1; 2] {"1"; "0"} [true; false]}); ## test char matrix and cell array of strings %!assert (nthargout (1:3, @grp2idx, ["oct"; "sci"; "oct"; "oct"; "sci"]), %! {[1; 2; 1; 1; 2] {"oct"; "sci"} ["oct"; "sci"]}); ## and cell array of strings %!assert (nthargout (1:3, @grp2idx, {"oct"; "sci"; "oct"; "oct"; "sci"}), %! {[1; 2; 1; 1; 2] {"oct"; "sci"} {"oct"; "sci"}}); ## test numeric arrays %!assert (nthargout (1:3, @grp2idx, [ 1 -3 -2 -3 -3 2 1 -1 3 -3]), %! {[1; 2; 3; 2; 2; 4; 1; 5; 6; 2], {"1"; "-3"; "-2"; "2"; "-1"; "3"}, ... %! [1; -3; -2; 2; -1; 3]}); ## test for NaN and empty strings %!assert (nthargout (1:3, @grp2idx, [2 2 3 NaN 2 3]), %! {[1; 1; 2; NaN; 1; 2] {"2"; "3"} [2; 3]}) %!assert (nthargout (1:3, @grp2idx, {"et" "sa" "sa" "" "et"}), %! {[1; 2; 2; NaN; 1] {"et"; "sa"} {"et"; "sa"}}) ## Test that order when handling strings is by order of appearance %!test assert (nthargout (1:3, @grp2idx, ["sci"; "oct"; "sci"; "oct"; "oct"]), %! {[1; 2; 1; 2; 2] {"sci"; "oct"} ["sci"; "oct"]}); %!test assert (nthargout (1:3, @grp2idx, {"sci"; "oct"; "sci"; "oct"; "oct"}), %! {[1; 2; 1; 2; 2] {"sci"; "oct"} {"sci"; "oct"}}); %!test assert (nthargout (1:3, @grp2idx, {"sa" "et" "et" "" "sa"}), %! {[1; 2; 2; NaN; 1] {"sa"; "et"} {"sa"; "et"}}) statistics-release-1.7.3/inst/grpstats.m000066400000000000000000000231261475240274700203540ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{mean} =} grpstats (@var{x}) ## @deftypefnx {statistics} {@var{mean} =} grpstats (@var{x}, @var{group}) ## @deftypefnx {statistics} {[@var{a}, @var{b}, @dots{}] =} grpstats (@var{x}, @var{group}, @var{whichstats}) ## @deftypefnx {statistics} {[@var{a}, @var{b}, @dots{}] =} grpstats (@var{x}, @var{group}, @var{whichstats}, @var{alpha}) ## ## Summary statistics by group. @code{grpstats} computes groupwise summary ## statistics, for data in a matrix @var{x}. @code{grpstats} treats NaNs as ## missing values, and removes them. ## ## @code{@var{means} = grpstats (@var{x}, @var{group})}, when X is a matrix of ## observations, returns the means of each column of @var{x} by @var{group}. ## @var{group} is a grouping variable defined as a categorical variable, ## numeric, string array, or cell array of strings. @var{group} can be [] or ## omitted to compute the mean of the entire sample without grouping. ## ## @code{[@var{a}, @var{b}, @dots{}] = grpstats (@var{x}, @var{group}, ## @var{whichstats})}, for a numeric matrix X, returns the statistics specified ## by @var{whichstats}, as separate arrays @var{a}, @var{b}, @dots{}. ## @var{whichstats} can be a single function name, or a cell array containing ## multiple function names. The number of output arguments must match the ## number of function names in @var{whichstats}. ## Names in @var{whichstats} can be chosen from among the following: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab "mean" @tab mean ## @item @tab "median" @tab median ## @item @tab "sem" @tab standard error of the mean ## @item @tab "std" @tab standard deviation ## @item @tab "var" @tab variance ## @item @tab "min" @tab minimum value ## @item @tab "max" @tab maximum value ## @item @tab "range" @tab maximum - minimum ## @item @tab "numel" @tab count, or number of elements ## @item @tab "meanci" @tab 95% confidence interval for the mean ## @item @tab "predci" @tab 95% prediction interval for a new observation ## @item @tab "gname" @tab group name ## @end multitable ## ## @code{[@dots{}] = grpstats (@var{x}, @var{group}, @var{whichstats}, ## @var{alpha})} specifies the confidence level as 100(1-ALPHA)% for the "meanci" ## and "predci" options. Default value for @var{alpha} is 0.05. ## ## Examples: ## ## @example ## load carsmall; ## [m,p,g] = grpstats (Weight, Model_Year, @{"mean", "predci", "gname"@}) ## n = length(m); ## errorbar((1:n)',m,p(:,2)-m) ## set (gca, "xtick", 1:n, "xticklabel", g); ## title ("95% prediction intervals for mean weight by year") ## @end example ## ## @seealso{grp2idx} ## @end deftypefn function [varargout] = grpstats (x ,group, whichstats, alpha) ## Check input arguments narginchk (1, 4) ## Check X being a vector or 2d matrix of real values if (ndims (x) > 2 || ! isnumeric (x) || islogical (x)) error ("grpstats: X must be a vector or 2d matrix of real values."); endif ## If X is a vector, make it a column vector if (isvector (x)) x = x(:); endif ## Check groups and if empty make a single group for all X [r, c] = size (x); if (nargin < 2 || isempty (group)) group = ones (r, 1); endif ## Get group names and indices [group_idx, group_names] = grp2idx (group); ngroups = length (group_names); if (length (group_idx) != r) error ("grpstats: samples in X and GROUPS mismatch."); endif ## Add default for whichstats and check for 3rd input argument func_names = {}; if (nargin > 2 && ischar (whichstats)) func_names = {whichstats}; elseif (nargin > 2 && iscell (whichstats)) func_names = whichstats; endif ## Add default for alpha and check for 4th input argument if (nargin > 3) if (! (isnumeric (alpha) && isscalar (alpha) ... && alpha > 0 && alpha < 1)) error ("grpstats: ALPHA must be a real scalar in the range (0,1)."); endif else alpha = 0.05; endif ## Calculate functions if (isempty (func_names)) ## Check consistent number of output arguments if (nargout == 1) for j = 1:ngroups group_x = x(find (group_idx == j), :); group_mean(j,:) = mean (group_x, 1, "omitnan") ; endfor varargout{1} = group_mean; else error ("grpstats: incosistent number of output arguments."); endif else func_num = length (func_names); ## Check consistent number of output arguments if (nargout != func_num) error ("grpstats: incosistent number of output arguments."); endif for l = 1:func_num switch (func_names{l}) case "mean" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_mean(j,:) = mean (group_x, 1, "omitnan"); endfor varargout{l} = group_mean; case "median" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_mean(j,:) = median (group_x, 1, "omitnan"); endfor varargout{l} = group_mean; case "sem" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_sem(j,:) = std (group_x, 0, 1, "omitnan") / ... sqrt (size (group_x, 1) - sum (isnan (group_x), 1)); endfor varargout{l} = group_sem; case "std" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_std(j,:) = std (group_x, 0, 1, "omitnan"); endfor varargout{l} = group_std; case "var" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_var(j,:) = var (group_x, 0, 1, "omitnan"); endfor varargout{l} = group_var; case "min" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_min(j,:) = nanmin (group_x); endfor varargout{l} = group_min; case "max" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_max(j,:) = nanmax (group_x); endfor varargout{l} = group_max; case "range" func_handle = @(x) range (x, 1); for j = 1:ngroups group_x = x(find (group_idx == j), :); group_range(j,:) = range (group_x, 1); endfor varargout{l} = group_range; case "numel" for j = 1:ngroups group_x = x(find (group_idx == j), :); group_numel(j,:) = size (group_x, 1) - sum (isnan (group_x), 1); endfor varargout{l} = group_numel; case "meanci" for j = 1:ngroups group_x = x(find (group_idx == j), :); m = mean (group_x, 1, "omitnan") ; n = size (x, 1) - sum (isnan (group_x), 1); s = std (group_x, 0, 1, "omitnan") ./ sqrt (n); d = s .* - tinv (alpha / 2, max (n - 1, [], 1)); group_meanci(j,:) = [m-d, m+d]; endfor varargout{l} = group_meanci; case "predci" for j = 1:ngroups group_x = x(find (group_idx == j), :); m = mean (group_x, 1, "omitnan") ; n = size (x, 1) - sum (isnan (group_x), 1); s = std (group_x, 0, 1, "omitnan") ./ sqrt (1 + (1 ./ n)); d = s .* - tinv (alpha / 2, max (n - 1, [], 1)); group_predci(j,:) = [m-d, m+d]; endfor varargout{l} = group_predci; case "gname" varargout{l} = group_names; otherwise error ("grpstats: wrong whichstats option."); endswitch endfor endif endfunction %!demo %! load carsmall; %! [m,p,g] = grpstats (Weight, Model_Year, {"mean", "predci", "gname"}) %! n = length(m); %! errorbar((1:n)',m,p(:,2)-m); %! set (gca, "xtick", 1:n, "xticklabel", g); %! title ("95% prediction intervals for mean weight by year"); %!demo %! load carsmall; %! [m,p,g] = grpstats ([Acceleration,Weight/1000],Cylinders, ... %! {"mean", "meanci", "gname"}, 0.05) %! [c,r] = size (m); %! errorbar((1:c)'.*ones(c,r),m,p(:,[(1:r)])-m); %! set (gca, "xtick", 1:c, "xticklabel", g); %! title ("95% prediction intervals for mean weight by year"); %!test %! load carsmall %! means = grpstats (Acceleration, Origin); %! assert (means, [14.4377; 18.0500; 15.8867; 16.3778; 16.6000; 15.5000], 0.001); %!test %! load carsmall %! [grpMin,grpMax,grp] = grpstats (Acceleration, Origin, {"min","max","gname"}); %! assert (grpMin, [8.0; 15.3; 13.9; 12.2; 15.7; 15.5]); %! assert (grpMax, [22.2; 21.9; 18.2; 24.6; 17.5; 15.5]); %!test %! load carsmall %! [grpMin,grpMax,grp] = grpstats (Acceleration, Origin, {"min","max","gname"}); %! assert (grp', {"USA", "France", "Japan", "Germany", "Sweden", "Italy"}); %!test %! load carsmall %! [m,p,g] = grpstats ([Acceleration,Weight/1000], Cylinders, ... %! {"mean", "meanci", "gname"}, 0.05); %! assert (p(:,1), [11.17621760075134, 16.13845847655224, 16.16222663683362]', ... %! [1e-14, 2e-14, 1e-14]'); statistics-release-1.7.3/inst/gscatter.m000066400000000000000000000203011475240274700203110ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} gscatter (@var{x}, @var{y}, @var{g}) ## @deftypefnx {statistics} {} gscatter (@var{x}, @var{y}, @var{g}, @var{clr}, @var{sym}, @var{siz}) ## @deftypefnx {statistics} {} gscatter (@dots{}, @var{doleg}, @var{xnam}, @var{ynam}) ## @deftypefnx {statistics} {@var{h} =} gscatter (@dots{}) ## ## Draw a scatter plot with grouped data. ## ## @code{gscatter} is a utility function to draw a scatter plot of @var{x} and ## @var{y}, according to the groups defined by @var{g}. Input @var{x} and ## @var{y} are numeric vectors of the same size, while @var{g} is either a ## vector of the same size as @var{x} or a character matrix with the same number ## of rows as the size of @var{x}. As a vector @var{g} can be numeric, logical, ## a character array, a string array (not implemented), a cell string or cell ## array. ## ## A number of optional inputs change the appearance of the plot: ## @itemize @bullet ## @item @var{"clr"} ## defines the color for each group; if not enough colors are defined by ## @var{"clr"}, @code{gscatter} cycles through the specified colors. Colors can ## be defined as named colors, as rgb triplets or as indices for the current ## @code{colormap}. The default value is a different color for each group, ## according to the current @code{colormap}. ## ## @item @var{"sym"} ## is a char array of symbols for each group; if not enough symbols are defined ## by @var{"sym"}, @code{gscatter} cycles through the specified symbols. ## ## @item @var{"siz"} ## is a numeric array of sizes for each group; if not enough sizes are defined ## by @var{"siz"}, @code{gscatter} cycles through the specified sizes. ## ## @item @var{"doleg"} ## is a boolean value to show the legend; it can be either @qcode{on} (default) ## or @qcode{off}. ## ## @item @var{"xnam"} ## is a character array, the name for the x axis. ## ## @item @var{"ynam"} ## is a character array, the name for the y axis. ## @end itemize ## ## Output @var{h} is an array of graphics handles to the @code{line} object of ## each group. ## ## @end deftypefn ## ## @seealso{scatter} function h = gscatter (varargin) ## optional axes handle if (isaxes (varargin{1})) ## parameter is an axes handle hax = varargin{1}; varargin = varargin(2:end); nargin--; endif ## check the input parameters if (nargin < 3) print_usage (); endif ## ## necessary parameters ## ## x coordinates if (isvector (varargin{1}) && isnumeric (varargin{1})) x = varargin{1}; n = numel (x); else error ("gscatter: x must be a numeric vector"); endif ## y coordinates if (isvector (varargin{2}) && isnumeric (varargin{2})) if (numel (varargin{2}) == n) y = varargin{2}; else error ("gscatter: x and y must have the same size"); endif else error ("gscatter: y must be a numeric vector"); endif ## groups if (isrow (varargin{3})) varargin{3} = transpose (varargin{3}); endif if (ismatrix (varargin{3}) && ischar (varargin{3})) varargin{3} = cellstr (varargin{3}); # char matrix to cellstr elseif (iscell (varargin{3}) && ! iscellstr (varargin{3})) varargin{3} = cell2mat (varargin{3}); # numeric cell to vector endif if (isvector (varargin{3})) # only numeric vectors or cellstr if (rows (varargin{3}) == n) gv = varargin{3}; if (iscellstr (gv)) g_names = unique (gv); # avoid warning else g_names = unique (gv, "rows"); endif g_len = numel (g_names); if (iscellstr (g_names)) for i = 1 : g_len g(find (strcmp(gv, g_names{i}))) = i; endfor else for i = 1 : g_len g(find (gv == g_names(i))) = i; endfor endif else error ("gscatter: g must have the same size as x and y"); endif else error (["gscatter: g must be a numeric or logical or char vector, "... "or a cell or cellstr array, or a char matrix"]); endif ## ## optional parameters ## ## Note: this parameters are passed as they are to 'line', ## the validity check is delegated to 'line' g_col = lines (g_len); g_size = 6 * ones (g_len, 1); g_sym = repmat ('o', 1, g_len); ## optional parameters for legend and axes labels do_legend = 1; # legend shown by default ## MATLAB compatibility: by default MATLAB uses the variable name as ## label for either axis mygetname = @(x) inputname(1); # to retrieve the name of a variable x_nam = mygetname (varargin{1}); # this should retrieve the name of the var, y_nam = mygetname (varargin{2}); # but it does not work ## parameters are all in fixed positions for i = 4 : nargin switch (i) case 4 ## colours c_list = varargin{4}; if (isrow (c_list)) c_list = transpose (c_list); endif c_list_len = rows (c_list); g_col = repmat (c_list, ceil (g_len / c_list_len)); case {5, 6} ## size and symbols s_list = varargin{i}; s_list_len = length (s_list); g_tmp = repmat (s_list, ceil (g_len / s_list_len)); if (i == 6) g_size = g_tmp; else g_sym = g_tmp; endif case 7 ## legend switch (lower (varargin{7})) case "on" do_legend = 1; case "off" do_legend = 0; otherwise error ("gscatter: invalid dolegend parameter '%s'", varargin{7}); endswitch case {8, 9} ## x and y label if (! ischar (varargin{i}) && ! isvector (varargin{i})) error ("gscatter: xnam and ynam must be strings"); endif if (i == 8) x_nam = varargin{8}; else y_nam = varargin{9}; endif endswitch endfor ## scatter plot with grouping if (! exist ("hax", "var")) hax = gca (); endif ## return value h = []; hold on; for i = 1 : g_len idcs = find (g == i); h(i) = line (hax, x(idcs), y(idcs), "linestyle", "none", ... "markersize", g_size(i), "color", g_col(i,:), "marker", g_sym(i)); endfor if (do_legend) if (isnumeric (g_names)) g_names = num2str (g_names); endif warning ("off", "Octave:legend:unimplemented-location", "local"); legend (hax, g_names, "location", "best"); endif xlabel (hax, x_nam); ylabel (hax, y_nam); hold off; endfunction %!demo %! load fisheriris; %! X = meas(:,3:4); %! cidcs = kmeans (X, 3, "Replicates", 5); %! gscatter (X(:,1), X(:,2), cidcs, [.75 .75 0; 0 .75 .75; .75 0 .75], "os^"); %! title ("Fisher's iris data"); ## Test plotting %!shared visibility_setting %! visibility_setting = get (0, "DefaultFigureVisible"); %!test %! hf = figure ("visible", "off"); %! unwind_protect %! load fisheriris; %! X = meas(:,3:4); %! cidcs = kmeans (X, 3, "Replicates", 5); %! gscatter (X(:,1), X(:,2), cidcs, [.75 .75 0; 0 .75 .75; .75 0 .75], "os^"); %! title ("Fisher's iris data"); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error gscatter (); %!error gscatter ([1]); %!error gscatter ([1], [2]); %!error gscatter ('abc', [1 2 3], [1]); %!error gscatter ([1 2 3], [1 2], [1]); %!error gscatter ([1 2 3], 'abc', [1]); %!error gscatter ([1 2], [1 2], [1]); %!error gscatter ([1 2], [1 2], [1 2], 'rb', 'so', 12, 'xxx'); statistics-release-1.7.3/inst/harmmean.m000066400000000000000000000237361475240274700203040ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{m} =} harmmean (@var{x}) ## @deftypefnx {statistics} {@var{m} =} harmmean (@var{x}, "all") ## @deftypefnx {statistics} {@var{m} =} harmmean (@var{x}, @var{dim}) ## @deftypefnx {statistics} {@var{m} =} harmmean (@var{x}, @var{vecdim}) ## @deftypefnx {statistics} {@var{m} =} harmmean (@dots{}, @var{nanflag}) ## ## Compute the harmonic mean of @var{x}. ## ## @itemize ## @item If @var{x} is a vector, then @code{harmmean(@var{x})} returns the ## harmonic mean of the elements in @var{x} defined as ## @tex ## $$ {\rm harmmean}(x) = \frac{N}{\sum_{i=1}^N \frac{1}{x_i}} $$ ## ## @end tex ## @ifnottex ## ## @example ## harmmean (@var{x}) = N / SUM_i @var{x}(i)^-1 ## @end example ## ## @end ifnottex ## @noindent ## where @math{N} is the length of the @var{x} vector. ## ## @item If @var{x} is a matrix, then @code{harmmean(@var{x})} returns a row ## vector with the harmonic mean of each columns in @var{x}. ## ## @item If @var{x} is a multidimensional array, then @code{harmmean(@var{x})} ## operates along the first nonsingleton dimension of @var{x}. ## ## @item @var{x} must not contain any negative or complex values. ## @end itemize ## ## @code{harmmean(@var{x}, "all")} returns the harmonic mean of all the elements ## in @var{x}. If @var{x} contains any 0, then the returned value is 0. ## ## @code{harmmean(@var{x}, @var{dim})} returns the harmonic mean along the ## operating dimension @var{dim} of @var{x}. Calculating the harmonic mean of ## any subarray containing any 0 will return 0. ## ## @code{harmmean(@var{x}, @var{vecdim})} returns the harmonic mean over the ## dimensions specified in the vector @var{vecdim}. For example, if @var{x} is ## a 2-by-3-by-4 array, then @code{harmmean(@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the harmonic mean of ## the elements on the corresponding page of @var{x}. If @var{vecdim} indexes ## all dimensions of @var{x}, then it is equivalent to @code{harmmean (@var{x}, ## "all")}. Any dimension in @var{vecdim} greater than @code{ndims (@var{x})} ## is ignored. ## ## @code{harmmean(@dots{}, @var{nanflag})} specifies whether to exclude NaN ## values from the calculation, using any of the input argument combinations in ## previous syntaxes. By default, harmmean includes NaN values in the ## calculation (@var{nanflag} has the value "includenan"). To exclude NaN ## values, set the value of @var{nanflag} to "omitnan". ## ## @seealso{geomean, mean} ## @end deftypefn function m = harmmean (x, varargin) if (nargin < 1 || nargin > 3) print_usage (); endif if (! isnumeric (x) || ! isreal (x) || ! all (x(! isnan (x))(:) >= 0)) error ("harmmean: X must contain real nonnegative values."); endif ## Set initial conditions all_flag = false; omitnan = false; nvarg = numel (varargin); varg_chars = cellfun ("ischar", varargin); szx = size (x); ndx = ndims (x); if (nvarg > 1 && ! varg_chars(2:end)) ## Only first varargin can be numeric print_usage (); endif ## Process any other char arguments. if (any (varg_chars)) for i = varargin(varg_chars) switch (lower (i{:})) case "all" all_flag = true; case "omitnan" omitnan = true; case "includenan" omitnan = false; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif ## Single numeric input argument, no dimensions given. if (nvarg == 0) if (all_flag) x = x(:); if (omitnan) x = x(! isnan (x)); endif if (any (x == 0)) m = 0; return; endif m = length (x) ./ sum (1 ./ x); m(m == Inf) = 0; # handle zeros in X else ## Find the first non-singleton dimension. (dim = find (szx != 1, 1)) || (dim = 1); n = szx(dim); is_nan = 0; if (omitnan) idx = isnan (x); n = sum (! idx, dim); is_nan = sum (idx, dim); x(idx) = 1; # remove NaNs by subtracting is_nan below endif m = n ./ (sum (1 ./ x, dim) - is_nan); m(m == Inf) = 0; # handle zeros in X endif else ## Two numeric input arguments, dimensions given. Note scalar is vector! vecdim = varargin{1}; if (isempty (vecdim) || ! (isvector (vecdim) && all (vecdim > 0)) ... || any (rem (vecdim, 1))) error ("harmmean: DIM must be a positive integer scalar or vector."); endif if (ndx == 2 && isempty (x) && szx == [0,0]) ## FIXME: this special case handling could be removed once sum ## compatibly handles all sizes of empty inputs sz_out = szx; sz_out (vecdim(vecdim <= ndx)) = 1; m = NaN (sz_out); else if (isscalar (vecdim)) if (vecdim > ndx) m = x; else n = szx(vecdim); is_nan = 0; if (omitnan) nanx = isnan (x); n = sum (! nanx, vecdim); is_nan = sum (nanx, vecdim); x(nanx) = 1; # remove NaNs by subtracting is_nan below endif m = n ./ (sum (1 ./ x, vecdim) - is_nan); m(m == Inf) = 0; # handle zeros in X endif else vecdim = sort (vecdim); if (! all (diff (vecdim))) error (strcat (["harmmean: VECDIM must contain non-repeating"], ... [" positive integers."])); endif ## Ignore exceeding dimensions in VECDIM vecdim(find (vecdim > ndims (x))) = []; if (isempty (vecdim)) m = x; else ## Move vecdims to dim 1. ## Calculate permutation vector remdims = 1 : ndx; # All dimensions remdims(vecdim) = []; # Delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is similar to all flag if (nremd == 0) x = x(:); if (omitnan) x = x(! isnan (x)); endif if (any (x == 0)) m = 0; return; endif m = length (x) ./ sum (1 ./ x); m(m == Inf) = 0; # handle zeros in X else ## Permute to bring vecdims to front perm = [vecdim, remdims]; x = permute (x, perm); ## Reshape to squash all vecdims in dim1 num_dim = prod (szx(vecdim)); szx(vecdim) = []; szx = [ones(1, length(vecdim)), szx]; szx(1) = num_dim; x = reshape (x, szx); ## Calculate mean on dim1 if (omitnan) nanx = isnan (x); n = sum (! nanx, 1); is_nan = sum (nanx, 1); x(nanx) = 1; # remove NaNs by subtracting is_nan below else n = szx(1); is_nan = 0; endif m = n ./ (sum (1 ./ x, 1) - is_nan); m(m == Inf) = 0; # handle zeros in X ## Inverse permute back to correct dimensions m = ipermute (m, perm); endif endif endif endif endif endfunction ## Test single input and optional arguments "all", DIM, "omitnan") %!test %! x = [0:10]; %! y = [x;x+5;x+10]; %! assert (harmmean (x), 0); %! m = [0 8.907635160795225 14.30854471766802]; %! assert (harmmean (y, 2), m', 4e-14); %! assert (harmmean (y, "all"), 0); %! y(2,4) = NaN; %! m(2) = 9.009855936313949; %! assert (harmmean (y, 2), [0 NaN m(3)]', 4e-14); %! assert (harmmean (y', "omitnan"), m, 4e-14); %! z = y + 20; %! assert (harmmean (z, "all"), NaN); %! assert (harmmean (z, "all", "includenan"), NaN); %! assert (harmmean (z, "all", "omitnan"), 29.1108719858295, 4e-14); %! m = [24.59488458841874 NaN 34.71244385944397]; %! assert (harmmean (z'), m, 4e-14); %! assert (harmmean (z', "includenan"), m, 4e-14); %! m(2) = 29.84104075528277; %! assert (harmmean (z', "omitnan"), m, 4e-14); %! assert (harmmean (z, 2, "omitnan"), m', 4e-14); ## Test dimension indexing with vecdim in n-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (harmmean (x, [3 2])), [10 1 1 3]); %! assert (size (harmmean (x, [1 2])), [1 1 6 3]); %! assert (size (harmmean (x, [1 2 4])), [1 1 6]); %! assert (size (harmmean (x, [1 4 3])), [1 40]); %! assert (size (harmmean (x, [1 2 3 4])), [1 1]); ## Test results with vecdim in n-dimensional arrays and "omitnan" %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! m = repmat ([5.559045930488016;13.04950789021461], [5 1 1 3]); %! assert (harmmean (x, [3 2]), m, 4e-14); %! x(2,5,6,3) = NaN; %! m(2,3) = NaN; %! assert (harmmean (x, [3 2]), m, 4e-14); %! m(2,3) = 13.06617961315406; %! assert (harmmean (x, [3 2], "omitnan"), m, 4e-14); ## Test errors %!error harmmean ("char") %!error harmmean ([1 -1 3]) %!error ... %! harmmean (repmat ([1:20;6:25], [5 2 6 3 5]), -1) %!error ... %! harmmean (repmat ([1:20;6:25], [5 2 6 3 5]), 0) %!error ... %! harmmean (repmat ([1:20;6:25], [5 2 6 3 5]), [1 1]) statistics-release-1.7.3/inst/hist3.m000066400000000000000000000311171475240274700175360ustar00rootroot00000000000000## Copyright (C) 2015 Carnë Draug ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {} hist3 (@var{X}) ## @deftypefnx {statistics} {} hist3 (@var{X}, @var{nbins}) ## @deftypefnx {statistics} {} hist3 (@var{X}, @code{"Nbins"}, @var{nbins}) ## @deftypefnx {statistics} {} hist3 (@var{X}, @var{centers}) ## @deftypefnx {statistics} {} hist3 (@var{X}, @code{"Ctrs"}, @var{centers}) ## @deftypefnx {statistics} {} hist3 (@var{X}, @code{"Edges"}, @var{edges}) ## @deftypefnx {statistics} {[@var{N}, @var{C}] =} hist3 (@dots{}) ## @deftypefnx {statistics} {} hist3 (@dots{}, @var{prop}, @var{val}, @dots{}) ## @deftypefnx {statistics} {} hist3 (@var{hax}, @dots{}) ## ## Produce bivariate (2D) histogram counts or plots. ## ## The elements to produce the histogram are taken from the Nx2 matrix ## @var{X}. Any row with NaN values are ignored. The actual bins can ## be configured in 3 different: number, centers, or edges of the bins: ## ## @table @asis ## @item Number of bins (default) ## Produces equally spaced bins between the minimum and maximum values ## of @var{X}. Defined as a 2 element vector, @var{nbins}, one for each ## dimension. Defaults to @code{[10 10]}. ## ## @item Center of bins ## Defined as a cell array of 2 monotonically increasing vectors, ## @var{centers}. The width of each bin is determined from the adjacent ## values in the vector with the initial and final bin, extending to Infinity. ## ## @item Edge of bins ## Defined as a cell array of 2 monotonically increasing vectors, ## @var{edges}. @code{@var{N}(i,j)} contains the number of elements ## in @var{X} for which: ## ## @itemize @w{} ## @item ## @var{edges}@{1@}(i) <= @var{X}(:,1) < @var{edges}@{1@}(i+1) ## @item ## @var{edges}@{2@}(j) <= @var{X}(:,2) < @var{edges}@{2@}(j+1) ## @end itemize ## ## The consequence of this definition is that values outside the initial ## and final edge values are ignored, and that the final bin only contains ## the number of elements exactly equal to the final edge. ## ## @end table ## ## The return values, @var{N} and @var{C}, are the bin counts and centers ## respectively. These are specially useful to produce intensity maps: ## ## @example ## [counts, centers] = hist3 (data); ## imagesc (centers@{1@}, centers@{2@}, counts) ## @end example ## ## If there is no output argument, or if the axes graphics handle ## @var{hax} is defined, the function will plot a 3 dimensional bar ## graph. Any extra property/value pairs are passed directly to the ## underlying surface object. ## ## @seealso{hist, histc, lookup, mesh} ## @end deftypefn function [N, C] = hist3 (X, varargin) if (nargin < 1) print_usage (); endif next_argin = 1; should_draw = true; if (isaxes (X)) hax = X; X = varargin{next_argin++}; elseif (nargout == 0) hax = gca (); else should_draw = false; endif if (! ismatrix (X) || columns (X) != 2) error ("hist3: X must be a 2 columns matrix"); endif method = "nbins"; val = [10 10]; if (numel (varargin) >= next_argin) this_arg = varargin{next_argin++}; if (isnumeric (this_arg)) method = "nbins"; val = this_arg; elseif (iscell (this_arg)) method = "ctrs"; val = this_arg; elseif (numel (varargin) >= next_argin && any (strcmpi ({"nbins", "ctrs", "edges"}, this_arg))) method = tolower (this_arg); val = varargin{next_argin++}; else next_argin--; endif endif have_centers = false; switch (tolower (method)) case "nbins" [r_edges, c_edges] = edges_from_nbins (X, val); case "ctrs" have_centers = true; centers = val; [r_edges, c_edges] = edges_from_centers (val); case "centers" ## This was supported until 1.2.4 when the Matlab compatible option ## 'Ctrs' was added. persistent warned = false; if (! warned) warning ("hist3: option `centers' is deprecated. Use `ctrs'"); endif have_centers = true; centers = val; [r_edges, c_edges] = edges_from_centers (val); case "edges" if (! iscell (val) || numel (val) != 2 || ! all (cellfun (@isvector, val))) error ("hist3: EDGES must be a cell array with 2 vectors"); endif [r_edges] = vec (val{1}, 2); [c_edges] = vec (val{2}, 2); out_rows = any (X < [r_edges(1) c_edges(1)] | X > [r_edges(end) c_edges(end)], 2); X(out_rows,:) = []; otherwise ## we should never get here... error ("hist3: invalid binning method `%s'", method); endswitch ## We only remove the NaN now, after having computed the bin edges, ## because the extremes from each column that define the edges may ## be paired with a NaN. While such values do not appear on the ## histogram, they must still be used to compute the histogram ## edges. X(any (isnan (X), 2), :) = []; r_idx = lookup (r_edges, X(:,1), "l"); c_idx = lookup (c_edges, X(:,2), "l"); counts_size = [numel(r_edges) numel(c_edges)]; counts = accumarray ([r_idx, c_idx], 1, counts_size); if (should_draw) counts = counts.'; z = zeros ((size (counts) +1) *2); z(2:end-1,2:end-1) = kron (counts, ones (2, 2)); ## Setting the values for the end of the histogram bin like this ## seems straight wrong but that's hwo Matlab plots look. y = [kron(c_edges, ones (1, 2)) (c_edges(end)*2-c_edges(end-1))([1 1])]; x = [kron(r_edges, ones (1, 2)) (r_edges(end)*2-r_edges(end-1))([1 1])]; mesh (hax, x, y, z, "facecolor", [.75 .85 .95], varargin{next_argin:end}); else N = counts; if (nargout > 1) if (! have_centers) C = {(r_edges + [diff(r_edges)([1:end end])]/ 2) ... (c_edges + [diff(c_edges)([1:end end])]/ 2)}; else C = centers(:)'; C{1} = vec (C{1}, 2); C{2} = vec (C{2}, 2); endif endif endif endfunction function [r_edges, c_edges] = edges_from_nbins (X, nbins) if (! isnumeric (nbins) || numel (nbins) != 2) error ("hist3: NBINS must be a 2 element vector"); endif inits = min (X, [], 1); ends = max (X, [], 1); ends -= (ends - inits) ./ vec (nbins, 2); ## If any histogram side has an empty range, then still make NBINS ## but then place that value at the centre of the centre bin so that ## they appear in the centre in the plot. single_bins = inits == ends; if (any (single_bins)) inits(single_bins) -= (floor (nbins(single_bins) ./2)) + 0.5; ends(single_bins) = inits(single_bins) + nbins(single_bins) -1; endif r_edges = linspace (inits(1), ends(1), nbins(1)); c_edges = linspace (inits(2), ends(2), nbins(2)); endfunction function [r_edges, c_edges] = edges_from_centers (ctrs) if (! iscell (ctrs) || numel (ctrs) != 2 || ! all (cellfun (@isvector, ctrs))) error ("hist3: CTRS must be a cell array with 2 vectors"); endif r_edges = vec (ctrs{1}, 2); c_edges = vec (ctrs{2}, 2); r_edges(2:end) -= diff (r_edges) / 2; c_edges(2:end) -= diff (c_edges) / 2; endfunction %!demo %! X = [ %! 1 1 %! 1 1 %! 1 10 %! 1 10 %! 5 5 %! 5 5 %! 5 5 %! 5 5 %! 5 5 %! 7 3 %! 7 3 %! 7 3 %! 10 10 %! 10 10]; %! hist3 (X) %!test %! N_exp = [ 0 0 0 5 20 %! 0 0 10 15 0 %! 0 15 10 0 0 %! 20 5 0 0 0]; %! %! n = 100; %! x = [1:n]'; %! y = [n:-1:1]'; %! D = [x y]; %! N = hist3 (D, [4 5]); %! assert (N, N_exp); %!test %! N_exp = [0 0 0 0 1 %! 0 0 0 0 1 %! 0 0 0 0 1 %! 1 1 1 1 93]; %! %! n = 100; %! x = [1:n]'; %! y = [n:-1:1]'; %! D = [x y]; %! C{1} = [1 1.7 3 4]; %! C{2} = [1:5]; %! N = hist3 (D, C); %! assert (N, N_exp); ## bug 44987 %!test %! D = [1 1; 3 1; 3 3; 3 1]; %! [c, nn] = hist3 (D, {0:4, 0:4}); %! exp_c = zeros (5); %! exp_c([7 9 19]) = [1 2 1]; %! assert (c, exp_c); %! assert (nn, {0:4, 0:4}); %!test %! for i = 10 %! assert (size (hist3 (rand (9, 2), "Edges", {[0:.2:1]; [0:.2:1]})), [6 6]) %! endfor %!test %! edge_1 = linspace (0, 10, 10); %! edge_2 = linspace (0, 50, 10); %! [c, nn] = hist3 ([1:10; 1:5:50]', "Edges", {edge_1, edge_2}); %! exp_c = zeros (10, 10); %! exp_c([1 12 13 24 35 46 57 68 79 90]) = 1; %! assert (c, exp_c); %! %! assert (nn{1}, edge_1 + edge_1(2)/2, eps*10^4) %! assert (nn{2}, edge_2 + edge_2(2)/2, eps*10^4) %!shared X %! X = [ %! 5 2 %! 5 3 %! 1 4 %! 5 3 %! 4 4 %! 1 2 %! 2 3 %! 3 3 %! 5 4 %! 5 3]; %!test %! N = zeros (10); %! N([1 10 53 56 60 91 98 100]) = [1 1 1 1 3 1 1 1]; %! C = {(1.2:0.4:4.8), (2.1:0.2:3.9)}; %! assert (nthargout ([1 2], @hist3, X), {N C}, eps*10^3) %!test %! N = zeros (5, 7); %! N([1 5 17 18 20 31 34 35]) = [1 1 1 1 3 1 1 1]; %! C = {(1.4:0.8:4.6), ((2+(1/7)):(2/7):(4-(1/7)))}; %! assert (nthargout ([1 2], @hist3, X, [5 7]), {N C}, eps*10^3) %! assert (nthargout ([1 2], @hist3, X, "Nbins", [5 7]), {N C}, eps*10^3) %!test %! N = [0 1 0; 0 1 0; 0 0 1; 0 0 0]; %! C = {(2:5), (2.5:1:4.5)}; %! assert (nthargout ([1 2], @hist3, X, "Edges", {(1.5:4.5), (2:4)}), {N C}) %!test %! N = [0 0 1 0 1 0; 0 0 0 1 0 0; 0 0 1 4 2 0]; %! C = {(1.2:3.2), (0:5)}; %! assert (nthargout ([1 2], @hist3, X, "Ctrs", C), {N C}) %! assert (nthargout ([1 2], @hist3, X, C), {N C}) %!test %! [~, C] = hist3 (rand (10, 2), "Edges", {[0 .05 .15 .35 .55 .95], %! [-1 .05 .07 .2 .3 .5 .89 1.2]}); %! C_exp = {[ 0.025 0.1 0.25 0.45 0.75 1.15], ... %! [-0.475 0.06 0.135 0.25 0.4 0.695 1.045 1.355]}; %! assert (C, C_exp, eps*10^2) ## Test how handling of out of borders is different whether we are ## defining Centers or Edges. %!test %! Xv = repmat ([1:10]', [1 2]); %! %! ## Test Centers %! assert (hist3 (Xv, "Ctrs", {1:10, 1:10}), eye (10)) %! %! N_exp = eye (6); %! N_exp([1 end]) = 3; %! assert (hist3 (Xv, "Ctrs", {3:8, 3:8}), N_exp) %! %! N_exp = zeros (8, 6); %! N_exp([1 2 11 20 29 38 47 48]) = [2 1 1 1 1 1 1 2]; %! assert (hist3 (Xv, "Ctrs", {2:9, 3:8}), N_exp) %! %! ## Test Edges %! assert (hist3 (Xv, "Edges", {1:10, 1:10}), eye (10)) %! assert (hist3 (Xv, "Edges", {3:8, 3:8}), eye (6)) %! assert (hist3 (Xv, "Edges", {2:9, 3:8}), [zeros(1, 6); eye(6); zeros(1, 6)]) %! %! N_exp = zeros (14); %! N_exp(3:12, 3:12) = eye (10); %! assert (hist3 (Xv, "Edges", {-1:12, -1:12}), N_exp) %! %! ## Test for Nbins %! assert (hist3 (Xv), eye (10)) %! assert (hist3 (Xv, [10 10]), eye (10)) %! assert (hist3 (Xv, "nbins", [10 10]), eye (10)) %! assert (hist3 (Xv, [5 5]), eye (5) * 2) %! %! N_exp = zeros (7, 5); %! N_exp([1 9 10 18 26 27 35]) = [2 1 1 2 1 1 2]; %! assert (hist3 (Xv, [7 5]), N_exp) %!test # bug #51059 %! D = [1 1; NaN 2; 3 1; 3 3; 1 NaN; 3 1]; %! [c, nn] = hist3 (D, {0:4, 0:4}); %! exp_c = zeros (5); %! exp_c([7 9 19]) = [1 2 1]; %! assert (c, exp_c) %! assert (nn, {0:4, 0:4}) ## Single row of data or cases where all elements have the same value ## on one side of the histogram. %!test %! [c, nn] = hist3 ([1 8]); %! exp_c = zeros (10, 10); %! exp_c(6, 6) = 1; %! exp_nn = {-4:5, 3:12}; %! assert (c, exp_c) %! assert (nn, exp_nn, eps) %! %! [c, nn] = hist3 ([1 8], [10 11]); %! exp_c = zeros (10, 11); %! exp_c(6, 6) = 1; %! exp_nn = {-4:5, 3:13}; %! assert (c, exp_c) %! assert (nn, exp_nn, eps) ## NaNs paired with values defining the histogram edges. %!test %! [c, nn] = hist3 ([1 NaN; 2 3; 6 9; 8 NaN]); %! exp_c = zeros (10, 10); %! exp_c(2, 1) = 1; %! exp_c(8, 10) = 1; %! exp_nn = {linspace(1.35, 7.65, 10) linspace(3.3, 8.7, 10)}; %! assert (c, exp_c) %! assert (nn, exp_nn, eps*100) ## Columns full of NaNs (recent Matlab versions seem to throw an error ## but this did work like this on R2010b at least). %!test %! [c, nn] = hist3 ([1 NaN; 2 NaN; 6 NaN; 8 NaN]); %! exp_c = zeros (10, 10); %! exp_nn = {linspace(1.35, 7.65, 10) NaN(1, 10)}; %! assert (c, exp_c) %! assert (nn, exp_nn, eps*100) ## Behaviour of an empty X after removal of rows with NaN. %!test %! [c, nn] = hist3 ([1 NaN; NaN 3; NaN 9; 8 NaN]); %! exp_c = zeros (10, 10); %! exp_nn = {linspace(1.35, 7.65, 10) linspace(3.3, 8.7, 10)}; %! assert (c, exp_c) %! assert (nn, exp_nn, eps*100) statistics-release-1.7.3/inst/histfit.m000066400000000000000000000206561475240274700201640ustar00rootroot00000000000000## Copyright (C) 2003 Alberto Terruzzi ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} histfit (@var{x}) ## @deftypefnx {statistics} {} histfit (@var{x}, @var{nbins}) ## @deftypefnx {statistics} {} histfit (@var{x}, @var{nbins}, @var{distname}) ## @deftypefnx {statistics} {} histfit (@var{ax}, @dots{}) ## @deftypefnx {statistics} {@var{h} =} histfit (@dots{}) ## ## Plot histogram with superimposed distribution fit. ## ## @code{histfit (@var{x})} plots a histogram of the values in the vector ## @var{x} using the number of bins equal to the square root of the number of ## non-missing elements in @var{x} and superimposes a fitted normal density ## function. ## ## @code{histfit (@var{x}, @var{nbins})} plots a histogram of the values in the ## vector @var{x} using @var{nbins} number of bins in the histogram and ## superimposes a fitted normal density function. ## ## @code{histfit (@var{x}, @var{nbins}, @var{distname})} plots a histogram of ## the values in the vector @var{x} using @var{nbins} number of bins in the ## histogram and superimposes a fitted density function from the distribution ## specified by @var{distname}. ## ## @code{histfit (@var{ax}, @dots{})} uses the axes handle @var{ax} to plot the ## histogram and the fitted density function onto followed by any of the input ## argument combinations specified in the previous syntaxes. ## ## @code{@var{h} = histfit (@dots{})} returns a vector of handles @var{h}, where ## @qcode{@var{h}(1)} is the handle to the histogram and @qcode{@var{h}(1)} is ## the handle to the density curve. ## ## Note: calling @code{fitdist} without any input arguments will return a cell ## array of character vectors listing all supported distributions. ## ## @seealso{bar, hist, normplot, fitdist} ## @end deftypefn function [varargout] = histfit (varargin) ## Add list of supported probability distribution objects PDO = {'Beta'; 'BirnbaumSaunders'; 'Burr'; 'Exponential'; 'ExtremeValue'; ... 'Gamma'; 'GeneralizedExtremeValue'; 'GeneralizedPareto'; ... 'InverseGaussian'; 'Logistic'; 'Loglogistic'; 'Lognormal'; ... 'Nakagami'; 'NegativeBinomial'; 'Normal'; 'Poisson'; 'Rayleigh'; ... 'Rician'; 'tLocationScale'; 'Weibull'}; ABBR = {"bisa"; "ev"; "gev"; "gp"; "invg"; "nbin"; "tls"; "wbl"}; ## Check for zero input arguments if (numel (varargin) < 1) varargout{1} = PDO; return endif ## Check for axes handle if (isaxes (varargin{1})) ax = varargin{1}; varargin(1) = []; get_current_axes = false; else get_current_axes = true; endif ## Get data if (numel (varargin) < 1) error ("histfit: too few input arguments."); else x = varargin{1}; if (! isnumeric (x) || ! isreal (x) || ! isvector (x) || isscalar (x)) error ("histfit: X must be a numeric vector of real numbers."); endif ## Remove missing values x(isnan (x)) = []; xsize = numel (x); ## Check for valid data if (xsize < 1) error ("histfit: no data in X."); endif endif ## Get nbins if (numel (varargin) > 1) nbins = varargin{2}; if (! (isreal (nbins) && isscalar (nbins) && fix (nbins) == nbins)) error ("histfit: NBINS must be a real scalar integer value."); endif else nbins = ceil (sqrt (xsize)); endif ## Get distribution if (numel (varargin) > 2) distname = varargin{3}; ## Check distribution name if (! (ischar (distname) && size (distname, 1) == 1)) error ("histfit: DISTNAME must be a character vector."); elseif (strcmpi (distname, "kernel")) error ("histfit: 'Kernel' distribution is not supported yet."); elseif (! (any (strcmpi (distname, PDO)) || any (strcmpi (distname, ABBR)))) error ("histfit: unrecognized distribution name."); endif else distname = "normal"; endif ## Create axes handle (if necessary) if (get_current_axes) ax = gca (); endif ## Plot the histogram if (any (strcmpi (distname, {"poisson", "NegativeBinomial", "nbin"}))) binwidth = 1; xmin = min (x) - 1; xmax = max (x) + 1; [binsize, bincenter] = hist (x, [xmin:xmax]); else [binsize, bincenter] = hist (x, nbins); binwidth = max (diff (bincenter)); xmin = min (x) - binwidth / 2; xmax = max (x) + binwidth / 2; endif h = bar (ax, bincenter, binsize, 1, "facecolor", "b"); ## Fit distibution to data pd = fitdist (x, distname); ## Compute density function if (any (strcmpi (distname, {"poisson", "NegativeBinomial", "nbin"}))) x = [min(x):max(x)]'; y = pdf (pd, x); else x = [xmin:(xmax-xmin)/100:xmax]'; y = pdf (pd, x); endif ## Normalize density line and overplot the histogram y = xsize * y * binwidth; hold on; if (any (strcmpi (distname, {"poisson", "NegativeBinomial", "nbin"}))) h(2) = plot (ax, x, y, ";;r-o"); else h(2) = plot (ax, x, y, ";;r-"); endif xlim ([xmin, xmax]); hold off; ## Return the plot's handle if requested if (nargout == 1) varargout{1} = h; endif endfunction %!demo %! histfit (randn (100, 1)) %!demo %! histfit (poissrnd (2, 1000, 1), 10, "Poisson") %!demo %! histfit (betarnd (3, 10, 1000, 1), 10, "beta") ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [2, 4, 3, 2, 4, 3, 2, 5, 6, 4, 7, 5, 9, 8, 10, 4, 11]; %! histfit (x); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [2, 4, 3, 2, NaN, 3, 2, 5, 6, 4, 7, 5, 9, 8, 10, 4, 11]; %! histfit (x); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [2, 4, 3, 2, NaN, 3, 2, 5, 6, 4, 7, 5, 9, 8, 10, 4, 11]; %! histfit (x, 3); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! histfit (randn (100, 1)); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! histfit (poissrnd (2, 1000, 1), 10, "Poisson"); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! histfit (betarnd (3, 10, 1000, 1), 10, "beta"); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! ax = gca (); %! histfit (ax, randn (100, 1)); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! ax = gca (); %! histfit (ax, poissrnd (2, 1000, 1), 10, "Poisson"); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! ax = gca (); %! histfit (ax, betarnd (3, 10, 1000, 1), 10, "beta"); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!test %! hf = figure ("visible", "off"); %! unwind_protect %! ax = axes ("parent", hf); %! fail ("histfit (ax)", "histfit: too few input arguments."); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!error ... %! histfit ('wer') %!error histfit ([NaN, NaN, NaN]); %!error ... %! histfit (randn (100, 1), 5.6) %!error ... %! histfit (randn (100, 1), 8, 5) %!error ... %! histfit (randn (100, 1), 8, {'normal'}) %!error ... %! histfit (randn (100, 1), 8, 'Kernel') %!error ... %! histfit (randn (100, 1), 8, 'ASDASDASD') statistics-release-1.7.3/inst/hmmestimate.m000066400000000000000000000340551475240274700210250ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{transprobest}, @var{outprobest}] =} hmmestimate (@var{sequence}, @var{states}) ## @deftypefnx {statistics} {[@dots{}] =} hmmestimate (@dots{}, @code{"statenames"}, @var{statenames}) ## @deftypefnx {statistics} {[@dots{}] =} hmmestimate (@dots{}, @code{"symbols"}, @var{symbols}) ## @deftypefnx {statistics} {[@dots{}] =} hmmestimate (@dots{}, @code{"pseudotransitions"}, @var{pseudotransitions}) ## @deftypefnx {statistics} {[@dots{}] =} hmmestimate (@dots{}, @code{"pseudoemissions"}, @var{pseudoemissions}) ## ## Estimation of a hidden Markov model for a given sequence. ## ## Estimate the matrix of transition probabilities and the matrix of output ## probabilities of a given sequence of outputs and states generated by a ## hidden Markov model. The model assumes that the generation starts in ## state @code{1} at step @code{0} but does not include step @code{0} in the ## generated states and sequence. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{sequence} is a vector of a sequence of given outputs. The outputs ## must be integers ranging from @code{1} to the number of outputs of the ## hidden Markov model. ## ## @item ## @var{states} is a vector of the same length as @var{sequence} of given ## states. The states must be integers ranging from @code{1} to the number ## of states of the hidden Markov model. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{transprobest} is the matrix of the estimated transition ## probabilities of the states. @code{transprobest(i, j)} is the estimated ## probability of a transition to state @code{j} given state @code{i}. ## ## @item ## @var{outprobest} is the matrix of the estimated output probabilities. ## @code{outprobest(i, j)} is the estimated probability of generating ## output @code{j} given state @code{i}. ## @end itemize ## ## If @code{'symbols'} is specified, then @var{sequence} is expected to be a ## sequence of the elements of @var{symbols} instead of integers. ## @var{symbols} can be a cell array. ## ## If @code{'statenames'} is specified, then @var{states} is expected to be ## a sequence of the elements of @var{statenames} instead of integers. ## @var{statenames} can be a cell array. ## ## If @code{'pseudotransitions'} is specified then the integer matrix ## @var{pseudotransitions} is used as an initial number of counted ## transitions. @code{pseudotransitions(i, j)} is the initial number of ## counted transitions from state @code{i} to state @code{j}. ## @var{transprobest} will have the same size as @var{pseudotransitions}. ## Use this if you have transitions that are very unlikely to occur. ## ## If @code{'pseudoemissions'} is specified then the integer matrix ## @var{pseudoemissions} is used as an initial number of counted outputs. ## @code{pseudoemissions(i, j)} is the initial number of counted outputs ## @code{j} given state @code{i}. If @code{'pseudoemissions'} is also ## specified then the number of rows of @var{pseudoemissions} must be the ## same as the number of rows of @var{pseudotransitions}. @var{outprobest} ## will have the same size as @var{pseudoemissions}. Use this if you have ## outputs or states that are very unlikely to occur. ## ## @subheading Examples ## ## @example ## @group ## transprob = [0.8, 0.2; 0.4, 0.6]; ## outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; ## [sequence, states] = hmmgenerate (25, transprob, outprob); ## [transprobest, outprobest] = hmmestimate (sequence, states) ## @end group ## ## @group ## symbols = @{"A", "B", "C"@}; ## statenames = @{"One", "Two"@}; ## [sequence, states] = hmmgenerate (25, transprob, outprob, ... ## "symbols", symbols, ... ## "statenames", statenames); ## [transprobest, outprobest] = hmmestimate (sequence, states, ... ## "symbols', symbols, ... ## "statenames', statenames) ## @end group ## ## @group ## pseudotransitions = [8, 2; 4, 6]; ## pseudoemissions = [2, 4, 4; 7, 2, 1]; ## [sequence, states] = hmmgenerate (25, transprob, outprob); ## [transprobest, outprobest] = hmmestimate (sequence, states, ... ## "pseudotransitions", pseudotransitions, ... ## "pseudoemissions", pseudoemissions) ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Wendy L. Martinez and Angel R. Martinez. @cite{Computational Statistics ## Handbook with MATLAB}. Appendix E, pages 547-557, Chapman & Hall/CRC, ## 2001. ## ## @item ## Lawrence R. Rabiner. A Tutorial on Hidden Markov Models and Selected ## Applications in Speech Recognition. @cite{Proceedings of the IEEE}, ## 77(2), pages 257-286, February 1989. ## @end enumerate ## @end deftypefn function [transprobest, outprobest] = hmmestimate (sequence, states, varargin) # Check arguments if (nargin < 2 || mod (length (varargin), 2) != 0) print_usage (); endif len = length (sequence); if (length (states) != len) error ("hmmestimate: sequence and states must have equal length"); endif # Flag for symbols usesym = false; # Flag for statenames usesn = false; # Variables for return values transprobest = []; outprobest = []; # Process varargin for i = 1:2:length (varargin) # There must be an identifier: 'symbols', 'statenames', # 'pseudotransitions' or 'pseudoemissions' if (! ischar (varargin{i})) print_usage (); endif # Upper case is also fine lowerarg = lower (varargin{i}); if (strcmp (lowerarg, 'symbols')) usesym = true; # Use the following argument as symbols symbols = varargin{i + 1}; # The same for statenames elseif (strcmp (lowerarg, 'statenames')) usesn = true; # Use the following argument as statenames statenames = varargin{i + 1}; elseif (strcmp (lowerarg, 'pseudotransitions')) # Use the following argument as an initial count for transitions transprobest = varargin{i + 1}; if (! ismatrix (transprobest)) error (strcat (["hmmestimate: pseudotransitions must be a"], ... [" non-empty numeric matrix"])); endif if (rows (transprobest) != columns (transprobest)) error ("hmmestimate: pseudotransitions must be a square matrix"); endif elseif (strcmp (lowerarg, 'pseudoemissions')) # Use the following argument as an initial count for outputs outprobest = varargin{i + 1}; if (! ismatrix (outprobest)) error (strcat (["hmmestimate: pseudoemissions must be a non-empty"], ... [" numeric matrix"])); endif else error (strcat (["hmmestimate: expected 'symbols', 'statenames',"], ... [" 'pseudotransitions' or 'pseudoemissions' but"], ... sprintf (" found '%s'", varargin{i}))); endif endfor # Transform sequence from symbols to integers if necessary if (usesym) # sequenceint is used to build the transformed sequence sequenceint = zeros (1, len); for i = 1:length (symbols) # Search for symbols(i) in the sequence, isequal will have 1 at # corresponding indices; i is the right integer for that symbol isequal = ismember (sequence, symbols(i)); # We do not want to change sequenceint if the symbol appears a second # time in symbols if (any ((sequenceint == 0) & (isequal == 1))) isequal *= i; sequenceint += isequal; endif endfor if (! all (sequenceint)) index = max ((sequenceint == 0) .* (1:len)); error (["hmmestimate: sequence(" int2str (index) ") not in symbols"]); endif sequence = sequenceint; else if (! isvector (sequence)) error ("hmmestimate: sequence must be a non-empty vector"); endif if (! all (ismember (sequence, 1:max (sequence)))) index = max ((ismember (sequence, 1:max (sequence)) == 0) .* (1:len)); error (["hmmestimate: sequence(" int2str (index) ") not feasible"]); endif endif # Transform states from statenames to integers if necessary if (usesn) # statesint is used to build the transformed states statesint = zeros (1, len); for i = 1:length (statenames) # Search for statenames(i) in states, isequal will have 1 at # corresponding indices; i is the right integer for that statename isequal = ismember (states, statenames(i)); # We do not want to change statesint if the statename appears a second # time in statenames if (any ((statesint == 0) & (isequal == 1))) isequal *= i; statesint += isequal; endif endfor if (! all (statesint)) index = max ((statesint == 0) .* (1:len)); error (["hmmestimate: states(" int2str (index) ") not in statenames"]); endif states = statesint; else if (! isvector (states)) error ("hmmestimate: states must be a non-empty vector"); endif if (! all (ismember (states, 1:max (states)))) index = max ((ismember (states, 1:max (states)) == 0) .* (1:len)); error (["hmmestimate: states(" int2str (index) ") not feasible"]); endif endif # Estimate the number of different states as the max of states nstate = max (states); # Estimate the number of different outputs as the max of sequence noutput = max (sequence); # transprobest is empty if pseudotransitions is not specified if (isempty (transprobest)) # outprobest is not empty if pseudoemissions is specified if (! isempty (outprobest)) if (nstate > rows (outprobest)) error ("hmmestimate: not enough rows in pseudoemissions"); endif # The number of states is specified by pseudoemissions nstate = rows (outprobest); endif transprobest = zeros (nstate, nstate); else if (nstate > rows (transprobest)) error ("hmmestimate: not enough rows in pseudotransitions"); endif # The number of states is given by pseudotransitions nstate = rows (transprobest); endif # outprobest is empty if pseudoemissions is not specified if (isempty (outprobest)) outprobest = zeros (nstate, noutput); else if (noutput > columns (outprobest)) error ("hmmestimate: not enough columns in pseudoemissions"); endif # Number of outputs is specified by pseudoemissions noutput = columns (outprobest); if (rows (outprobest) != nstate) error (strcat (["hmmestimate: pseudoemissions must have the same"], ... [" number of rows as pseudotransitions"])); endif endif # Assume that the model started in state 1 cstate = 1; for i = 1:len # Count the number of transitions for each state pair transprobest(cstate, states(i)) ++; cstate = states (i); # Count the number of outputs for each state output pair outprobest(cstate, sequence(i)) ++; endfor # transprobest and outprobest contain counted numbers # Each row in transprobest and outprobest should contain estimated # probabilities # => scale so that the sum is 1 # A zero row remains zero # - for transprobest s = sum (transprobest, 2); s(s == 0) = 1; transprobest = transprobest ./ (s * ones (1, nstate)); # - for outprobest s = sum (outprobest, 2); s(s == 0) = 1; outprobest = outprobest ./ (s * ones (1, noutput)); endfunction %!test %! sequence = [1, 2, 1, 1, 1, 2, 2, 1, 2, 3, 3, ... %! 3, 3, 2, 3, 1, 1, 1, 1, 3, 3, 2, 3, 1, 3]; %! states = [1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, ... %! 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]; %! [transprobest, outprobest] = hmmestimate (sequence, states); %! expectedtransprob = [0.88889, 0.11111; 0.28571, 0.71429]; %! expectedoutprob = [0.16667, 0.33333, 0.50000; 1.00000, 0.00000, 0.00000]; %! assert (transprobest, expectedtransprob, 0.001); %! assert (outprobest, expectedoutprob, 0.001); %!test %! sequence = {"A", "B", "A", "A", "A", "B", "B", "A", "B", "C", "C", "C", ... %! "C", "B", "C", "A", "A", "A", "A", "C", "C", "B", "C", "A", "C"}; %! states = {"One", "One", "Two", "Two", "Two", "One", "One", "One", "One", ... %! "One", "One", "One", "One", "One", "One", "Two", "Two", "Two", ... %! "Two", "One", "One", "One", "One", "One", "One"}; %! symbols = {"A", "B", "C"}; %! statenames = {"One", "Two"}; %! [transprobest, outprobest] = hmmestimate (sequence, states, "symbols", ... %! symbols, "statenames", statenames); %! expectedtransprob = [0.88889, 0.11111; 0.28571, 0.71429]; %! expectedoutprob = [0.16667, 0.33333, 0.50000; 1.00000, 0.00000, 0.00000]; %! assert (transprobest, expectedtransprob, 0.001); %! assert (outprobest, expectedoutprob, 0.001); %!test %! sequence = [1, 2, 1, 1, 1, 2, 2, 1, 2, 3, 3, 3, ... %! 3, 2, 3, 1, 1, 1, 1, 3, 3, 2, 3, 1, 3]; %! states = [1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, ... %! 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]; %! pseudotransitions = [8, 2; 4, 6]; %! pseudoemissions = [2, 4, 4; 7, 2, 1]; %! [transprobest, outprobest] = hmmestimate (sequence, states, ... %! "pseudotransitions", pseudotransitions, "pseudoemissions", pseudoemissions); %! expectedtransprob = [0.85714, 0.14286; 0.35294, 0.64706]; %! expectedoutprob = [0.178571, 0.357143, 0.464286; ... %! 0.823529, 0.117647, 0.058824]; %! assert (transprobest, expectedtransprob, 0.001); %! assert (outprobest, expectedoutprob, 0.001); statistics-release-1.7.3/inst/hmmgenerate.m000066400000000000000000000216721475240274700210050ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{sequence}, @var{states}] =} hmmgenerate (@var{len}, @var{transprob}, @var{outprob}) ## @deftypefnx {statistics} {[@dots{}] =} hmmgenerate (@dots{}, @code{"symbols"}, @var{symbols}) ## @deftypefnx {statistics} {[@dots{}] =} hmmgenerate (@dots{}, @code{"statenames"}, @var{statenames}) ## ## Output sequence and hidden states of a hidden Markov model. ## ## Generate an output sequence and hidden states of a hidden Markov model. ## The model starts in state @code{1} at step @code{0} but will not include ## step @code{0} in the generated states and sequence. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{len} is the number of steps to generate. @var{sequence} and ## @var{states} will have @var{len} entries each. ## ## @item ## @var{transprob} is the matrix of transition probabilities of the states. ## @code{transprob(i, j)} is the probability of a transition to state ## @code{j} given state @code{i}. ## ## @item ## @var{outprob} is the matrix of output probabilities. ## @code{outprob(i, j)} is the probability of generating output @code{j} ## given state @code{i}. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{sequence} is a vector of length @var{len} of the generated ## outputs. The outputs are integers ranging from @code{1} to ## @code{columns (outprob)}. ## ## @item ## @var{states} is a vector of length @var{len} of the generated hidden ## states. The states are integers ranging from @code{1} to ## @code{columns (transprob)}. ## @end itemize ## ## If @code{"symbols"} is specified, then the elements of @var{symbols} are ## used for the output sequence instead of integers ranging from @code{1} to ## @code{columns (outprob)}. @var{symbols} can be a cell array. ## ## If @code{"statenames"} is specified, then the elements of ## @var{statenames} are used for the states instead of integers ranging from ## @code{1} to @code{columns (transprob)}. @var{statenames} can be a cell ## array. ## ## @subheading Examples ## ## @example ## @group ## transprob = [0.8, 0.2; 0.4, 0.6]; ## outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; ## [sequence, states] = hmmgenerate (25, transprob, outprob) ## @end group ## ## @group ## symbols = @{"A", "B", "C"@}; ## statenames = @{"One", "Two"@}; ## [sequence, states] = hmmgenerate (25, transprob, outprob, ... ## "symbols", symbols, ... ## "statenames", statenames) ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Wendy L. Martinez and Angel R. Martinez. @cite{Computational Statistics ## Handbook with MATLAB}. Appendix E, pages 547-557, Chapman & Hall/CRC, ## 2001. ## ## @item ## Lawrence R. Rabiner. A Tutorial on Hidden Markov Models and Selected ## Applications in Speech Recognition. @cite{Proceedings of the IEEE}, ## 77(2), pages 257-286, February 1989. ## @end enumerate ## @end deftypefn function [sequence, states] = hmmgenerate (len, transprob, outprob, varargin) # Check arguments if (nargin < 3 || mod (length (varargin), 2) != 0) print_usage (); endif if (! isscalar (len) || len < 0 || round (len) != len) error ("hmmgenerate: len must be a non-negative scalar integer.") endif if (! ismatrix (transprob)) error ("hmmgenerate: transprob must be a non-empty numeric matrix."); endif if (! ismatrix (outprob)) error ("hmmgenerate: outprob must be a non-empty numeric matrix."); endif # nstate is the number of states of the hidden Markov model nstate = rows (transprob); # noutput is the number of different outputs that the hidden Markov model # can generate noutput = columns (outprob); # Check whether transprob and outprob are feasible for a hidden Markov # model if (columns (transprob) != nstate) error ("hmmgenerate: transprob must be a square matrix."); endif if (rows (outprob) != nstate) error (strcat (["hmmgenerate: outprob must have the same number"], ... [" of rows as transprob."])); endif # Flag for symbols usesym = false; # Flag for statenames usesn = false; # Process varargin for i = 1:2:length (varargin) # There must be an identifier: 'symbols' or 'statenames' if (! ischar (varargin{i})) print_usage (); endif # Upper case is also fine lowerarg = lower (varargin{i}); if (strcmp (lowerarg, 'symbols')) if (length (varargin{i + 1}) != noutput) error (strcat (["hmmgenerate: number of symbols does not match"], ... [" number of possible outputs."])); endif usesym = true; # Use the following argument as symbols symbols = varargin{i + 1}; # The same for statenames elseif (strcmp (lowerarg, 'statenames')) if (length (varargin{i + 1}) != nstate) error (strcat (["hmmgenerate: number of statenames does not"], ... [" match number of states."])); endif usesn = true; # Use the following argument as statenames statenames = varargin{i + 1}; else error (strcat (["hmmgenerate: expected 'symbols' or 'statenames'"], ... sprintf (" but found '%s'.", varargin{i}))); endif endfor # Each row in transprob and outprob should contain probabilities # => scale so that the sum is 1 # A zero row remains zero # - for transprob s = sum (transprob, 2); s(s == 0) = 1; transprob = transprob ./ repmat (s, 1, nstate); # - for outprob s = sum (outprob, 2); s(s == 0) = 1; outprob = outprob ./ repmat (s, 1, noutput); # Generate sequences of uniformly distributed random numbers between 0 and 1 # - for the state transitions transdraw = rand (1, len); # - for the outputs outdraw = rand (1, len); # Generate the return vectors # They remain unchanged if the according probability row of transprob # and outprob contain, respectively, only zeros sequence = ones (1, len); states = ones (1, len); if (len > 0) # Calculate cumulated probabilities backwards for easy comparison with # the generated random numbers # Cumulated probability in first column must always be 1 # We might have a zero row # - for transprob transprob(:, end:-1:1) = cumsum (transprob(:, end:-1:1), 2); transprob(:, 1) = 1; # - for outprob outprob(:, end:-1:1) = cumsum (outprob(:, end:-1:1), 2); outprob(:, 1) = 1; # cstate is the current state # Start in state 1 but do not include it in the states vector cstate = 1; for i = 1:len # Compare the randon number i of transdraw to the cumulated # probability of the state transition and set the transition # accordingly states(i) = sum (transdraw(i) <= transprob(cstate, :)); cstate = states(i); endfor # Compare the random numbers of outdraw to the cumulated probabilities # of the outputs and set the sequence vector accordingly sequence = sum (repmat (outdraw, noutput, 1) <= outprob(states, :)', 1); # Transform default matrices into symbols/statenames if requested if (usesym) sequence = reshape (symbols(sequence), 1, len); endif if (usesn) states = reshape (statenames(states), 1, len); endif endif endfunction %!test %! len = 25; %! transprob = [0.8, 0.2; 0.4, 0.6]; %! outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; %! [sequence, states] = hmmgenerate (len, transprob, outprob); %! assert (length (sequence), len); %! assert (length (states), len); %! assert (min (sequence) >= 1); %! assert (max (sequence) <= columns (outprob)); %! assert (min (states) >= 1); %! assert (max (states) <= rows (transprob)); %!test %! len = 25; %! transprob = [0.8, 0.2; 0.4, 0.6]; %! outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; %! symbols = {"A", "B", "C"}; %! statenames = {"One", "Two"}; %! [sequence, states] = hmmgenerate (len, transprob, outprob, ... %! "symbols", symbols, "statenames", statenames); %! assert (length (sequence), len); %! assert (length (states), len); %! assert (strcmp (sequence, "A") + strcmp (sequence, "B") + ... %! strcmp (sequence, "C") == ones (1, len)); %! assert (strcmp (states, "One") + strcmp (states, "Two") == ones (1, len)); statistics-release-1.7.3/inst/hmmviterbi.m000066400000000000000000000230061475240274700206500ustar00rootroot00000000000000## Copyright (C) 2006, 2007 Arno Onken ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{vpath} =} hmmviterbi (@var{sequence}, @var{transprob}, @var{outprob}) ## @deftypefnx {statistics} {@var{vpath} =} hmmviterbi (@dots{}, @code{"symbols"}, @var{symbols}) ## @deftypefnx {statistics} {@var{vpath} =} hmmviterbi (@dots{}, @code{"statenames"}, @var{statenames}) ## ## Viterbi path of a hidden Markov model. ## ## Use the Viterbi algorithm to find the Viterbi path of a hidden Markov ## model given a sequence of outputs. The model assumes that the generation ## starts in state @code{1} at step @code{0} but does not include step ## @code{0} in the generated states and sequence. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{sequence} is the vector of length @var{len} of given outputs. The ## outputs must be integers ranging from @code{1} to ## @code{columns (outprob)}. ## ## @item ## @var{transprob} is the matrix of transition probabilities of the states. ## @code{transprob(i, j)} is the probability of a transition to state ## @code{j} given state @code{i}. ## ## @item ## @var{outprob} is the matrix of output probabilities. ## @code{outprob(i, j)} is the probability of generating output @code{j} ## given state @code{i}. ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{vpath} is the vector of the same length as @var{sequence} of the ## estimated hidden states. The states are integers ranging from @code{1} to ## @code{columns (transprob)}. ## @end itemize ## ## If @code{"symbols"} is specified, then @var{sequence} is expected to be a ## sequence of the elements of @var{symbols} instead of integers ranging ## from @code{1} to @code{columns (outprob)}. @var{symbols} can be a cell array. ## ## If @code{"statenames"} is specified, then the elements of ## @var{statenames} are used for the states in @var{vpath} instead of ## integers ranging from @code{1} to @code{columns (transprob)}. ## @var{statenames} can be a cell array. ## ## @subheading Examples ## ## @example ## @group ## transprob = [0.8, 0.2; 0.4, 0.6]; ## outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; ## [sequence, states] = hmmgenerate (25, transprob, outprob); ## vpath = hmmviterbi (sequence, transprob, outprob); ## @end group ## ## @group ## symbols = @{"A", "B", "C"@}; ## statenames = @{"One", "Two"@}; ## [sequence, states] = hmmgenerate (25, transprob, outprob, ... ## "symbols", symbols, "statenames", statenames); ## vpath = hmmviterbi (sequence, transprob, outprob, ... ## "symbols", symbols, "statenames", statenames); ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Wendy L. Martinez and Angel R. Martinez. @cite{Computational Statistics ## Handbook with MATLAB}. Appendix E, pages 547-557, Chapman & Hall/CRC, ## 2001. ## ## @item ## Lawrence R. Rabiner. A Tutorial on Hidden Markov Models and Selected ## Applications in Speech Recognition. @cite{Proceedings of the IEEE}, ## 77(2), pages 257-286, February 1989. ## @end enumerate ## @end deftypefn function vpath = hmmviterbi (sequence, transprob, outprob, varargin) # Check arguments if (nargin < 3 || mod (length (varargin), 2) != 0) print_usage (); endif if (! ismatrix (transprob)) error ("hmmviterbi: transprob must be a non-empty numeric matrix"); endif if (! ismatrix (outprob)) error ("hmmviterbi: outprob must be a non-empty numeric matrix"); endif len = length (sequence); # nstate is the number of states of the hidden Markov model nstate = rows (transprob); # noutput is the number of different outputs that the hidden Markov model # can generate noutput = columns (outprob); # Check whether transprob and outprob are feasible for a hidden Markov model if (columns (transprob) != nstate) error ("hmmviterbi: transprob must be a square matrix"); endif if (rows (outprob) != nstate) error (strcat (["hmmviterbi: outprob must have the same number of"], ... [" rows as transprob"])); endif # Flag for symbols usesym = false; # Flag for statenames usesn = false; # Process varargin for i = 1:2:length (varargin) # There must be an identifier: 'symbols' or 'statenames' if (! ischar (varargin{i})) print_usage (); endif # Upper case is also fine lowerarg = lower (varargin{i}); if (strcmp (lowerarg, 'symbols')) if (length (varargin{i + 1}) != noutput) error (strcat (["hmmviterbi: number of symbols does not match"], ... [" number of possible outputs"])); endif usesym = true; # Use the following argument as symbols symbols = varargin{i + 1}; # The same for statenames elseif (strcmp (lowerarg, 'statenames')) if (length (varargin{i + 1}) != nstate) error (strcat (["hmmviterbi: number of statenames does not match"], ... [" number of states"])); endif usesn = true; # Use the following argument as statenames statenames = varargin{i + 1}; else error (strcat (["hmmviterbi: expected 'symbols' or 'statenames'"], ... sprintf (" but found '%s'", varargin{i}))); endif endfor # Transform sequence from symbols to integers if necessary if (usesym) # sequenceint is used to build the transformed sequence sequenceint = zeros (1, len); for i = 1:noutput # Search for symbols(i) in the sequence, isequal will have 1 at # corresponding indices; i is the right integer for that symbol isequal = ismember (sequence, symbols(i)); # We do not want to change sequenceint if the symbol appears a second # time in symbols if (any ((sequenceint == 0) & (isequal == 1))) isequal *= i; sequenceint += isequal; endif endfor if (! all (sequenceint)) index = max ((sequenceint == 0) .* (1:len)); error (["hmmviterbi: sequence(" int2str (index) ") not in symbols"]); endif sequence = sequenceint; else if (! isvector (sequence) && ! isempty (sequence)) error ("hmmviterbi: sequence must be a vector"); endif if (! all (ismember (sequence, 1:noutput))) index = max ((ismember (sequence, 1:noutput) == 0) .* (1:len)); error (["hmmviterbi: sequence(" int2str (index) ") out of range"]); endif endif # Each row in transprob and outprob should contain log probabilities # => scale so that the sum is 1 and convert to log space # - for transprob s = sum (transprob, 2); s(s == 0) = 1; transprob = log (transprob ./ (s * ones (1, columns (transprob)))); # - for outprob s = sum (outprob, 2); s(s == 0) = 1; outprob = log (outprob ./ (s * ones (1, columns (outprob)))); # Store the path starting from i in spath(i, :) spath = ones (nstate, len + 1); # Set the first state for each path spath(:, 1) = (1:nstate)'; # Store the probability of path i in spathprob(i) spathprob = transprob(1, :); # Find the most likely paths for the given output sequence for i = 1:len # Calculate the new probabilities of the continuation with each state nextpathprob = ((spathprob' + outprob(:, sequence(i))) * ... ones (1, nstate)) + transprob; # Find the paths with the highest probabilities [spathprob, mindex] = max (nextpathprob); # Update spath and spathprob with the new paths spath = spath(mindex, :); spath(:, i + 1) = (1:nstate)'; endfor # Set vpath to the most likely path # We do not want the last state because we do not have an output for it [m, mindex] = max (spathprob); vpath = spath(mindex, 1:len); # Transform vpath into statenames if requested if (usesn) vpath = reshape (statenames(vpath), 1, len); endif endfunction %!test %! sequence = [1, 2, 1, 1, 1, 2, 2, 1, 2, 3, 3, 3, ... %! 3, 2, 3, 1, 1, 1, 1, 3, 3, 2, 3, 1, 3]; %! transprob = [0.8, 0.2; 0.4, 0.6]; %! outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; %! vpath = hmmviterbi (sequence, transprob, outprob); %! expected = [1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, ... %! 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]; %! assert (vpath, expected); %!test %! sequence = {"A", "B", "A", "A", "A", "B", "B", "A", "B", "C", "C", "C", ... %! "C", "B", "C", "A", "A", "A", "A", "C", "C", "B", "C", "A", "C"}; %! transprob = [0.8, 0.2; 0.4, 0.6]; %! outprob = [0.2, 0.4, 0.4; 0.7, 0.2, 0.1]; %! symbols = {"A", "B", "C"}; %! statenames = {"One", "Two"}; %! vpath = hmmviterbi (sequence, transprob, outprob, "symbols", symbols, ... %! "statenames", statenames); %! expected = {"One", "One", "Two", "Two", "Two", "One", "One", "One", ... %! "One", "One", "One", "One", "One", "One", "One", "Two", ... %! "Two", "Two", "Two", "One", "One", "One", "One", "One", "One"}; %! assert (vpath, expected); statistics-release-1.7.3/inst/hotelling_t2test.m000066400000000000000000000154311475240274700217770ustar00rootroot00000000000000## Copyright (C) 1996-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{stats}] =} hotelling_t2test (@var{x}) ## @deftypefnx {statistics} {[@dots{}] =} hotelling_t2test (@var{x}, @var{m}) ## @deftypefnx {statistics} {[@dots{}] =} hotelling_t2test (@var{x}, @var{y}) ## @deftypefnx {statistics} {[@dots{}] =} hotelling_t2test (@var{x}, @var{m}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@dots{}] =} hotelling_t2test (@var{x}, @var{y}, @var{Name}, @var{Value}) ## ## Compute Hotelling's T^2 ("T-squared") test for a single sample or two ## dependent samples (paired-samples). ## ## For a sample @var{x} from a multivariate normal distribution with unknown ## mean and covariance matrix, test the null hypothesis that ## @code{mean (@var{x}) == @var{m}}. ## ## For two dependent samples @var{x} and @var{y} from a multivariate normal ## distributions with unknown means and covariance matrices, test the null ## hypothesis that @code{mean (@var{x} - @var{y}) == 0}. ## ## @qcode{hotelling_t2test} treats NaNs as missing values, and ignores the ## corresponding rows. ## ## Name-Value pair arguments can be used to set statistical significance. ## @qcode{"alpha"} can be used to specify the significance level of the test ## (the default value is 0.05). ## ## If @var{h} is 1 the null hypothesis is rejected, meaning that the tested ## sample does not come from a multivariate distribution with mean @var{m}, or ## in case of two dependent samples that they do not come from the same ## multivariate distribution. If @var{h} is 0, then the null hypothesis cannot ## be rejected and it can be assumed that it holds true. ## ## The p-value of the test is returned in @var{pval}. ## ## @var{stats} is a structure containing the value of the Hotelling's @math{T^2} ## test statistic in the field "Tsq", and the degrees of freedom of the F ## distribution in the fields "df1" and "df2". Under the null hypothesis, ## @math{(n-p) T^2 / (p(n-1))} has an F distribution with @math{p} and ## @math{n-p} degrees of freedom, where @math{n} and @math{p} are the ## numbers of samples and variables, respectively. ## ## @seealso{hotelling_t2test2} ## @end deftypefn function [h, pval, stats] = hotelling_t2test (x, my, varargin) ## Check for minimum number of input arguments if (nargin < 1) print_usage (); endif ## Check X being a valid data set if (isscalar (x) || ndims (x) > 2) error ("hotelling_t2test: X must be a vector or a 2D matrix."); endif ## Set default arguments alpha = 0.05; ## Fix MY when X is a single input argument if (nargin == 1) if (isvector (x)) my = 0; elseif (ismatrix (x)) [n, p] = size (x); my = zeros (1, p); endif endif ## When X and MY are of equal size, then assume paired-sample if (isequal (size (x), size(my))) x = x - my; if (isvector (x)) my = 0; elseif (ismatrix (x)) [n, p] = size (x); my = zeros (1, p); endif endif ## Remove rows containing any NaNs x = rmmissing (x); ## Check additional options i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; ## Check for valid alpha if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("hotelling_t2test: invalid value for alpha."); endif otherwise error ("hotelling_t2test: invalid Name argument."); endswitch i = i + 1; endwhile ## Conditional error checking for X being a vector or matrix if (isvector (x)) if (! isscalar (my)) error ("hotelling_t2test: if X is a vector, M must be a scalar."); endif n = length (x); p = 1; elseif (ismatrix (x)) [n, p] = size (x); if (n <= p) error ("hotelling_t2test: X must have more rows than columns."); endif if (isvector (my) && length (my) == p) my = reshape (my, 1, p); else error (strcat (["hotelling_t2test: if X is a matrix, M must be a"], ... [" vector of length equal to the columns of X."])); endif endif ## Calculate the necessary statistics d = mean (x) - my; stats.Tsq = n * d * (cov (x) \ d'); stats.df1 = p; stats.df2 = n - p; pval = 1 - fcdf ((n-p) * stats.Tsq / (p * (n-1)), stats.df1, stats.df2); ## Determine the test outcome ## MATLAB returns this a double instead of a logical array h = double (pval < alpha); endfunction ## Test input validation %!error hotelling_t2test (); %!error ... %! hotelling_t2test (1); %!error ... %! hotelling_t2test (ones(2,2,2)); %!error ... %! hotelling_t2test (ones(20,2), [0, 0], "alpha", 1); %!error ... %! hotelling_t2test (ones(20,2), [0, 0], "alpha", -0.2); %!error ... %! hotelling_t2test (ones(20,2), [0, 0], "alpha", "a"); %!error ... %! hotelling_t2test (ones(20,2), [0, 0], "alpha", [0.01, 0.05]); %!error ... %! hotelling_t2test (ones(20,2), [0, 0], "name", 0.01); %!error ... %! hotelling_t2test (ones(20,1), [0, 0]); %!error ... %! hotelling_t2test (ones(4,5), [0, 0, 0, 0, 0]); %!error ... %! hotelling_t2test (ones(20,5), [0, 0, 0, 0]); ## Test results %!test %! randn ("seed", 1); %! x = randn (50000, 5); %! [h, pval, stats] = hotelling_t2test (x); %! assert (h, 0); %! assert (stats.df1, 5); %! assert (stats.df2, 49995); %!test %! randn ("seed", 1); %! x = randn (50000, 5); %! [h, pval, stats] = hotelling_t2test (x, ones (1, 5) * 10); %! assert (h, 1); %! assert (stats.df1, 5); %! assert (stats.df2, 49995); statistics-release-1.7.3/inst/hotelling_t2test2.m000066400000000000000000000146671475240274700220730ustar00rootroot00000000000000## Copyright (C) 1996-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{stats}] =} hotelling_t2test2 (@var{x}, @var{y}) ## @deftypefnx {statistics} {[@dots{}] =} hotelling_t2test2 (@var{x}, @var{y}, @var{Name}, @var{Value}) ## ## Compute Hotelling's T^2 ("T-squared") test for two independent samples. ## ## For two samples @var{x} from multivariate normal distributions with ## the same number of variables (columns), unknown means and unknown ## equal covariance matrices, test the null hypothesis ## @code{mean (@var{x}) == mean (@var{y})}. ## ## @qcode{hotelling_t2test2} treats NaNs as missing values, and ignores the ## corresponding rows for each sample independently. ## ## Name-Value pair arguments can be used to set statistical significance. ## @qcode{"alpha"} can be used to specify the significance level of the test ## (the default value is 0.05). ## ## If @var{h} is 1 the null hypothesis is rejected, meaning that the tested ## samples do not come from the same multivariate distribution. If @var{h} is ## 0, then the null hypothesis cannot be rejected and it can be assumed that ## both samples come from the same multivariate distribution. ## ## The p-value of the test is returned in @var{pval}. ## ## @var{stats} is a structure containing the value of the Hotelling's @math{T^2} ## test statistic in the field "Tsq", and the degrees of freedom of the F ## distribution in the fields "df1" and "df2". Under the null hypothesis, ## @tex ## $$ ## {(n_x+n_y-p-1) T^2 \over p(n_x+n_y-2)} ## $$ ## @end tex ## @ifnottex ## ## @example ## (n_x+n_y-p-1) T^2 / (p(n_x+n_y-2)) ## @end example ## ## @end ifnottex ## @noindent ## has an F distribution with @math{p} and @math{n_x+n_y-p-1} degrees of ## freedom, where @math{n_x} and @math{n_y} are the sample sizes and ## @math{p} is the number of variables. ## ## @seealso{hotelling_t2test} ## @end deftypefn function [h, pval, stats] = hotelling_t2test2 (x, y, varargin) ## Check for minimum number of input arguments if (nargin < 2) print_usage (); endif ## Check X being a valid data set if (isscalar (x) || ndims (x) > 2) error ("hotelling_t2test2: X must be a vector or a 2D matrix."); endif ## Check Y being a valid data set if (isscalar (y) || ndims (y) > 2) error ("hotelling_t2test2: Y must be a vector or a 2D matrix."); endif ## Set default arguments alpha = 0.05; ## Remove rows containing any NaNs x = rmmissing (x); y = rmmissing (y); ## Check additional options i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; ## Check for valid alpha if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("hotelling_t2test2: invalid value for alpha."); endif otherwise error ("hotelling_t2test2: invalid Name argument."); endswitch i = i + 1; endwhile ## Conditional error checking for X being a vector or matrix if (isvector (x)) n_x = length (x); if (! isvector (y)) error ("hotelling_t2test2: if X is a vector, Y must also be a vector."); else n_y = length (y); p = 1; endif elseif (ismatrix (x)) [n_x, p] = size (x); [n_y, q] = size (y); if (p != q) error (strcat (["hotelling_t2test2: X and Y must have the same"], ... [" number of columns."])); endif endif ## Calculate the necessary statistics d = mean (x) - mean (y); S = ((n_x - 1) * cov (x) + (n_y - 1) * cov (y)) / (n_x + n_y - 2); stats.Tsq = (n_x * n_y / (n_x + n_y)) * d * (S \ d'); stats.df1 = p; stats.df2 = n_x + n_y - p - 1; pval = 1 - fcdf ((n_x + n_y - p - 1) * stats.Tsq / (p * (n_x + n_y - 2)), ... stats.df1, stats.df2); ## Determine the test outcome ## MATLAB returns this a double instead of a logical array h = double (pval < alpha); endfunction ## Test input validation %!error hotelling_t2test2 (); %!error ... %! hotelling_t2test2 ([2, 3, 4, 5, 6]); %!error ... %! hotelling_t2test2 (1, [2, 3, 4, 5, 6]); %!error ... %! hotelling_t2test2 (ones (2,2,2), [2, 3, 4, 5, 6]); %!error ... %! hotelling_t2test2 ([2, 3, 4, 5, 6], 2); %!error ... %! hotelling_t2test2 ([2, 3, 4, 5, 6], ones (2,2,2)); %!error ... %! hotelling_t2test2 (ones (20,2), ones (20,2), "alpha", 1); %!error ... %! hotelling_t2test2 (ones (20,2), ones (20,2), "alpha", -0.2); %!error ... %! hotelling_t2test2 (ones (20,2), ones (20,2), "alpha", "a"); %!error ... %! hotelling_t2test2 (ones (20,2), ones (20,2), "alpha", [0.01, 0.05]); %!error ... %! hotelling_t2test2 (ones (20,2), ones (20,2), "name", 0.01); %!error ... %! hotelling_t2test2 (ones (20,1), ones (20,2)); %!error ... %! hotelling_t2test2 (ones (20,2), ones (25,3)); ## Test results %!test %! randn ("seed", 1); %! x1 = randn (60000, 5); %! randn ("seed", 5); %! x2 = randn (30000, 5); %! [h, pval, stats] = hotelling_t2test2 (x1, x2); %! assert (h, 0); %! assert (stats.df1, 5); %! assert (stats.df2, 89994); statistics-release-1.7.3/inst/inconsistent.m000066400000000000000000000102771475240274700212300ustar00rootroot00000000000000## Copyright (C) 2020-2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{Y} =} inconsistent (@var{Z}) ## @deftypefnx {statistics} {@var{Y} =} inconsistent (@var{Z}, @var{d}) ## ## Compute the inconsistency coefficient for each link of a hierarchical cluster ## tree. ## ## Given a hierarchical cluster tree @var{Z} generated by the @code{linkage} ## function, @code{inconsistent} computes the inconsistency coefficient for each ## link of the tree, using all the links down to the @var{d}-th level below that ## link. ## ## The default depth @var{d} is 2, which means that only two levels are ## considered: the level of the computed link and the level below that. ## ## Each row of @var{Y} corresponds to the row of same index of @var{Z}. ## The columns of @var{Y} are respectively: the mean of the heights of the links ## used for the calculation, the standard deviation of the heights of those ## links, the number of links used, the inconsistency coefficient. ## ## @strong{Reference} ## Jain, A., and R. Dubes. Algorithms for Clustering Data. ## Upper Saddle River, NJ: Prentice-Hall, 1988. ## @end deftypefn ## ## @seealso{cluster, clusterdata, dendrogram, linkage, pdist, squareform} function Y = inconsistent (Z, d = 2) ## check the input if (nargin < 1) || (nargin > 2) print_usage (); endif ## MATLAB compatibility: ## when d = 0, which does not make sense, the result of inconsistent is the ## same as d = 1, which is... inconsistent if ((d < 0) || (! isscalar (d)) || (mod (d, 1))) error ("inconsistent: d must be a positive integer scalar"); endif if ((columns (Z) != 3) || (! isnumeric (Z)) || ... (! (max (Z(end, 1:2)) == rows (Z) * 2))) error (["inconsistent: Z must be a matrix generated by the linkage " ... "function"]); endif ## number of observations n = rows (Z) + 1; ## compute the inconsistency coefficient for every link for i = 1:rows (Z) v = inconsistent_recursion (i, d); # nested recursive function - see below Y(i, 1) = mean (v); Y(i, 2) = std (v); Y(i, 3) = length (v); ## the inconsistency coefficient is (current_link_height - mean) / std; ## if the standard deviation is zero, it is zero by definition if (Y(i, 2) != 0) Y(i, 4) = (v(end) - Y(i, 1)) / Y(i, 2); else Y(i, 4) = 0; endif endfor ## recursive function ## while depth > 1 search the links (columns 1 and 2 of Z) below the current ## link and then append the height of the current link to the vector v. ## The height of the starting link should be the last one of the vector. function v = inconsistent_recursion (index, depth) v = []; if (depth > 1) for j = 1:2 if (Z(index, j) > n) new_index = Z(index, j) - n; v = [v (inconsistent_recursion (new_index, depth - 1))]; endif endfor endif v(end+1) = Z(index, 3); endfunction endfunction ## Test input validation %!error inconsistent () %!error inconsistent ([1 2 1], 2, 3) %!error inconsistent (ones (2, 2)) %!error inconsistent ([1 2 1], -1) %!error inconsistent ([1 2 1], 1.3) %!error inconsistent ([1 2 1], [1 1]) %!error inconsistent (ones (2, 3)) ## Test output %!test %! load fisheriris; %! Z = linkage(meas, 'average', 'chebychev'); %! assert (cond (inconsistent (Z)), 39.9, 1e-3); statistics-release-1.7.3/inst/ismissing.m000066400000000000000000000160231475240274700205100ustar00rootroot00000000000000## Copyright (C) 1995-2023 The Octave Project Developers ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{TF} =} ismissing (@var{A}) ## @deftypefnx {statistics} {@var{TF} =} ismissing (@var{A}, @var{indicator}) ## ## Find missing data in a numeric or string array. ## ## Given an input numeric data array, char array, or array of cell strings ## @var{A}, @code{ismissing} returns a logical array @var{TF} with ## the same dimensions as @var{A}, where @code{true} values match missing ## values in the input data. ## ## The optional input @var{indicator} is an array of values that represent ## missing values in the input data. The values which represent missing data ## by default depend on the data type of @var{A}: ## ## @itemize ## @item ## @qcode{NaN}: @code{single}, @code{double}. ## ## @item ## @qcode{' '} (white space): @code{char}. ## ## @item ## @qcode{@{''@}}: string cells. ## @end itemize ## ## Note: logical and numeric data types may be used in any combination ## for @var{A} and @var{indicator}. @var{A} and the indicator values will be ## compared as type double, and the output will have the same class as @var{A}. ## Data types other than those specified above have no defined 'missing' value. ## As such, the TF output for those inputs will always be ## @code{false(size(@var{A}))}. The exception to this is that @var{indicator} ## can be specified for logical and numeric inputs to designate values that ## will register as 'missing'. ## ## @seealso{fillmissing, rmmissing, standardizeMissing} ## @end deftypefn function TF = ismissing (A, indicator) if (nargin < 1) || (nargin > 2) print_usage (); endif ## check "indicator" if (nargin != 2) indicator = []; endif ## if A is an array of cell strings and indicator just a string, ## convert indicator to a cell string with one element if (iscellstr (A) && ischar (indicator) && ! iscellstr (indicator)) indicator = {indicator}; endif if ((! isempty (indicator)) && ((isnumeric (A) && ! (isnumeric (indicator) || islogical (indicator))) || (ischar (A) && ! ischar (indicator)) || (iscellstr (A) && ! (iscellstr (indicator))))) error ("ismissing: 'indicator' and 'A' must have the same data type"); endif ## main logic if (isempty (indicator)) if (isnumeric (A)) ## numeric matrix: just find the NaNs ## integer types have no missing value, but isnan will return false TF = isnan (A); elseif (iscellstr (A)) ## cell strings - find empty cells TF = cellfun ('isempty', A); elseif (ischar (A)) ## char matrix: find the white spaces TF = isspace (A); else ##no missing type defined, return false TF = false (size (A)); endif else ## indicator specified for missing data TF = false (size (A)); if (isnumeric(A) || ischar (A) || islogical (A)) for iter = 1 : numel (indicator) if (isnan (indicator(iter))) TF(isnan(A)) = true; else TF(A == indicator(iter)) = true; endif endfor elseif (iscellstr (A)) for iter = 1 : numel (indicator) TF(strcmp (A, indicator(iter))) = true; endfor else error ("ismissing: indicators not supported for data type '%s'", ... class(A)); endif endif endfunction %!assert (ismissing ([1,NaN,3]), [false,true,false]) %!assert (ismissing ('abcd f'), [false,false,false,false,true,false]) %!assert (ismissing ({'xxx','','xyz'}), [false,true,false]) %!assert (ismissing ({'x','','y'}), [false,true,false]) %!assert (ismissing ({'x','','y';'z','a',''}), logical([0,1,0;0,0,1])) %!assert (ismissing ([1,2;NaN,2]), [false,false;true,false]) %!assert (ismissing ([1,2;NaN,2], 2), [false,true;false,true]) %!assert (ismissing ([1,2;NaN,2], [1 2]), [true,true;false,true]) %!assert (ismissing ([1,2;NaN,2], NaN), [false,false;true,false]) ## test nD array data %!assert (ismissing (cat(3,magic(2),magic(2))), logical (zeros (2,2,2))) %!assert (ismissing (cat(3,magic(2),[1 2;3 NaN])), logical (cat(3,[0,0;0,0],[0,0;0,1]))) %!assert (ismissing ([1 2; 3 4], [5 1; 2 0]), logical([1 1; 0 0])) %!assert (ismissing (cat(3,'f oo','ba r')), logical(cat(3,[0 1 0 0],[0 0 1 0]))) %!assert (ismissing (cat(3,{'foo'},{''},{'bar'})), logical(cat(3,0,1,0))) ## test data type handling %!assert (ismissing (double (NaN)), true) %!assert (ismissing (single (NaN)), true) %!assert (ismissing (' '), true) %!assert (ismissing ({''}), true) %!assert (ismissing ({' '}), false) %!assert (ismissing (double (eye(3)), single (1)), logical(eye(3))) %!assert (ismissing (double (eye(3)), true), logical(eye(3))) %!assert (ismissing (double (eye(3)), int32 (1)), logical(eye(3))) %!assert (ismissing (single (eye(3)), true), logical(eye(3))) %!assert (ismissing (single (eye(3)), double (1)), logical(eye(3))) %!assert (ismissing (single(eye(3)), int32 (1)), logical(eye(3))) ## test data types without missing values %!assert (ismissing ({'123', '', 123}), [false false false]) %!assert (ismissing (logical ([1 0 1])), [false false false]) %!assert (ismissing (int32 ([1 2 3])), [false false false]) %!assert (ismissing (uint32 ([1 2 3])), [false false false]) %!assert (ismissing ({1, 2, 3}), [false false false]) %!assert (ismissing ([struct struct struct]), [false false false]) %!assert (ismissing (logical (eye(3)), true), logical(eye(3))) %!assert (ismissing (logical (eye(3)), double (1)), logical(eye(3))) %!assert (ismissing (logical (eye(3)), single (1)), logical(eye(3))) %!assert (ismissing (logical (eye(3)), int32 (1)), logical(eye(3))) %!assert (ismissing (int32 (eye(3)), int32 (1)), logical(eye(3))) %!assert (ismissing (int32 (eye(3)), true), logical(eye(3))) %!assert (ismissing (int32 (eye(3)), double (1)), logical(eye(3))) %!assert (ismissing (int32 (eye(3)), single (1)), logical(eye(3))) ## test empty input handling %!assert (ismissing ([]), logical([])) %!assert (ismissing (''), logical([])) %!assert (ismissing (ones (0,1)), logical(ones(0,1))) %!assert (ismissing (ones (1,0)), logical(ones(1,0))) %!assert (ismissing (ones (1,2,0)), logical(ones(1,2,0))) ## Test input validation %!error ismissing () %!error <'indicator' and 'A' must have the same> ismissing ([1 2; 3 4], "abc") %!error <'indicator' and 'A' must have the same> ismissing ({"", "", ""}, 1) %!error <'indicator' and 'A' must have the same> ismissing (1, struct) %!error ismissing (struct, 1) statistics-release-1.7.3/inst/isoutlier.m000066400000000000000000001054651475240274700205330ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{TF} =} isoutlier (@var{x}) ## @deftypefnx {statistics} {@var{TF} =} isoutlier (@var{x}, @var{method}) ## @deftypefnx {statistics} {@var{TF} =} isoutlier (@var{x}, @qcode{"percentiles"}, @var{threshold}) ## @deftypefnx {statistics} {@var{TF} =} isoutlier (@var{x}, @var{movmethod}, @var{window}) ## @deftypefnx {statistics} {@var{TF} =} isoutlier (@dots{}, @var{dim}) ## @deftypefnx {statistics} {@var{TF} =} isoutlier (@dots{}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{TF}, @var{L}, @var{U}, @var{C}] =} isoutlier (@dots{}) ## ## Find outliers in data ## ## @code{isoutlier (@var{x})} returns a logical array whose elements are true ## when an outlier is detected in the corresponding element of @var{x}. ## @code{isoutlier} treats NaNs as missing values and removes them. ## ## @itemize ## @item ## If @var{x} is a matrix, then @code{isoutlier} operates on each column of ## @var{x} separately. ## @item ## If @var{x} is a multidimensional array, then @code{isoutlier} operates along ## the first dimension of @var{x} whose size does not equal 1. ## @end itemize ## ## By default, an outlier is a value that is more than three scaled median ## absolute deviations (MAD) from the median. The scaled median is defined as ## @code{c*median(abs(A-median(A)))}, where @code{c=-1/(sqrt(2)*erfcinv(3/2))}. ## ## @code{isoutlier (@var{x}, @var{method})} specifies a method for detecting ## outliers. The following methods are available: ## ## @multitable @columnfractions 0.13 0.02 0.8 ## @headitem Method @tab @tab Description ## @item @qcode{"median"} @tab @tab Outliers are defined as elements more than ## three scaled MAD from the median. ## @item @qcode{"mean"} @tab @tab Outliers are defined as elements more than ## three standard deviations from the mean. ## @item @qcode{"quartiles"} @tab @tab Outliers are defined as elements more ## than 1.5 interquartile ranges above the upper quartile (75 percent) or below ## the lower quartile (25 percent). This method is useful when the data in ## @var{x} is not normally distributed. ## @item @qcode{"grubbs"} @tab @tab Outliers are detected using Grubbs’ test for ## outliers, which removes one outlier per iteration based on hypothesis ## testing. This method assumes that the data in @var{x} is normally ## distributed. ## @item @qcode{"gesd"} @tab @tab Outliers are detected using the generalized ## extreme Studentized deviate test for outliers. This iterative method is ## similar to @qcode{"grubbs"}, but can perform better when there are multiple ## outliers masking each other. ## @end multitable ## ## @code{isoutlier (@var{x}, @qcode{"percentiles"}, @var{threshold})} detects ## outliers based on a percentile thresholds, specified as a two-element row ## vector whose elements are in the interval @math{[0, 100]}. The first element ## indicates the lower percentile threshold, and the second element indicates ## the upper percentile threshold. The first element of threshold must be less ## than the second element. ## ## @code{isoutlier (@var{x}, @var{movmethod}, @var{window})} specifies a moving ## method for detecting outliers. The following methods are available: ## ## @multitable @columnfractions 0.13 0.02 0.8 ## @headitem Method @tab @tab Description ## @item @qcode{"movmedian"} @tab @tab Outliers are defined as elements more ## than three local scaled MAD from the local median over a window length ## specified by @var{window}. ## @item @qcode{"movmean"} @tab @tab Outliers are defined as elements more than ## three local standard deviations from the from the local mean over a window ## length specified by @var{window}. ## @end multitable ## ## @var{window} must be a positive integer scalar or a two-element vector of ## positive integers. When @var{window} is a scalar, if it is an odd number, ## the window is centered about the current element and contains ## @qcode{@var{window} - 1} neighboring elements. If even, then the window is ## centered about the current and previous elements. When @var{window} is a ## two-element vector of positive integers @math{[nb, na]}, the window contains ## the current element, @math{nb} elements before the current element, and ## @math{na} elements after the current element. When @qcode{"SamplePoints"} ## are also specified, @var{window} can take any real positive values (either as ## a scalar or a two-element vector) and in this case, the windows are computed ## relative to the sample points. ## ## @var{dim} specifies the operating dimension and it must be a positive integer ## scalar. If not specified, then, by default, @code{isoutlier} operates along ## the first non-singleton dimension of @var{x}. ## ## The following optional parameters can be specified as @var{Name}/@var{Value} ## paired arguments. ## ## @itemize ## @item @qcode{"SamplePoints"} can be specified as a vector of sample points ## with equal length as the operating dimension. The sample points represent ## the x-axis location of the data and must be sorted and contain unique ## elements. Sample points do not need to be uniformly sampled. By default, ## the vector is @qcode{[1, 2, 3, @dots{}, @var{n}]}, where ## @qcode{@var{n} = size (@var{x}, @var{dim})}. You can use unequally spaced ## @qcode{"SamplePoints"} to define a variable-length window for one of the ## moving methods available. ## ## @item @qcode{"ThresholdFactor"} can be specified as a nonnegative scalar. ## For methods @qcode{"median"} and @qcode{"movmedian"}, the detection threshold ## factor replaces the number of scaled MAD, which is 3 by default. For methods ## @qcode{"mean"} and @qcode{"movmean"}, the detection threshold factor replaces ## the number of standard deviations, which is 3 by default. For methods ## @qcode{"grubbs"} and @qcode{"gesd"}, the detection threshold factor ranges ## from 0 to 1, specifying the critical @math{alpha}-value of the respective ## test, and it is 0.05 by default. For the @qcode{"quartiles"} method, the ## detection threshold factor replaces the number of interquartile ranges, which ## is 1.5 by default. @qcode{"ThresholdFactor"} is not supported for the ## @qcode{"quartiles"} method. ## ## @item @qcode{"MaxNumOutliers"} is only relevant to the @qcode{"gesd"} method ## and it must be a positive integer scalar specifying the maximum number of ## outliers returned by the @qcode{"gesd"} method. By default, it is the ## integer nearest to the 10% of the number of elements along the operating ## dimension in @var{x}. The @qcode{"gesd"} method assumes the nonoutlier input ## data is sampled from an approximate normal distribution. When the data is ## not sampled in this way, the number of returned outliers might exceed the ## @qcode{MaxNumOutliers} value. ## @end itemize ## ## @code{[@var{TF}, @var{L}, @var{U}, @var{C}] = isoutlier (@dots{})} returns ## up to 4 output arguments as described below. ## ## @itemize ## @item @var{TF} is the outlier indicator with the same size a @var{x}. ## ## @item @var{L} is the lower threshold used by the outlier detection method. ## If @var{method} is used for outlier detection, then @var{L} has the same size ## as @var{x} in all dimensions except for the operating dimension where the ## length is 1. If @var{movmethod} is used, then @var{L} has the same size as ## @var{x}. ## ## @item @var{U} is the upper threshold used by the outlier detection method. ## If @var{method} is used for outlier detection, then @var{U} has the same size ## as @var{x} in all dimensions except for the operating dimension where the ## length is 1. If @var{movmethod} is used, then @var{U} has the same size as ## @var{x}. ## ## @item @var{C} is the center value used by the outlier detection method. ## If @var{method} is used for outlier detection, then @var{C} has the same size ## as @var{x} in all dimensions except for the operating dimension where the ## length is 1. If @var{movmethod} is used, then @var{C} has the same size as ## @var{x}. For @qcode{"median"}, @qcode{"movmedian"}, @qcode{"mean"}, and ## @qcode{"movmean"} methods, @var{C} is computed by taking into acount the ## outlier values. For @qcode{"grubbs"} and @qcode{"gesd"} methods, @var{C} is ## computed by excluding the outliers. For the @qcode{"percentiles"} method, ## @var{C} is the average between @var{U} and @var{L} thresholds. ## @end itemize ## ## @seealso{filloutliers, rmoutliers, ismissing} ## @end deftypefn function [TF, L, U, C] = isoutlier (x, varargin) ## Check for valid input data if (nargin < 1) print_usage; endif ## Handle case if X is a scalar if (isscalar (x)) TF = false; L = x; U = x; C = x; return endif ## Add defaults dim = []; method = "median"; window = []; SamplePoints = []; ThresholdFactor = 3; MaxNumOutliers = []; ## MATLAB's constant for scaled Median Absolute Deviation ## c = -1 / (sqrt (2) * erfcinv (3/2)) c = 1.482602218505602; ## Parse exrta arguments while (numel (varargin) > 0) if (ischar (varargin{1})) switch (lower (varargin{1})) case "median" method = "median"; ThresholdFactor = 3; varargin(1) = []; case "mean" method = "mean"; ThresholdFactor = 3; varargin(1) = []; case "quartiles" method = "quartiles"; ThresholdFactor = 1.5; varargin(1) = []; case "grubbs" method = "grubbs"; ThresholdFactor = 0.05; varargin(1) = []; case "gesd" method = "gesd"; ThresholdFactor = 0.05; MaxNumOutliers = []; varargin(1) = []; case "movmedian" method = "movmedian"; window = varargin{2}; if (! isnumeric (window) || numel (window) < 1 || numel (window) > 2 || any (window <= 0)) error (strcat (["isoutlier: WINDOW must be a positive scalar"], ... [" or a two-element vector of positive values"])); endif varargin([1:2]) = []; case "movmean" method = "movmean"; window = varargin{2}; if (! isnumeric (window) || numel (window) < 1 || numel (window) > 2 || any (window <= 0)) error (strcat (["isoutlier: WINDOW must be a positive scalar"], ... [" or a two-element vector of positive values"])); endif varargin([1:2]) = []; case "percentiles" method = "percentiles"; threshold = varargin{2}; if (! isnumeric (threshold) || ! (numel (threshold) == 2)) error (strcat (["isoutlier: THRESHOLD must be a two-element"], ... [" vector whose elements are in the interval"], ... [" [0, 100]."])); endif if (! (threshold(1) < threshold(2)) || threshold(1) < 0 || threshold(2) > 100) error (strcat (["isoutlier: THRESHOLD must be a two-element"], ... [" vector whose elements are in the interval"], ... [" [0, 100]."])); endif varargin([1:2]) = []; case "samplepoints" SamplePoints = varargin{2}; if (! isvector (SamplePoints) || isscalar (SamplePoints)) error ("isoutlier: sample points must be a vector."); endif if (numel (unique (SamplePoints)) != numel (SamplePoints)) error ("isoutlier: sample points must be unique."); endif if (any (sort (SamplePoints) != SamplePoints)) error ("isoutlier: sample points must be sorted."); endif varargin([1:2]) = []; case "thresholdfactor" ThresholdFactor = varargin{2}; if (! isscalar (ThresholdFactor) || ThresholdFactor <= 0) error ("isoutlier: threshold factor must be a nonnegative scalar."); endif varargin([1:2]) = []; case "maxnumoutliers" MaxNumOutliers = varargin{2}; if (! isscalar (MaxNumOutliers) || MaxNumOutliers <= 0 || ! (fix (MaxNumOutliers) == MaxNumOutliers)) error (strcat (["isoutlier: maximum outlier count must be a"], ... [" positive integer scalar."])); endif varargin([1:2]) = []; otherwise error ("isoutlier: invalid input argument."); endswitch elseif (isnumeric (varargin{1})) dim = varargin{1}; if (! fix (dim) == dim || dim < 1 || ! isscalar (dim) || ! isscalar (varargin{1})) error ("isoutlier: DIM must be a positive integer scalar."); endif varargin(1) = []; else error ("isoutlier: invalid input argument."); endif endwhile ## Find 1st operating dimension (if empty) if (isempty (dim)) szx = size (x); (dim = find (szx != 1, 1)) || (dim = 1); endif ## Check for valid WINDOW unless Sample Points are given if (isempty (SamplePoints) && ! isempty (window)) if (! all (fix (window) == window)) error (strcat (["isoutlier: WINDOW must be a positive integer"], ... [" scalar or a two-element vector of positive"], ... [" integers, unless SamplePoints are defined."])); endif endif ## Check for valid value of ThresholdFactor for 'grubbs' and 'geds' methods if (any (strcmpi (method, {"grubbs", "gesd"})) && ThresholdFactor > 1) error (strcat (["isoutlier: threshold factor must must be in [0 1]"], ... [" range for 'grubbs' and 'gesd' methods."])); endif ## Switch methods switch method case "median" [L, U, C] = median_method (x, dim, ThresholdFactor, c); TF = x < L | x > U; case "mean" [L, U, C] = mean_method (x, dim, ThresholdFactor); TF = x < L | x > U; case "quartiles" [L, U, C] = quartiles_method (x, dim, ThresholdFactor); TF = x < L | x > U; case "grubbs" [TF, L, U, C] = grubbs_method (x, dim, ThresholdFactor); case "gesd" [L, U, C] = gesd_method (x, dim, ThresholdFactor, MaxNumOutliers); TF = x < L | x > U; case "movmedian" sp = SamplePoints; [L, U, C] = movmedian_method (x, dim, ThresholdFactor, c, window, sp); TF = x < L | x > U; case "movmean" sp = SamplePoints; [L, U, C] = movmean_method (x, dim, ThresholdFactor, window, sp); TF = x < L | x > U; case "percentiles" [L, U, C] = percentiles_method (x, dim, threshold); TF = x < L | x > U; endswitch endfunction ## Find lower and upper outlier thresholds with median method function [L, U, C] = median_method (x, dim, ThresholdFactor, c) C = median (x, dim, "omitnan"); sMAD = c * mad (x, 1, dim); L = C - ThresholdFactor * sMAD; U = C + ThresholdFactor * sMAD; endfunction ## Find lower and upper outlier thresholds with mean method function [L, U, M] = mean_method (x, dim, ThresholdFactor) M = mean (x, dim, "omitnan"); S = std (x, [], dim, "omitnan"); L = M - ThresholdFactor * S; U = M + ThresholdFactor * S; endfunction ## Find lower and upper outlier thresholds with quartiles method function [L, U, C] = quartiles_method (x, dim, ThresholdFactor) Q = quantile (x, dim); C = Q(3); L = Q(2) - (Q(4) - Q(2)) * ThresholdFactor; U = Q(4) + (Q(4) - Q(2)) * ThresholdFactor; endfunction ## Find lower and upper outlier thresholds with grubbs method function [TF, L, U, C] = grubbs_method (x, dim, ThresholdFactor) ## Move the desired dim to be the 1st dimension (rows) szx = size (x); # size of dimensions N = szx(dim); # elements in operating dimension nd = length (szx); # number of dimensions dperm = [dim, 1:(dim-1), (dim+1):nd]; # permutation of dimensions x = permute (x, dperm); # permute dims to first dimension ncols = prod (szx(dperm(2:end))); # rest of dimensions as single column x = reshape (x, N, ncols); # reshape input ## Create return matrices L = zeros ([1, szx(dperm(2:end))]); U = L; C = L; TF = false (size (x)); ## Apply processing to each column for i = 1:ncols tmp_x = x(:,i); TFvec = [(i-1)*size(x,1)+1:i*size(x,1)]; TFvec(isnan (tmp_x)) = []; tmp_x(isnan (tmp_x)) = []; ## Search for outliers (one at a time) while (true) ## Get descriptive statistics n = length (tmp_x); C(i) = mean (tmp_x); S = std (tmp_x); ## Locate maximum deviation from mean dif_x = abs (tmp_x - C(i)); max_x = max (dif_x); loc_x = find (dif_x == max_x, 1); ## Calculate Grubbs's critical value t_crit = tinv (ThresholdFactor / (2 * n), n - 2); G_crit = ((n - 1) / sqrt (n)) * abs (t_crit) / sqrt (n - 2 + t_crit ^ 2); ## Check hypothesis if (max_x / S > G_crit) tmp_x(loc_x) = []; TF(TFvec(loc_x)) = true; TFvec(loc_x) = []; else break; endif endwhile L(i) = C(i) - S * G_crit; U(i) = C(i) + S * G_crit; endfor ## Restore shape TF = ipermute (TF, dperm); L = ipermute (L, dperm); U = ipermute (U, dperm); C = ipermute (C, dperm); endfunction ## Find lower and upper outlier thresholds with gesd method function [L, U, C] = gesd_method (x, dim, ThresholdFactor, MaxNumOutliers) ## Add default value in MaxNumOutliers (if empty) szx = size (x); N = szx(dim); if (isempty (MaxNumOutliers)) MaxNumOutliers = ceil (N * 0.1); endif ## Move the desired dim to be the 1st dimension (rows) nd = length (szx); # number of dimensions dperm = [dim, 1:(dim-1), (dim+1):nd]; # permutation of dimensions x = permute (x, dperm); # permute dims to first dimension ncols = prod (szx(dperm(2:end))); # rest of dimensions as single column x = reshape (x, N, ncols); # reshape input ## Create return matrices L = zeros ([1, szx(dperm(2:end))]); U = L; C = L; TF = false (size (x)); ## Apply processing to each column for i = 1:ncols tmp_x = x(:,i); vec_x = [(i-1)*size(x,1)+1:i*size(x,1)]; vec_x(isnan (tmp_x)) = []; tmp_x(isnan (tmp_x)) = []; n = length (tmp_x); if (n > 1) mean_x = zeros (MaxNumOutliers,1); S = zeros (MaxNumOutliers,1); lambda = zeros (MaxNumOutliers,1); R = zeros (MaxNumOutliers,1); Ridx = zeros (MaxNumOutliers,1); ## Search for given outliers for j = 1:MaxNumOutliers ## Get descriptive statistics mean_x(j) = mean (tmp_x); S(j) = std (tmp_x); ## Locate maximum deviation from mean dif_x = abs (tmp_x - mean_x(j)); max_x = max (dif_x); loc_x = find (dif_x == max_x, 1); ## Calculate R R(j) = max_x / S(j); tmp_x(loc_x) = []; Ridx(j) = vec_x(loc_x); vec_x(loc_x) = []; ## Calculate lambda pp = 1 - ThresholdFactor / (2 * (n - j + 1)); t = tinv (pp, n - j - 1); lambda(j) = (n - j) * t / sqrt ((n - j - 1 + t .^ 2) * (n - j + 1)); endfor ## Find largest index idx = find (R > lambda, 1, "last"); if (isempty (idx)) TFidx = 1; else TFidx = min (idx + 1, MaxNumOutliers); endif L(i) = mean_x(TFidx) - S(TFidx) * lambda(TFidx); U(i) = mean_x(TFidx) + S(TFidx) * lambda(TFidx); C(i) = mean_x(TFidx); endif endfor ## Restore shape L = ipermute (L, dperm); U = ipermute (U, dperm); C = ipermute (C, dperm); endfunction ## Find lower and upper outlier thresholds with movmedian method function [L, U, C] = movmedian_method (x, dim, ThresholdFactor, c, window, sp); szx = size (x); N = szx(dim); ## Constrain window to the element in the operating dimension if (numel (window) == 1 && window > N) window = N; elseif (numel (window) == 2 && sum (window) > N) window = N; endif if (isempty (sp)) FCN = @(x) median (x, "omitnan"); C = movfun (FCN, x, window, "dim", dim); FCN = @(x) mad (x, 1); MAD = movfun (FCN, x, window, "dim", dim); else ## Check that sample points(sp) have the N elements if (numel (sp) != N) error (strcat (["isoutlier: sample points must have the same size"], ... [" as the operating dimension."])); endif ## Move the desired dim to be the 1st dimension (rows) nd = length (szx); # number of dimensions dperm = [dim, 1:(dim-1), (dim+1):nd]; # permutation of dimensions x = permute (x, dperm); # permute dims to first dimension ncols = prod (szx(dperm(2:end))); # rest of dimensions as single column x = reshape (x, N, ncols); # reshape input ## Find beg+end from window if (numel (window) == 2) w_lo = window(1); w_hi = window(2); else if (mod (window, 2) == 1) w_lo = w_hi = (window - 1) / 2; else w_lo = window / 2; w_hi = w_lo - 1; endif endif ## Create return matrices C = zeros (size (x)); MAD = C; for i = 1:ncols tmp_x = x(:,i); for j = 1:N cp = sp - sp(j); nb = length (cp(cp < 0 & cp >= -w_lo)); na = length (cp(cp > 0 & cp <= w_hi)); sp_ind = [j-nb:j+na]; C(j,i) = median (tmp_x(sp_ind), "omitnan"); MAD(j,i) = mad (tmp_x(sp_ind), 1); endfor endfor ## Restore shape C = ipermute (C, dperm); MAD = ipermute (MAD, dperm); endif ## Compute scaled MAD sMAD = c * MAD; L = C - ThresholdFactor * sMAD; U = C + ThresholdFactor * sMAD; endfunction ## Find lower and upper outlier thresholds with movmean method function [L, U, M] = movmean_method (x, dim, ThresholdFactor, window, sp); ## Constrain window to the element in the operating dimension szx = size (x); N = szx(dim); if (numel (window) == 1 && window > N) window = N; elseif (numel (window) == 2 && sum (window) > N) window = N; endif if (isempty (sp)) FCN = @(x) mean (x, "omitnan"); M = movfun (FCN, x, window, "dim", dim); FCN = @(x) std (x, [], "omitnan"); S = movfun (FCN, x, window, "dim", dim); else ## Check that sample points(sp) have the N elements if (numel (sp) != N) error (strcat (["isoutlier: sample points must have the same size"], ... [" as the operating dimension."])); endif ## Move the desired dim to be the 1st dimension (rows) nd = length (szx); # number of dimensions dperm = [dim, 1:(dim-1), (dim+1):nd]; # permutation of dimensions x = permute (x, dperm); # permute dims to first dimension ncols = prod (szx(dperm(2:end))); # rest of dimensions as single column x = reshape (x, N, ncols); # reshape input ## Find beg+end from window if (numel (window) == 2) w_lo = window(1); w_hi = window(2); else if (mod (window, 2) == 1) w_lo = w_hi = (window - 1) / 2; else w_lo = window / 2; w_hi = w_lo - 1; endif endif ## Create return matrices M = zeros (size (x)); S = M; for i = 1:ncols tmp_x = x(:,i); for j = 1:N cp = sp - sp(j); nb = length (cp(cp < 0 & cp >= -w_lo)); na = length (cp(cp > 0 & cp <= w_hi)); sp_ind = [j-nb:j+na]; M(j,i) = mean (tmp_x(sp_ind), "omitnan"); S(j,i) = std (tmp_x(sp_ind), [], "omitnan"); endfor endfor ## Restore shape M = ipermute (M, dperm); S = ipermute (S, dperm); endif L = M - ThresholdFactor * S; U = M + ThresholdFactor * S; endfunction ## Find lower and upper outlier thresholds with percentiles method function [L, U, C] = percentiles_method (x, dim, threshold) P = [threshold(1)/100, threshold(2)/100]; Q = quantile (x, P, dim); L = Q(1); U = Q(2); C = (L + U) / 2; endfunction %!demo %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! TF = isoutlier (A, "mean") %!demo %! ## Use a moving detection method to detect local outliers in a sine wave %! %! x = -2*pi:0.1:2*pi; %! A = sin(x); %! A(47) = 0; %! time = datenum (2023,1,1,0,0,0) + (1/24)*[0:length(x)-1] - 730485; %! TF = isoutlier (A, "movmedian", 5*(1/24), "SamplePoints", time); %! plot (time, A) %! hold on %! plot (time(TF), A(TF), "x") %! datetick ('x', 20, 'keepticks') %! legend ("Original Data", "Outlier Data") %!demo %! ## Locate an outlier in a vector of data and visualize the outlier %! %! x = 1:10; %! A = [60 59 49 49 58 100 61 57 48 58]; %! [TF, L, U, C] = isoutlier (A); %! plot (x, A); %! hold on %! plot (x(TF), A(TF), "x"); %! xlim ([1,10]); %! line ([1,10], [L, L], "Linestyle", ":"); %! text (1.1, L-2, "Lower Threshold"); %! line ([1,10], [U, U], "Linestyle", ":"); %! text (1.1, U-2, "Upper Threshold"); %! line ([1,10], [C, C], "Linestyle", ":"); %! text (1.1, C-3, "Center Value"); %! legend ("Original Data", "Outlier Data"); ## Output validation tests (checked against MATLAB) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! assert (isoutlier (A, "mean"), logical([zeros(1,8) 1 zeros(1,6)])) %! assert (isoutlier (A, "median"), ... %! logical([zeros(1,3) 1 zeros(1,4) 1 zeros(1,6)])) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "mean"); %! assert (L, -109.2459044922864, 1e-12) %! assert (U, 264.9792378256198, 1e-12) %! assert (C, 77.8666666666666, 1e-12) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "median"); %! assert (L, 50.104386688966386, 1e-12) %! assert (U, 67.895613311033610, 1e-12) %! assert (C, 59) %!test %! A = magic(5) + diag(200*ones(1,5)); %! T = logical (eye (5)); %! assert (isoutlier (A, 2), T) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "movmedian", 5); %! l = [54.5522, 52.8283, 54.5522, 54.5522, 54.5522, 53.5522, 53.5522, ... %! 53.5522, 47.6566, 56.5522, 57.5522, 56.5522, 51.1044, 52.3283, 53.5522]; %! u = [63.4478, 66.1717, 63.4478, 63.4478, 63.4478, 62.4478, 62.4478, ... %! 62.4478, 74.3434, 65.4478, 66.4478, 65.4478, 68.8956, 65.6717, 62.4478]; %! c = [59, 59.5, 59, 59, 59, 58, 58, 58, 61, 61, 62, 61, 60, 59, 58]; %! assert (L, l, 1e-4) %! assert (U, u, 1e-4) %! assert (C, c) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "movmedian", 5, "SamplePoints", [1:15]); %! l = [54.5522, 52.8283, 54.5522, 54.5522, 54.5522, 53.5522, 53.5522, ... %! 53.5522, 47.6566, 56.5522, 57.5522, 56.5522, 51.1044, 52.3283, 53.5522]; %! u = [63.4478, 66.1717, 63.4478, 63.4478, 63.4478, 62.4478, 62.4478, ... %! 62.4478, 74.3434, 65.4478, 66.4478, 65.4478, 68.8956, 65.6717, 62.4478]; %! c = [59, 59.5, 59, 59, 59, 58, 58, 58, 61, 61, 62, 61, 60, 59, 58]; %! assert (L, l, 1e-4) %! assert (U, u, 1e-4) %! assert (C, c) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "movmean", 5); %! l = [54.0841, 6.8872, 11.5608, 12.1518, 11.0210, 10.0112, -218.2840, ... %! -217.2375, -215.1239, -213.4890, -211.3264, 55.5800, 52.9589, ... %! 52.5979, 51.0627]; %! u = [63.2492, 131.1128, 122.4392, 122.2482, 122.5790, 122.7888, 431.0840, ... %! 430.8375, 430.3239, 429.8890, 429.3264, 65.6200, 66.6411, 65.9021, ... %! 66.9373]; %! c = [58.6667, 69, 67, 67.2, 66.8, 66.4, 106.4, 106.8, 107.6, 108.2, 109, ... %! 60.6, 59.8, 59.25, 59]; %! assert (L, l, 1e-4) %! assert (U, u, 1e-4) %! assert (C, c, 1e-4) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "movmean", 5, "SamplePoints", [1:15]); %! l = [54.0841, 6.8872, 11.5608, 12.1518, 11.0210, 10.0112, -218.2840, ... %! -217.2375, -215.1239, -213.4890, -211.3264, 55.5800, 52.9589, ... %! 52.5979, 51.0627]; %! u = [63.2492, 131.1128, 122.4392, 122.2482, 122.5790, 122.7888, 431.0840, ... %! 430.8375, 430.3239, 429.8890, 429.3264, 65.6200, 66.6411, 65.9021, ... %! 66.9373]; %! c = [58.6667, 69, 67, 67.2, 66.8, 66.4, 106.4, 106.8, 107.6, 108.2, 109, ... %! 60.6, 59.8, 59.25, 59]; %! assert (L, l, 1e-4) %! assert (U, u, 1e-4) %! assert (C, c, 1e-4) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "gesd"); %! assert (TF, logical ([0 0 0 1 0 0 0 0 1 0 0 0 0 0 0])) %! assert (L, 34.235977035439944, 1e-12) %! assert (U, 89.764022964560060, 1e-12) %! assert (C, 62) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "gesd", "ThresholdFactor", 0.01); %! assert (TF, logical ([0 0 0 1 0 0 0 0 1 0 0 0 0 0 0])) %! assert (L, 31.489256770616173, 1e-12) %! assert (U, 92.510743229383820, 1e-12) %! assert (C, 62) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "gesd", "ThresholdFactor", 5e-10); %! assert (TF, logical ([0 0 0 0 0 0 0 0 1 0 0 0 0 0 0])) %! assert (L, 23.976664158788935, 1e-12) %! assert (U, 100.02333584121110, 1e-12) %! assert (C, 62) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "grubbs"); %! assert (TF, logical ([0 0 0 1 0 0 0 0 1 0 0 0 0 0 0])) %! assert (L, 54.642809574646606, 1e-12) %! assert (U, 63.511036579199555, 1e-12) %! assert (C, 59.076923076923080, 1e-12) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "grubbs", "ThresholdFactor", 0.01); %! assert (TF, logical ([0 0 0 1 0 0 0 0 1 0 0 0 0 0 0])) %! assert (L, 54.216083184201850, 1e-12) %! assert (U, 63.937762969644310, 1e-12) %! assert (C, 59.076923076923080, 1e-12) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "percentiles", [10 90]); %! assert (TF, logical ([0 0 0 0 0 0 0 0 1 0 0 0 0 0 0])) %! assert (L, 57) %! assert (U, 100) %! assert (C, 78.5) %!test %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %! [TF, L, U, C] = isoutlier (A, "percentiles", [20 80]); %! assert (TF, logical ([1 0 0 1 0 0 1 0 1 0 0 0 0 0 1])) %! assert (L, 57.5) %! assert (U, 62) %! assert (C, 59.75) ## Test input validation %!shared A %! A = [57 59 60 100 59 58 57 58 300 61 62 60 62 58 57]; %!error ... %! isoutlier (A, "movmedian", 0); %!error ... %! isoutlier (A, "movmedian", []); %!error ... %! isoutlier (A, "movmedian", [2 3 4]); %!error ... %! isoutlier (A, "movmedian", 1.4); %!error ... %! isoutlier (A, "movmedian", [0 1]); %!error ... %! isoutlier (A, "movmedian", [2 -1]); %!error ... %! isoutlier (A, "movmedian", {2 3}); %!error ... %! isoutlier (A, "movmedian", "char"); %! %!error ... %! isoutlier (A, "movmean", 0); %!error ... %! isoutlier (A, "movmean", []); %!error ... %! isoutlier (A, "movmean", [2 3 4]); %!error ... %! isoutlier (A, "movmean", 1.4); %!error ... %! isoutlier (A, "movmean", [0 1]); %!error ... %! isoutlier (A, "movmean", [2 -1]); %!error ... %! isoutlier (A, "movmean", {2 3}); %!error ... %! isoutlier (A, "movmean", "char"); %! %!error ... %! isoutlier (A, "percentiles", [-1 90]); %!error ... %! isoutlier (A, "percentiles", [10 -90]); %!error ... %! isoutlier (A, "percentiles", [90]); %!error ... %! isoutlier (A, "percentiles", [90 20]); %!error ... %! isoutlier (A, "percentiles", [90 20]); %!error ... %! isoutlier (A, "percentiles", [10 20 90]); %!error ... %! isoutlier (A, "percentiles", {10 90}); %!error ... %! isoutlier (A, "percentiles", "char"); %! %!error ... %! isoutlier (A, "movmean", 5, "SamplePoints", ones(3,15)); %!error ... %! isoutlier (A, "movmean", 5, "SamplePoints", 15); %!error ... %! isoutlier (A, "movmean", 5, "SamplePoints", [1,1:14]); %!error ... %! isoutlier (A, "movmean", 5, "SamplePoints", [2,1,3:15]); %!error ... %! isoutlier (A, "movmean", 5, "SamplePoints", [1:14]); %! %!error ... %! isoutlier (A, "movmean", 5, "ThresholdFactor", [1:14]); %!error ... %! isoutlier (A, "movmean", 5, "ThresholdFactor", -1); %!error ... %! isoutlier (A, "gesd", "ThresholdFactor", 3); %!error ... %! isoutlier (A, "grubbs", "ThresholdFactor", 3); %! %!error ... %! isoutlier (A, "movmean", 5, "MaxNumOutliers", [1:14]); %!error ... %! isoutlier (A, "movmean", 5, "MaxNumOutliers", -1); %!error ... %! isoutlier (A, "movmean", 5, "MaxNumOutliers", 0); %!error ... %! isoutlier (A, "movmean", 5, "MaxNumOutliers", 1.5); %! %!error ... %! isoutlier (A, {"movmean"}, 5, "SamplePoints", [1:15]); %!error isoutlier (A, {1}); %!error isoutlier (A, true); %!error isoutlier (A, false); %!error isoutlier (A, 0); %!error isoutlier (A, [1 2]); %!error isoutlier (A, -2); statistics-release-1.7.3/inst/jackknife.m000066400000000000000000000125741475240274700204370ustar00rootroot00000000000000## Copyright (C) 2011 Alexander Klein ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{jackstat} =} jackknife (@var{E}, @var{x}) ## @deftypefnx {statistics} {@var{jackstat} =} jackknife (@var{E}, @var{x}, @dots{}) ## ## Compute jackknife estimates of a parameter taking one or more given samples ## as parameters. ## ## In particular, @var{E} is the estimator to be jackknifed as a function name, ## handle, or inline function, and @var{x} is the sample for which the estimate ## is to be taken. The @var{i}-th entry of @var{jackstat} will contain the ## value of the estimator on the sample @var{x} with its @var{i}-th row omitted. ## ## @example ## @group ## jackstat (@var{i}) = @var{E}(@var{x}(1 : @var{i} - 1, @var{i} + 1 : length(@var{x}))) ## @end group ## @end example ## ## Depending on the number of samples to be used, the estimator must have the ## appropriate form: ## @itemize ## @item ## If only one sample is used, then the estimator need not be concerned with ## cell arrays, for example jackknifing the standard deviation of a sample can ## be performed with @code{@var{jackstat} = jackknife (@@std, rand (100, 1))}. ## @item ## If, however, more than one sample is to be used, the samples must all be of ## equal size, and the estimator must address them as elements of a cell-array, ## in which they are aggregated in their order of appearance: ## @end itemize ## ## @example ## @group ## @var{jackstat} = jackknife (@@(x) std(x@{1@})/var(x@{2@}), ## rand (100, 1), randn (100, 1)) ## @end group ## @end example ## ## If all goes well, a theoretical value @var{P} for the parameter is already ## known, @var{n} is the sample size, ## ## @code{@var{t} = @var{n} * @var{E}(@var{x}) - (@var{n} - 1) * ## mean(@var{jackstat})} ## ## and ## ## @code{@var{v} = sumsq(@var{n} * @var{E}(@var{x}) - (@var{n} - 1) * ## @var{jackstat} - @var{t}) / (@var{n} * (@var{n} - 1))} ## ## then ## ## @code{(@var{t}-@var{P})/sqrt(@var{v})} should follow a t-distribution with ## @var{n}-1 degrees of freedom. ## ## Jackknifing is a well known method to reduce bias. ## Further details can be found in: ## @subheading References ## ## @enumerate ## @item ## Rupert G. Miller. The jackknife - a review. Biometrika (1974), 61(1):1-15. ## doi:10.1093/biomet/61.1.1 ## @item ## Rupert G. Miller. Jackknifing Variances. Ann. Math. Statist. (1968), ## Volume 39, Number 2, 567-582. doi:10.1214/aoms/1177698418 ## @end enumerate ## @end deftypefn function jackstat = jackknife (anEstimator, varargin) ## Convert function name to handle if necessary, or throw an error. if (! strcmp (typeinfo (anEstimator), "function handle")) if (isascii (anEstimator)) anEstimator = str2func (anEstimator); else error (strcat (["jackknife: estimators must be passed as function"], ... [" names or handles."])); endif endif ## Simple jackknifing can be done with a single vector argument, and ## first and foremost with a function that does not care about cell-arrays. if (length (varargin) == 1 && isnumeric (varargin {1})) aSample = varargin{1}; g = length (aSample); jackstat = zeros (1, g); for k = 1:g jackstat (k) = anEstimator (aSample([1:k - 1,k + 1:g])); endfor ## More complicated input requires more work, however. else g = cellfun (@(x) length (x), varargin); if (any (g - g(1))) error ("jackknife: all passed data must be of equal length."); endif g = g(1); jackstat = zeros (1, g); for k = 1:g jackstat(k) = anEstimator (cellfun (@(x) x( [ 1 : k - 1, k + 1 : g ]), ... varargin, "UniformOutput", false)); endfor endif endfunction %!demo %! for k = 1:1000 %! rand ("seed", k); # for reproducibility %! x = rand (10, 1); %! s(k) = std (x); %! jackstat = jackknife (@std, x); %! j(k) = 10 * std (x) - 9 * mean (jackstat); %! endfor %! figure(); %! hist ([s', j'], 0:sqrt(1/12)/10:2*sqrt(1/12)) %!demo %! for k = 1:1000 %! randn ("seed", k); # for reproducibility %! x = randn (1, 50); %! rand ("seed", k); # for reproducibility %! y = rand (1, 50); %! jackstat = jackknife (@(x) std(x{1})/std(x{2}), y, x); %! j(k) = 50 * std (y) / std (x) - 49 * mean (jackstat); %! v(k) = sumsq ((50 * std (y) / std (x) - 49 * jackstat) - j(k)) / (50 * 49); %! endfor %! t = (j - sqrt (1 / 12)) ./ sqrt (v); %! figure(); %! plot (sort (tcdf (t, 49)), ... %! "-;Almost linear mapping indicates good fit with t-distribution.;") ## Test output %!test %! ##Example from Quenouille, Table 1 %! d=[0.18 4.00 1.04 0.85 2.14 1.01 3.01 2.33 1.57 2.19]; %! jackstat = jackknife ( @(x) 1/mean(x), d ); %! assert ( 10 / mean(d) - 9 * mean(jackstat), 0.5240, 1e-5 ); statistics-release-1.7.3/inst/kmeans.m000066400000000000000000000620761475240274700177720ustar00rootroot00000000000000## Copyright (C) 2011 Soren Hauberg ## Copyright (C) 2012 Daniel Ward ## Copyright (C) 2015-2016 Lachlan Andrew ## Copyright (C) 2016 Michael Bentley ## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{idx} =} kmeans (@var{data}, @var{k}) ## @deftypefnx {statistics} {[@var{idx}, @var{centers}] =} kmeans (@var{data}, @var{k}) ## @deftypefnx {statistics} {[@var{idx}, @var{centers}, @var{sumd}] =} kmeans (@var{data}, @var{k}) ## @deftypefnx {statistics} {[@var{idx}, @var{centers}, @var{sumd}, @var{dist}] =} kmeans (@var{data}, @var{k}) ## @deftypefnx {statistics} {[@dots{}] =} kmeans (@var{data}, @var{k}, @var{param1}, @var{value1}, @dots{}) ## @deftypefnx {statistics} {[@dots{}] =} kmeans (@var{data}, [], @qcode{"start"}, @var{start}, @dots{}) ## ## Perform a @var{k}-means clustering of the @math{NxD} matrix @var{data}. ## ## If parameter @qcode{"start"} is specified, then @var{k} may be empty ## in which case @var{k} is set to the number of rows of @var{start}. ## ## The outputs are: ## ## @multitable @columnfractions 0.15 0.05 0.8 ## @item @var{idx} @tab @tab An @math{Nx1} vector whose @math{i}-th element is ## the class to which row @math{i} of @var{data} is assigned. ## ## @item @var{centers} @tab @tab A @math{KxD} array whose @math{i}-th row is the ## centroid of cluster @math{i}. ## ## @item @var{sumd} @tab @tab A @math{kx1} vector whose @math{i}-th entry is the ## sum of the distances from samples in cluster @math{i} to centroid @math{i}. ## ## @item @var{dist} @tab @tab An @math{Nxk} matrix whose @math{i}@math{j}-th ## element is the distance from sample @math{i} to centroid @math{j}. ## @end multitable ## ## The following parameters may be placed in any order. Each parameter ## must be followed by its value, as in Name-Value pairs. ## ## @multitable @columnfractions 0.15 0.02 0.83 ## @headitem Name @tab @tab Description ## @item @qcode{"Start"} @tab @tab The initialization method for the centroids. ## @end multitable ## ## @multitable @columnfractions 0.04 0.19 0.02 0.75 ## @headitem @tab Value @tab @tab Description ## @item @tab @qcode{"plus"} @tab @tab The k-means++ algorithm. (Default) ## @item @tab @qcode{"sample"} @tab @tab A subset of @math{k} rows from ## @var{data}, sampled uniformly without replacement. ## @item @tab @qcode{"cluster"} @tab @tab Perform a pilot clustering on 10% of ## the rows of @var{data}. ## @item @tab @qcode{"uniform"} @tab @tab Each component of each centroid is ## drawn uniformly from the interval between the maximum and minimum values of ## that component within @var{data}. This performs poorly and is implemented ## only for Matlab compatibility. ## @item @tab @var{numeric matrix} @tab @tab A @math{kxD} matrix of centroid ## starting locations. The rows correspond to seeds. ## @item @tab @var{numeric array} @tab @tab A @math{kxDxr} array of centroid ## starting locations. The third dimension invokes replication of the ## clustering routine. Page @math{r} contains the set of seeds for replicate ## @math{r}. @qcode{kmeans} infers the number of replicates (specified by the ## @qcode{"Replicates"} Name-Value pair argument) from the size of the third ## dimension. ## @end multitable ## ## @multitable @columnfractions 0.15 0.02 0.838 ## @headitem Name @tab @tab Description ## @item @qcode{"Distance"} @tab @tab The distance measure used for partitioning ## and calculating centroids. ## @end multitable ## ## @multitable @columnfractions 0.04 0.19 0.02 0.75 ## @headitem @tab Value @tab @tab Description ## @item @tab @qcode{"sqeuclidean"} @tab @tab The squared Euclidean distance. ## i.e. the sum of the squares of the differences between corresponding ## components. In this case, the centroid is the arithmetic mean of all samples ## in its cluster. This is the only distance for which this algorithm is truly ## "k-means". ## @item @tab @qcode{"cityblock"} @tab @tab The sum metric, or L1 distance, ## i.e. the sum of the absolute differences between corresponding components. ## In this case, the centroid is the median of all samples in its cluster. ## This gives the k-medians algorithm. ## @item @tab @qcode{"cosine"} @tab @tab One minus the cosine of the included ## angle between points (treated as vectors). Each centroid is the mean of the ## points in that cluster, after normalizing those points to unit Euclidean ## length. ## @item @tab @qcode{"correlation"} @tab @tab One minus the sample correlation ## between points (treated as sequences of values). Each centroid is the ## component-wise mean of the points in that cluster, after centering and ## normalizing those points to zero mean and unit standard deviation. ## @item @tab @qcode{"hamming"} @tab @tab The number of components in which the ## sample and the centroid differ. In this case, the centroid is the median of ## all samples in its cluster. Unlike Matlab, Octave allows non-logical ## @var{data}. ## @end multitable ## ## @multitable @columnfractions 0.15 0.02 0.838 ## @headitem Name @tab @tab Description ## @item @qcode{"EmptyAction"} @tab @tab What to do when a centroid is not the ## closest to any data sample. ## @end multitable ## ## @multitable @columnfractions 0.04 0.19 0.02 0.75 ## @headitem @tab Value @tab @tab Description ## @item @tab @qcode{"error"} @tab @tab Throw an error. ## @item @tab @qcode{"singleton"} @tab @tab (Default) Select the row of ## @var{data} that has the highest error and use that as the new centroid. ## @item @tab @qcode{"drop"} @tab @tab Remove the centroid, and continue ## computation with one fewer centroid. The dimensions of the outputs ## @var{centroids} and @var{d} are unchanged, with values for omitted centroids ## replaced by NaN. ## @end multitable ## ## @multitable @columnfractions 0.15 0.02 0.838 ## @headitem Name @tab @tab Description ## @item @qcode{"Display"} @tab @tab Display a text summary. ## @end multitable ## ## @multitable @columnfractions 0.04 0.19 0.02 0.75 ## @headitem @tab Value @tab @tab Description ## @item @tab @qcode{"off"} @tab @tab (Default) Display no summary. ## @item @tab @qcode{"final"} @tab @tab Display a summary for each clustering ## operation. ## @item @tab @qcode{"iter"} @tab @tab Display a summary for each iteration of a ## clustering operation. ## @end multitable ## ## @multitable @columnfractions 0.15 0.02 0.838 ## @headitem Name @tab @tab Value ## @item @qcode{"Replicates"} @tab @tab A positive integer specifying the number ## of independent clusterings to perform. The output values are the values for ## the best clustering, i.e., the one with the smallest value of @var{sumd}. ## If @var{Start} is numeric, then @var{Replicates} defaults to ## (and must equal) the size of the third dimension of @var{Start}. ## Otherwise it defaults to 1. ## @item @qcode{"MaxIter"} @tab @tab The maximum number of iterations to perform ## for each replicate. If the maximum change of any centroid is less than ## 0.001, then the replicate terminates even if @var{MaxIter} iterations have no ## occurred. The default is 100. ## @end multitable ## ## Example: ## ## [~,c] = kmeans (rand(10, 3), 2, "emptyaction", "singleton"); ## ## @seealso{linkage} ## @end deftypefn function [classes, centers, sumd, D] = kmeans (data, k, varargin) [reg, prop] = parseparams (varargin); ## defaults for options emptyaction = "singleton"; start = "plus"; replicates = 1; max_iter = 100; distance = "sqeuclidean"; display = "off"; replicates_set_explicitly = false; ## Remove rows containing NaN / NA, but record which rows are used data_idx = ! any (isnan (data), 2); original_rows = rows (data); data = data(data_idx,:); #used for getting the number of samples n_rows = rows (data); #used for convergence of the centroids err = 1; ## Input checking, validate the matrix if (! isnumeric (data) || ! ismatrix (data) || ! isreal (data)) error ("kmeans: first input argument must be a DxN real data matrix"); elseif (! isnumeric (k)) error ("kmeans: second argument must be numeric"); endif ## Parse options while (length (prop) > 0) if (length (prop) < 2) error ("kmeans: Option '%s' has no argument", prop{1}); endif switch (lower (prop{1})) case "emptyaction" emptyaction = prop{2}; case "start" start = prop{2}; case "maxiter" max_iter = prop{2}; case "distance" distance = prop{2}; case "replicates" replicates = prop{2}; replicates_set_explicitly = true; case "display" display = prop{2}; case {"onlinephase", "options"} warning ("kmeans: Ignoring unimplemented option '%s'", prop{1}); otherwise error ("kmeans: Unknown option %s", prop{1}); endswitch prop = {prop{3:end}}; endwhile ## Process options ## check for the 'emptyaction' property switch (emptyaction) case {"singleton", "error", "drop"} ; otherwise d = [", " disp(emptyaction)] (1:end-1); # strip trailing \n if (length (d) > 20) d = ""; endif error ("kmeans: unsupported empty cluster action parameter%s", d); endswitch ## check for the 'replicates' property if (! isnumeric (replicates) || ! isscalar (replicates) || ! isreal (replicates) || replicates < 1) d = [", " disp(replicates)] (1:end-1); # strip trailing \n if (length (d) > 20) d = ""; endif error ("kmeans: invalid number of replicates%s", d); endif ## check for the 'MaxIter' property if (! isnumeric (max_iter) || ! isscalar (max_iter) || ! isreal (max_iter) || max_iter < 1) d = [", " disp(max_iter)] (1:end-1); # strip trailing \n if (length (d) > 20) d = ""; endif error ("kmeans: invalid MaxIter%s", d); endif ## check for the 'start' property switch (lower (start)) case {"sample", "plus", "cluster"} start = lower (start); case {"uniform"} start = "uniform"; min_data = min (data); range = max (data) - min_data; otherwise if (! isnumeric (start)) d = [", " disp(start)] (1:end-1); # strip trailing \n if (length (d) > 20) d = ""; endif error ("kmeans: invalid start parameter%s", d); endif if (isempty (k)) k = rows (start); elseif (rows (start) != k) error (["kmeans: Number of initializers (%d) " ... "should match number of centroids (%d)"], rows (start), k); endif if (replicates_set_explicitly) if (replicates != size (start, 3)) error (["kmeans: The third dimension of the initializer (%d) " ... "should match the number of replicates (%d)"], ... size (start, 3), replicates); endif else replicates = size (start, 3); endif endswitch ## check for the 'distance' property ## dist returns the distance btwn each row of matrix x and a row vector c switch (lower (distance)) case "sqeuclidean" dist = @(x, c) sumsq (bsxfun (@minus, x, c), 2); centroid = @(x) mean (x, 1); case "cityblock" dist = @(x, c) sum (abs (bsxfun (@minus, x, c)), 2); centroid = @(x) median (x, 1); case "cosine" ## Pre-normalize all data. ## (when Octave implements normr, will use data = normr (data) ) for i = 1:rows (data) data(i,:) = data(i,:) / sqrt (sumsq (data(i,:))); endfor dist = @(x, c) 1 - (x * c') ./ sqrt (sumsq (c)); centroid = @(x) mean (x, 1); ## already normalized case "correlation" ## Pre-normalize all data. data = data - mean (data, 2); ## (when Octave implements normr, will use data = normr (data) ) for i = 1:rows (data) data(i,:) = data(i,:) / sqrt (sumsq (data(i,:))); endfor dist = @(x, c) 1 - (x * (c - mean (c))') ... ./ sqrt (sumsq (c - mean (c))); centroid = @(x) mean (x, 1); ## already normalized case "hamming" dist = @(x, c) sum (bsxfun (@ne, x, c), 2); centroid = @(x) median (x, 1); otherwise error ("kmeans: unsupported distance parameter %s", distance); endswitch ## check for the 'display' property if (! strcmp (display, "off")) display = lower (display); switch (display) case {"off", "final"} ; case "iter" printf ("%6s\t%6s\t%8s\t%12s\n", "iter", "phase", "num", "sum"); otherwise error ("kmeans: invalid display parameter %s", display); endswitch endif ## Done processing options ######################################## ## Now that k has been set (possibly by 'replicates' option), check/use it. if (! isscalar (k)) error ("kmeans: second input argument must be a scalar"); endif ## used to hold the distances from each sample to each class D = zeros (n_rows, k); best = Inf; best_centers = []; for rep = 1:replicates ## keep track of the number of data points that change class old_classes = zeros (rows (data), 1); n_changes = -1; ## check for the 'start' property switch (lower (start)) case "sample" idx = randperm (n_rows, k); centers = data(idx, :); case "plus" # k-means++, by Arthur and Vassilios(?) centers(1,:) = data(randi (n_rows),:); d = inf (n_rows, 1); # Distance to nearest centroid so far for i = 2:k d = min (d, dist (data, centers(i - 1, :))); centers(i,:) = data(find (cumsum (d) > rand * sum (d), 1), :); endfor case "cluster" idx = randperm (n_rows, max (k, ceil (n_rows / 10))); [~, centers] = kmeans (data(idx,:), k, "start", "sample", ... "distance", distance); case "uniform" # vectorised 'min_data + range .* rand' centers = bsxfun (@plus, min_data, bsxfun (@times, range, rand (k, columns (data)))); otherwise centers = start(:,:,rep); endswitch ## Run the algorithm iter = 1; ## Classify once before the loop; to set sumd, and if max_iter == 0 ## Compute distances and classify [D, classes, sumd] = update_dist (data, centers, D, k, dist); while (err > 0.001 && iter++ <= max_iter && n_changes != 0) ## Calculate new centroids replaced_centroids = []; ## Used by "emptyaction = singleton" for i = 1:k ## Get binary vector indicating membership in cluster i membership = (classes == i); ## Check for empty clusters if (! any (membership)) switch emptyaction ## if 'singleton', then find the point that is the ## farthest from any centroid (and not replacing an empty cluster ## from earlier in this pass) and add it to the empty cluster case 'singleton' available = setdiff (1:n_rows, replaced_centroids); [~, idx] = max (min (D(available,:)')); idx = available(idx); replaced_centroids = [replaced_centroids, idx]; classes(idx) = i; membership(idx) = 1; ## if 'drop' then set C and D to NA case 'drop' centers(i,:) = NA; D(i,:) = NA; ## if 'error' then throw the error otherwise error ("kmeans: empty cluster created"); endswitch endif ## end check for empty clusters ## update the centroids if (any (membership)) ## if we didn't "drop" the cluster centers(i, :) = centroid (data(membership, :)); endif endfor ## Compute distances, classes and sums [D, classes, new_sumd] = update_dist (data, centers, D, k, dist); ## calculate the difference in the sum of distances err = sum (sumd - new_sumd); ## update the current sum of distances sumd = new_sumd; ## compute the number of class changes n_changes = sum (old_classes != classes); old_classes = classes; ## display iteration status if (strcmp (display, "iter")) printf ("%6d\t%6d\t%8d\t%12.3f\n", (iter - 1), 1, ... n_changes, sum (sumd)); endif endwhile ## throw a warning if the algorithm did not converge if (iter > max_iter && err > 0.001 && n_changes != 0) warning ("kmeans: failed to converge in %d iterations", max_iter); endif if (sum (sumd) < sum (best) || isinf (best)) best = sumd; best_centers = centers; endif ## display final results if (strcmp (display, "final")) printf ("Replicate %d, %d iterations, total sum of distances = %.3f.\n", ... rep, iter, sum (sumd)); endif endfor centers = best_centers; ## Compute final distances, classes and sums [D, classes, sumd] = update_dist (data, centers, D, k, dist); ## display final results if (strcmp (display, "final") || strcmp (display, "iter")) printf ("Best total sum of distances = %.3f\n", sum (sumd)); endif ## Return with equal size as inputs if (original_rows != rows (data)) final = NA (original_rows,1); final(data_idx) = classes; ## other positions already NaN / NA classes = final; endif endfunction ## Update distances, classes and sums function [D, classes, sumd] = update_dist (data, centers, D, k, dist) for i = 1:k D (:, i) = dist (data, centers(i, :)); endfor [~, classes] = min (D, [], 2); ## calculate the sum of within-class distances sumd = zeros (k, 1); for i = 1:k sumd(i) = sum (D(classes == i,i)); endfor endfunction %!demo %! ## Generate a two-cluster problem %! randn ("seed", 31) # for reproducibility %! C1 = randn (100, 2) + 1; %! randn ("seed", 32) # for reproducibility %! C2 = randn (100, 2) - 1; %! data = [C1; C2]; %! %! ## Perform clustering %! rand ("seed", 1) # for reproducibility %! [idx, centers] = kmeans (data, 2); %! %! ## Plot the result %! figure; %! plot (data (idx==1, 1), data (idx==1, 2), "ro"); %! hold on; %! plot (data (idx==2, 1), data (idx==2, 2), "bs"); %! plot (centers (:, 1), centers (:, 2), "kv", "markersize", 10); %! hold off; %!demo %! ## Cluster data using k-means clustering, then plot the cluster regions %! ## Load Fisher's iris data set and use the petal lengths and widths as %! ## predictors %! %! load fisheriris %! X = meas(:,3:4); %! %! figure; %! plot (X(:,1), X(:,2), "k*", "MarkerSize", 5); %! title ("Fisher's Iris Data"); %! xlabel ("Petal Lengths (cm)"); %! ylabel ("Petal Widths (cm)"); %! %! ## Cluster the data. Specify k = 3 clusters %! rand ("seed", 1) # for reproducibility %! [idx, C] = kmeans (X, 3); %! x1 = min (X(:,1)):0.01:max (X(:,1)); %! x2 = min (X(:,2)):0.01:max (X(:,2)); %! [x1G, x2G] = meshgrid (x1, x2); %! XGrid = [x1G(:), x2G(:)]; %! %! idx2Region = kmeans (XGrid, 3, "MaxIter", 1, "Start", C); %! figure; %! gscatter (XGrid(:,1), XGrid(:,2), idx2Region, ... %! [0, 0.75, 0.75; 0.75, 0, 0.75; 0.75, 0.75, 0], ".."); %! hold on; %! plot (X(:,1), X(:,2), "k*", "MarkerSize", 5); %! title ("Fisher's Iris Data"); %! xlabel ("Petal Lengths (cm)"); %! ylabel ("Petal Widths (cm)"); %! legend ("Region 1", "Region 2", "Region 3", "Data", "Location", "SouthEast"); %! hold off %!demo %! ## Partition Data into Two Clusters %! %! randn ("seed", 1) # for reproducibility %! r1 = randn (100, 2) * 0.75 + ones (100, 2); %! randn ("seed", 2) # for reproducibility %! r2 = randn (100, 2) * 0.5 - ones (100, 2); %! X = [r1; r2]; %! %! figure; %! plot (X(:,1), X(:,2), "."); %! title ("Randomly Generated Data"); %! rand ("seed", 1) # for reproducibility %! [idx, C] = kmeans (X, 2, "Distance", "cityblock", ... %! "Replicates", 5, "Display", "final"); %! figure; %! plot (X(idx==1,1), X(idx==1,2), "r.", "MarkerSize", 12); %! hold on %! plot(X(idx==2,1), X(idx==2,2), "b.", "MarkerSize", 12); %! plot (C(:,1), C(:,2), "kx", "MarkerSize", 15, "LineWidth", 3); %! legend ("Cluster 1", "Cluster 2", "Centroids", "Location", "NorthWest"); %! title ("Cluster Assignments and Centroids"); %! hold off %!demo %! ## Assign New Data to Existing Clusters %! %! ## Generate a training data set using three distributions %! randn ("seed", 5) # for reproducibility %! r1 = randn (100, 2) * 0.75 + ones (100, 2); %! randn ("seed", 7) # for reproducibility %! r2 = randn (100, 2) * 0.5 - ones (100, 2); %! randn ("seed", 9) # for reproducibility %! r3 = randn (100, 2) * 0.75; %! X = [r1; r2; r3]; %! %! ## Partition the training data into three clusters by using kmeans %! %! rand ("seed", 1) # for reproducibility %! [idx, C] = kmeans (X, 3); %! %! ## Plot the clusters and the cluster centroids %! %! figure %! gscatter (X(:,1), X(:,2), idx, "bgm", "***"); %! hold on %! plot (C(:,1), C(:,2), "kx"); %! legend ("Cluster 1", "Cluster 2", "Cluster 3", "Cluster Centroid") %! %! ## Generate a test data set %! randn ("seed", 25) # for reproducibility %! r1 = randn (100, 2) * 0.75 + ones (100, 2); %! randn ("seed", 27) # for reproducibility %! r2 = randn (100, 2) * 0.5 - ones (100, 2); %! randn ("seed", 29) # for reproducibility %! r3 = randn (100, 2) * 0.75; %! Xtest = [r1; r2; r3]; %! %! ## Classify the test data set using the existing clusters %! ## Find the nearest centroid from each test data point by using pdist2 %! %! D = pdist2 (C, Xtest, "euclidean"); %! [group, ~] = find (D == min (D)); %! %! ## Plot the test data and label the test data using idx_test with gscatter %! %! gscatter (Xtest(:,1), Xtest(:,2), group, "bgm", "ooo"); %! legend ("Cluster 1", "Cluster 2", "Cluster 3", "Cluster Centroid", ... %! "Data classified to Cluster 1", "Data classified to Cluster 2", ... %! "Data classified to Cluster 3", "Location", "NorthWest"); %! title ("Assign New Data to Existing Clusters"); ## Test output %!test %! samples = 4; %! dims = 3; %! k = 2; %! [cls, c, d, z] = kmeans (rand (samples,dims), k, "start", rand (k,dims, 5), %! "emptyAction", "singleton"); %! assert (size (cls), [samples, 1]); %! assert (size (c), [k, dims]); %! assert (size (d), [k, 1]); %! assert (size (z), [samples, k]); %!test %! samples = 4; %! dims = 3; %! k = 2; %! [cls, c, d, z] = kmeans (rand (samples,dims), [], "start", rand (k,dims, 5), %! "emptyAction", "singleton"); %! assert (size (cls), [samples, 1]); %! assert (size (c), [k, dims]); %! assert (size (d), [k, 1]); %! assert (size (z), [samples, k]); %!test %! [cls, c] = kmeans ([1 0; 2 0], 2, "start", [8,0;0,8], "emptyaction", "drop"); %! assert (cls, [1; 1]); %! assert (c, [1.5, 0; NA, NA]); %!test %! kmeans (rand (4,3), 2, "start", rand (2,3, 5), "replicates", 5, %! "emptyAction", "singleton"); %!test %! kmeans (rand (3,4), 2, "start", "sample", "emptyAction", "singleton"); %!test %! kmeans (rand (3,4), 2, "start", "plus", "emptyAction", "singleton"); %!test %! kmeans (rand (3,4), 2, "start", "cluster", "emptyAction", "singleton"); %!test %! kmeans (rand (3,4), 2, "start", "uniform", "emptyAction", "singleton"); %!test %! kmeans (rand (4,3), 2, "distance", "sqeuclidean", "emptyAction", "singleton"); %!test %! kmeans (rand (4,3), 2, "distance", "cityblock", "emptyAction", "singleton"); %!test %! kmeans (rand (4,3), 2, "distance", "cosine", "emptyAction", "singleton"); %!test %! kmeans (rand (4,3), 2, "distance", "correlation", "emptyAction", "singleton"); %!test %! kmeans (rand (4,3), 2, "distance", "hamming", "emptyAction", "singleton"); %!test %! kmeans ([1 0; 1.1 0], 2, "start", eye(2), "emptyaction", "singleton"); ## Test input validation %!error kmeans (rand (3,2), 4); %!error kmeans ([1 0; 1.1 0], 2, "start", eye(2), "emptyaction", "panic"); %!error kmeans (rand (4,3), 2, "start", rand (2,3, 5), "replicates", 1); %!error kmeans (rand (4,3), 2, "start", rand (2,2)); %!error kmeans (rand (4,3), 2, "distance", "manhattan"); %!error kmeans (rand (3,4), 2, "start", "normal"); %!error kmeans (rand (4,3), 2, "replicates", i); %!error kmeans (rand (4,3), 2, "replicates", -1); %!error kmeans (rand (4,3), 2, "replicates", []); %!error kmeans (rand (4,3), 2, "replicates", [1 2]); %!error kmeans (rand (4,3), 2, "replicates", "one"); %!error kmeans (rand (4,3), 2, "MAXITER", i); %!error kmeans (rand (4,3), 2, "MaxIter", -1); %!error kmeans (rand (4,3), 2, "maxiter", []); %!error kmeans (rand (4,3), 2, "maxiter", [1 2]); %!error kmeans (rand (4,3), 2, "maxiter", "one"); %!error kmeans ([1 0; 1.1 0], 2, "start", eye(2), "emptyaction", "error"); statistics-release-1.7.3/inst/knnsearch.m000066400000000000000000000657651475240274700205000ustar00rootroot00000000000000## Copyright (C) 2023-2024 Andreas Bertsatos ## Copyright (C) 2023 Mohammed Azmat Khan ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{idx} =} knnsearch (@var{X}, @var{Y}) ## @deftypefnx {statistics} {[@var{idx}, @var{D}] =} knnsearch (@var{X}, @var{Y}) ## @deftypefnx {statistics} {[@dots{}] =} knnsearch (@dots{}, @var{name}, @var{value}) ## ## Find k-nearest neighbors from input data. ## ## @code{@var{idx} = knnsearch (@var{X}, @var{Y})} finds @math{K} nearest ## neighbors in @var{X} for @var{Y}. It returns @var{idx} which contains indices ## of @math{K} nearest neighbors of each row of @var{Y}, If not specified, ## @qcode{@var{K} = 1}. @var{X} must be an @math{NxP} numeric matrix of input ## data, where rows correspond to observations and columns correspond to ## features or variables. @var{Y} is an @math{MxP} numeric matrix with query ## points, which must have the same numbers of column as @var{X}. ## ## @code{[@var{idx}, @var{D}] = knnsearch (@var{X}, @var{Y})} also returns the ## the distances, @var{D}, which correspond to the @math{K} nearest neighbour in ## @var{X} for each @var{Y} ## ## Additional parameters can be specified by @qcode{Name-Value} pair arguments. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"K"} @tab @tab is the number of nearest neighbors to be found ## in the kNN search. It must be a positive integer value and by default it is ## 1. ## ## @item @qcode{"P"} @tab @tab is the Minkowski distance exponent and it must be ## a positive scalar. This argument is only valid when the selected distance ## metric is @qcode{"minkowski"}. By default it is 2. ## ## @item @qcode{"Scale"} @tab @tab is the scale parameter for the standardized ## Euclidean distance and it must be a nonnegative numeric vector of equal ## length to the number of columns in @var{X}. This argument is only valid when ## the selected distance metric is @qcode{"seuclidean"}, in which case each ## coordinate of @var{X} is scaled by the corresponding element of ## @qcode{"scale"}, as is each query point in @var{Y}. By default, the scale ## parameter is the standard deviation of each coordinate in @var{X}. ## ## @item @qcode{"Cov"} @tab @tab is the covariance matrix for computing the ## mahalanobis distance and it must be a positive definite matrix matching the ## the number of columns in @var{X}. This argument is only valid when the ## selected distance metric is @qcode{"mahalanobis"}. ## ## @item @qcode{"BucketSize"} @tab @tab is the maximum number of data points in ## the leaf node of the Kd-tree and it must be a positive integer. This ## argument is only valid when the selected search method is @qcode{"kdtree"}. ## ## @item @qcode{"SortIndices"} @tab @tab is a boolean flag to sort the returned ## indices in ascending order by distance and it is @qcode{true} by default. ## When the selected search method is @qcode{"exhaustive"} or the ## @qcode{"IncludeTies"} flag is true, @code{knnsearch} always sorts the ## returned indices. ## ## @item @qcode{"Distance"} @tab @tab is the distance metric used by ## @code{knnsearch} as specified below: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{"euclidean"} @tab Euclidean distance. ## @item @tab @qcode{"seuclidean"} @tab standardized Euclidean distance. Each ## coordinate difference between the rows in @var{X} and the query matrix ## @var{Y} is scaled by dividing by the corresponding element of the standard ## deviation computed from @var{X}. To specify a different scaling, use the ## @qcode{"Scale"} name-value argument. ## @item @tab @qcode{"cityblock"} @tab City block distance. ## @item @tab @qcode{"chebychev"} @tab Chebychev distance (maximum coordinate ## difference). ## @item @tab @qcode{"minkowski"} @tab Minkowski distance. The default exponent ## is 2. To specify a different exponent, use the @qcode{"P"} name-value ## argument. ## @item @tab @qcode{"mahalanobis"} @tab Mahalanobis distance, computed using a ## positive definite covariance matrix. To change the value of the covariance ## matrix, use the @qcode{"Cov"} name-value argument. ## @item @tab @qcode{"cosine"} @tab Cosine distance. ## @item @tab @qcode{"correlation"} @tab One minus the sample linear correlation ## between observations (treated as sequences of values). ## @item @tab @qcode{"spearman"} @tab One minus the sample Spearman's rank ## correlation between observations (treated as sequences of values). ## @item @tab @qcode{"hamming"} @tab Hamming distance, which is the percentage ## of coordinates that differ. ## @item @tab @qcode{"jaccard"} @tab One minus the Jaccard coefficient, which is ## the percentage of nonzero coordinates that differ. ## @item @tab @var{@@distfun} @tab Custom distance function handle. A distance ## function of the form @code{function @var{D2} = distfun (@var{XI}, @var{YI})}, ## where @var{XI} is a @math{1xP} vector containing a single observation in ## @math{P}-dimensional space, @var{YI} is an @math{NxP} matrix containing an ## arbitrary number of observations in the same @math{P}-dimensional space, and ## @var{D2} is an @math{NxP} vector of distances, where @qcode{(@var{D2}k)} is ## the distance between observations @var{XI} and @qcode{(@var{YI}k,:)}. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @item @qcode{"NSMethod"} @tab @tab is the nearest neighbor search method used ## by @code{knnsearch} as specified below. ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{"kdtree"} @tab Creates and uses a Kd-tree to find nearest ## neighbors. @qcode{"kdtree"} is the default value when the number of columns ## in @var{X} is less than or equal to 10, @var{X} is not sparse, and the ## distance metric is @qcode{"euclidean"}, @qcode{"cityblock"}, ## @qcode{"manhattan"}, @qcode{"chebychev"}, or @qcode{"minkowski"}. Otherwise, ## the default value is @qcode{"exhaustive"}. This argument is only valid when ## the distance metric is one of the four aforementioned metrics. ## @item @tab @qcode{"exhaustive"} @tab Uses the exhaustive search algorithm by ## computing the distance values from all the points in @var{X} to each point in ## @var{Y}. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @item @qcode{"IncludeTies"} @tab @tab is a boolean flag to indicate if the ## returned values should contain the indices that have same distance as the ## @math{K^th} neighbor. When @qcode{false}, @code{knnsearch} chooses the ## observation with the smallest index among the observations that have the same ## distance from a query point. When @qcode{true}, @code{knnsearch} includes ## all nearest neighbors whose distances are equal to the @math{K^th} smallest ## distance in the output arguments. To specify @math{K}, use the @qcode{"K"} ## name-value pair argument. ## @end multitable ## ## @seealso{rangesearch, pdist2, fitcknn} ## @end deftypefn function [idx, dist] = knnsearch (X, Y, varargin) ## Check input data if (nargin < 2) error ("knnsearch: too few input arguments."); endif if (size (X, 2) != size (Y, 2)) error ("knnsearch: number of columns in X and Y must match."); endif ## Add default values K = 1; # Number of nearest neighbors P = 2; # Exponent for Minkowski distance S = []; # Scale for the standardized Euclidean distance C = []; # Covariance matrix for Mahalanobis distance BS = 50; # Maximum number of points per leaf node for Kd-tree SI = true; # Sort returned indices according to distance Distance = "euclidean"; # Distance metric to be used NSMethod = []; # Nearest neighbor search method InclTies = false; # Include ties for distance with kth neighbor DistParameter = []; # Distance parameter for pdist2 ## Parse additional parameters in Name/Value pairs PSC = 0; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "k" K = varargin{2}; case "p" P = varargin{2}; PSC += 1; case "scale" S = varargin{2}; PSC += 1; case "cov" C = varargin{2}; PSC += 1; case "bucketsize" BS = varargin{2}; case "sortindices" SI = varargin{2}; case "distance" Distance = varargin{2}; case "nsmethod" NSMethod = varargin{2}; case "includeties" InclTies = varargin{2}; otherwise error ("knnsearch: invalid NAME in optional pairs of arguments."); endswitch varargin(1:2) = []; endwhile ## Check input parameters if (PSC > 1) error ("knnsearch: only a single distance parameter can be defined."); endif if (! isscalar (K) || ! isnumeric (K) || K < 1 || K != round (K)) error ("knnsearch: invalid value of K."); endif if (! isscalar (P) || ! isnumeric (P) || P <= 0) error ("knnsearch: invalid value of Minkowski Exponent."); endif if (! isempty (S)) if (any (S) < 0 || numel (S) != columns (X) || ! strcmpi (Distance, "seuclidean")) error ("knnsearch: invalid value in Scale or the size of Scale."); endif endif if (! isempty (C)) if (! strcmp (Distance, "mahalanobis") || ! ismatrix (C) || ! isnumeric (C)) error (strcat (["knnsearch: invalid value in Cov, Cov can only"], ... [" be given for mahalanobis distance."])); endif endif if (! isscalar (BS) || BS < 0) error ("knnsearch: invalid value of bucketsize."); endif ## Select the appropriate distance parameter if (strcmpi (Distance, "minkowski")) DistParameter = P; elseif (strcmpi (Distance, "seuclidean")) DistParameter = S; elseif (strcmpi (Distance, "mahalanobis")) DistParameter = C; endif ## Check NSMethod and set kdtree as default if the conditions match if (isempty (NSMethod)) ## Set default method 'kdtree' if conditions are satistfied; if (! issparse (X) && (columns (X) <= 10) && ... (strcmpi (Distance, "euclidean") || strcmpi (Distance, "cityblock") || strcmpi (Distance, "minkowski") || strcmpi (Distance, "chebychev"))) NSMethod = "kdtree"; else NSMethod = "exhaustive"; endif else ## Check if kdtree can be used if (strcmpi (NSMethod,"kdtree") && ! ( strcmpi (Distance, "euclidean") || strcmpi (Distance, "cityblock") || strcmpi (Distance, "minkowski") || strcmpi (Distance, "chebychev"))) error (strcat (["knnsearch: 'kdtree' cannot be used with"], ... [" the given distance metric."])); endif endif ## Check for NSMethod if (strcmpi (NSMethod, "kdtree")) ## Build kdtree and search the query point ret = buildkdtree (X, BS); ## Check for ties and sortindices if (! InclTies) ## Only return k neighbors ## No need for returning cell dist = []; idx = []; for i = 1:rows (Y) NN = findkdtree (ret, Y(i, :), K, Distance, DistParameter); D = pdist2 (X(NN,:), Y(i,:), Distance, DistParameter); sorted_D = sortrows ([NN, D], [2, 1]); dist = [dist; sorted_D(1:K, 2)']; idx = [idx; sorted_D(1:K, 1)']; endfor if (SI) ## Rows are already sorted by distance dist = (dist); idx = (idx); else dist = (dist); idx = (idx); endif else ## Return all neighbors as cell dist = cell (rows (Y), 1); idx = cell (rows (Y), 1); for i = 1:rows (Y) NN = findkdtree (ret, Y(i, :), K, Distance, DistParameter); D = pdist2 (X(NN,:), Y(i,:), Distance, DistParameter); sorted_D = sortrows ([NN, D], [2, 1]); kth_dist = sorted_D (K, 2); tied_idx = (sorted_D (:, 2) <= kth_dist); dist {i} = sorted_D (tied_idx, 2)'; idx {i} = sorted_D (tied_idx, 1)'; endfor endif else ## Calculate all distances if (K == 1) D = pdist2 (X, Y, Distance, DistParameter); D = reshape (D', size (Y, 1), size (X, 1)); [dist, idx] = min (D, [], 2); else # always sort indices in this case if (InclTies) dist = cell (rows (Y), 1); idx = cell (rows (Y), 1); for i = 1:rows (Y) D = pdist2 (X, Y(i,:), Distance, DistParameter); [dt, id] = sort (D); kth_dist = dt (K); tied_idx = (dt <= kth_dist); dist {i} = dt(tied_idx, :)'; idx {i} = id(tied_idx, :)'; endfor else ## No ties included D = pdist2 (X, Y, Distance, DistParameter); D = reshape (D', size (Y, 1), size (X, 1)); [dist, idx] = sort (D, 2); dist = dist(:,1:K); idx = idx(:,1:K); endif endif endif endfunction ## buildkdtree function ret = buildkdtree_recur (X, r, d, BS) count = length (r); dimen = size (X, 2); if (count == 1) ret = struct ("point", r(1), "dimen", d); else mid = ceil (count / 2); ret = struct ("point", r(mid), "dimen", d); d = mod (d, dimen) + 1; ## Build left sub tree if (mid > 1) left = r(1:mid-1); left_points = X(left,d); [val, left_idx] = sort (left_points); leftr = left(left_idx); ret.left = buildkdtree_recur (X, leftr, d); endif ## Build right sub tree if (count > mid) right = r(mid+1:count); right_points = X(right,d); [val, right_idx] = sort (right_points); rightr = right(right_idx); ret.right = buildkdtree_recur (X, rightr, d); endif endif endfunction ## wrapper function for buildkdtree_recur function ret = buildkdtree (X, BS) [val, r] = sort (X(:,1)); ret = struct ("data", X, "root", buildkdtree_recur (X, r, 1, BS)); endfunction function farthest = kdtree_cand_farthest (X, p, cand, dist, distparam) D = pdist2 (X, p, dist, distparam); [val, index] = max (D'(cand)); farthest = cand (index); endfunction ## function to insert into NN list function inserted = kdtree_cand_insert (X, p, cand, k, point, dist, distparam) if (length (cand) < k) inserted = [cand; point]; else farthest = kdtree_cand_farthest (X, p, cand, dist, distparam); if (pdist2 (cand(find(cand == farthest),:), point, dist, distparam)) inserted = [cand; point]; else farthest = kdtree_cand_farthest (X, p, cand, dist, distparam); cand (find (cand == farthest)) = point; inserted = cand; endif endif endfunction ## function to search in a kd tree function nn = findkdtree_recur (X, node, p, nn, ... k, dist, distparam) point = node.point; d = node.dimen; if (X(point,d) > p(d)) ## Search in left sub tree if (isfield (node, "left")) nn = findkdtree_recur (X, node.left, p, nn, k, dist, distparam); endif ## Add current point if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); if (length(nn) < k || pdist2 (X(point,:), p, dist, distparam) <= pdist2 (X(farthest,:), p, dist, distparam)) nn = kdtree_cand_insert (X, p, nn, k, point, dist, distparam); endif ## Search in right sub tree if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); radius = pdist2 (X(farthest,:), p, dist, distparam); if (isfield (node, "right") && (length(nn) < k || p(d) + radius > X(point,d))) nn = findkdtree_recur (X, node.right, p, nn, ... k, dist, distparam); endif else ## Search in right sub tree if (isfield (node, "right")) nn = findkdtree_recur (X, node.right, p, nn, k, dist, distparam); endif ## Add current point if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); if (length (nn) < k || pdist2 (X(point,:), p, dist, distparam) <= pdist2 (X(farthest,:), p, dist, distparam)) nn = kdtree_cand_insert (X, p, nn, k, point, dist, distparam); endif ## Search in left sub tree if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); radius = pdist2 (X(farthest,:), p, dist, distparam); if (isfield (node, "left") && (length (nn) < k || p(d) - radius <= X(point,d))) nn = findkdtree_recur (X, node.left, p, nn, k, dist, distparam); endif endif endfunction ## wrapper function for findkdtree_recur function nn = findkdtree (tree, p, k, dist, distparam) X = tree.data; root = tree.root; nn = findkdtree_recur (X, root, p, [], k, dist, distparam); endfunction %!demo %! ## find 10 nearest neighbour of a point using different distance metrics %! ## and compare the results by plotting %! load fisheriris %! X = meas(:,3:4); %! Y = species; %! point = [5, 1.45]; %! %! ## calculate 10 nearest-neighbours by minkowski distance %! [id, d] = knnsearch (X, point, "K", 10); %! %! ## calculate 10 nearest-neighbours by minkowski distance %! [idm, dm] = knnsearch (X, point, "K", 10, "distance", "minkowski", "p", 5); %! %! ## calculate 10 nearest-neighbours by chebychev distance %! [idc, dc] = knnsearch (X, point, "K", 10, "distance", "chebychev"); %! %! ## plotting the results %! gscatter (X(:,1), X(:,2), species, [.75 .75 0; 0 .75 .75; .75 0 .75], ".", 20); %! title ("Fisher's Iris Data - Nearest Neighbors with different types of distance metrics"); %! xlabel("Petal length (cm)"); %! ylabel("Petal width (cm)"); %! %! line (point(1), point(2), "marker", "X", "color", "k", ... %! "linewidth", 2, "displayname", "query point") %! line (X(id,1), X(id,2), "color", [0.5 0.5 0.5], "marker", "o", ... %! "linestyle", "none", "markersize", 10, "displayname", "eulcidean") %! line (X(idm,1), X(idm,2), "color", [0.5 0.5 0.5], "marker", "d", ... %! "linestyle", "none", "markersize", 10, "displayname", "Minkowski") %! line (X(idc,1), X(idc,2), "color", [0.5 0.5 0.5], "marker", "p", ... %! "linestyle", "none", "markersize", 10, "displayname", "chebychev") %! xlim ([4.5 5.5]); %! ylim ([1 2]); %! axis square; %!demo %! ## knnsearch on iris dataset using kdtree method %! load fisheriris %! X = meas(:,3:4); %! gscatter (X(:,1), X(:,2), species, [.75 .75 0; 0 .75 .75; .75 0 .75], ".", 20); %! title ("Fisher's iris dataset : Nearest Neighbors with kdtree search"); %! %! ## new point to be predicted %! point = [5 1.45]; %! %! line (point(1), point(2), "marker", "X", "color", "k", ... %! "linewidth", 2, "displayname", "query point") %! %! ## knnsearch using kdtree method %! [idx, d] = knnsearch (X, point, "K", 10, "NSMethod", "kdtree"); %! %! ## plotting predicted neighbours %! line (X(idx,1), X(idx,2), "color", [0.5 0.5 0.5], "marker", "o", ... %! "linestyle", "none", "markersize", 10, ... %! "displayname", "nearest neighbour") %! xlim ([4 6]) %! ylim ([1 3]) %! axis square %! ## details of predicted labels %! tabulate (species(idx)) %! %! ctr = point - d(end); %! diameter = 2 * d(end); %! ## Draw a circle around the 10 nearest neighbors. %! h = rectangle ("position", [ctr, diameter, diameter], "curvature", [1 1]); %! %! ## here only 8 neighbours are plotted instead of 10 since the dataset %! ## contains duplicate values ## Test output %!shared X, Y %! X = [1, 2, 3, 4; 2, 3, 4, 5; 3, 4, 5, 6]; %! Y = [1, 2, 2, 3; 2, 3, 3, 4]; %!test %! [idx, D] = knnsearch (X, Y, "Distance", "euclidean"); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * sqrt (2)); %!test %! eucldist = @(v,m) sqrt(sumsq(repmat(v,rows(m),1)-m,2)); %! [idx, D] = knnsearch (X, Y, "Distance", eucldist); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * sqrt (2)); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "euclidean", "includeties", true); %! assert (iscell (idx), true); %! assert (iscell (D), true) %! assert (idx {1}, [1]); %! assert (idx {2}, [1, 2]); %! assert (D{1}, ones (1, 1) * sqrt (2)); %! assert (D{2}, ones (1, 2) * sqrt (2)); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "euclidean", "k", 2); %! assert (idx, [1, 2; 1, 2]); %! assert (D, [sqrt(2), 3.162277660168380; sqrt(2), sqrt(2)], 1e-14); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "seuclidean"); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * sqrt (2)); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "seuclidean", "k", 2); %! assert (idx, [1, 2; 1, 2]); %! assert (D, [sqrt(2), 3.162277660168380; sqrt(2), sqrt(2)], 1e-14); %!test %! xx = [1, 2; 1, 3; 2, 4; 3, 6]; %! yy = [2, 4; 2, 6]; %! [idx, D] = knnsearch (xx, yy, "Distance", "mahalanobis"); %! assert (idx, [3; 2]); %! assert (D, [0; 3.162277660168377], 1e-14); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "minkowski"); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * sqrt (2)); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "minkowski", "p", 3); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * 1.259921049894873, 1e-14); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "cityblock"); %! assert (idx, [1; 1]); %! assert (D, [2; 2]); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "chebychev"); %! assert (idx, [1; 1]); %! assert (D, [1; 1]); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "cosine"); %! assert (idx, [2; 3]); %! assert (D, [0.005674536395645; 0.002911214328620], 1e-14); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "correlation"); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * 0.051316701949486, 1e-14); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "spearman"); %! assert (idx, [1; 1]); %! assert (D, ones (2, 1) * 0.051316701949486, 1e-14); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "hamming"); %! assert (idx, [1; 1]); %! assert (D, [0.5; 0.5]); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "jaccard"); %! assert (idx, [1; 1]); %! assert (D, [0.5; 0.5]); %!test %! [idx, D] = knnsearch (X, Y, "Distance", "jaccard", "k", 2); %! assert (idx, [1, 2; 1, 2]); %! assert (D, [0.5, 1; 0.5, 0.5]); %!test %! a = [1, 5; 1, 2; 2, 2; 1.5, 1.5; 5, 1; 2 -1.34; 1, -3; 4, -4; -3, 1; 8, 9]; %! b = [1, 1]; %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "kdtree", "includeties", true); %! assert (iscell (idx), true); %! assert (iscell (D), true) %! assert (cell2mat (idx), [4, 2, 3, 6, 1, 5, 7, 9]); %! assert (cell2mat (D), [0.7071, 1.0000, 1.4142, 2.5447, 4.0000, 4.0000, 4.0000, 4.0000],1e-4); %!test %! a = [1, 5; 1, 2; 2, 2; 1.5, 1.5; 5, 1; 2 -1.34; 1, -3; 4, -4; -3, 1; 8, 9]; %! b = [1, 1]; %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "exhaustive", "includeties", true); %! assert (iscell (idx), true); %! assert (iscell (D), true) %! assert (cell2mat (idx), [4, 2, 3, 6, 1, 5, 7, 9]); %! assert (cell2mat (D), [0.7071, 1.0000, 1.4142, 2.5447, 4.0000, 4.0000, 4.0000, 4.0000],1e-4); %!test %! a = [1, 5; 1, 2; 2, 2; 1.5, 1.5; 5, 1; 2 -1.34; 1, -3; 4, -4; -3, 1; 8, 9]; %! b = [1, 1]; %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "kdtree", "includeties", false); %! assert (iscell (idx), false); %! assert (iscell (D), false) %! assert (idx, [4, 2, 3, 6, 1]); %! assert (D, [0.7071, 1.0000, 1.4142, 2.5447, 4.0000],1e-4); %!test %! a = [1, 5; 1, 2; 2, 2; 1.5, 1.5; 5, 1; 2 -1.34; 1, -3; 4, -4; -3, 1; 8, 9]; %! b = [1, 1]; %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "exhaustive", "includeties", false); %! assert (iscell (idx), false); %! assert (iscell (D), false) %! assert (idx, [4, 2, 3, 6, 1]); %! assert (D, [0.7071, 1.0000, 1.4142, 2.5447, 4.0000],1e-4); %!test %! load fisheriris %! a = meas; %! b = min(meas); %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "kdtree"); %! assert (idx, [42, 9, 14, 39, 13]); %! assert (D, [0.5099, 0.9950, 1.0050, 1.0536, 1.1874],1e-4); %!test %! load fisheriris %! a = meas; %! b = mean(meas); %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "kdtree"); %! assert (idx, [65, 83, 89, 72, 100]); %! assert (D, [0.3451, 0.3869, 0.4354, 0.4481, 0.4625],1e-4); %!test %! load fisheriris %! a = meas; %! b = max(meas); %! [idx, D] = knnsearch (a, b, "K", 5, "NSMethod", "kdtree"); %! assert (idx, [118, 132, 110, 106, 136]); %! assert (D, [0.7280, 0.9274, 1.3304, 1.5166, 1.6371],1e-4); %! %!test %! load fisheriris %! a = meas; %! b = max(meas); %! [idx, D] = knnsearch (a, b, "K", 5, "includeties", true); %! assert ( iscell (idx), true); %! assert ( iscell (D), true); %! assert (cell2mat (idx), [118, 132, 110, 106, 136]); %! assert (cell2mat (D), [0.7280, 0.9274, 1.3304, 1.5166, 1.6371],1e-4); ## Test input validation %!error knnsearch (1) %!error ... %! knnsearch (ones (4, 5), ones (4)) %!error ... %! knnsearch (ones (4, 2), ones (3, 2), "Distance", "euclidean", "some", "some") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "scale", ones (1, 5), "P", 3) %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "K", 0) %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "P",-2) %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "scale", ones(4,5), "distance", "euclidean") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "cov", ["some" "some"]) %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "cov", ones(4,5), "distance", "euclidean") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "bucketsize", -1) %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "cosine") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "mahalanobis") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "correlation") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "seuclidean") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "spearman") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "hamming") %!error ... %! knnsearch (ones (4, 5), ones (1, 5), "NSmethod", "kdtree", "distance", "jaccard") statistics-release-1.7.3/inst/kruskalwallis.m000066400000000000000000000244751475240274700214050ustar00rootroot00000000000000## Copyright (C) 2021 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} kruskalwallis (@var{x}) ## @deftypefnx {statistics} {@var{p} =} kruskalwallis (@var{x}, @var{group}) ## @deftypefnx {statistics} {@var{p} =} kruskalwallis (@var{x}, @var{group}, @var{displayopt}) ## @deftypefnx {statistics} {[@var{p}, @var{tbl}] =} kruskalwallis (@var{x}, @dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{tbl}, @var{stats}] =} kruskalwallis (@var{x}, @dots{}) ## ## Perform a Kruskal-Wallis test, the non-parametric alternative of a one-way ## analysis of variance (ANOVA), for comparing the means of two or more groups ## of data under the null hypothesis that the groups are drawn from the same ## population, i.e. the group means are equal. ## ## kruskalwallis can take up to three input arguments: ## ## @itemize ## @item ## @var{x} contains the data and it can either be a vector or matrix. ## If @var{x} is a matrix, then each column is treated as a separate group. ## If @var{x} is a vector, then the @var{group} argument is mandatory. ## @item ## @var{group} contains the names for each group. If @var{x} is a matrix, then ## @var{group} can either be a cell array of strings of a character array, with ## one row per column of @var{x}. If you want to omit this argument, enter an ## empty array ([]). If @var{x} is a vector, then @var{group} must be a vector ## of the same lenth, or a string array or cell array of strings with one row ## for each element of @var{x}. @var{x} values corresponding to the same value ## of @var{group} are placed in the same group. ## @item ## @var{displayopt} is an optional parameter for displaying the groups contained ## in the data in a boxplot. If omitted, it is 'on' by default. If group names ## are defined in @var{group}, these are used to identify the groups in the ## boxplot. Use 'off' to omit displaying this figure. ## @end itemize ## ## kruskalwallis can return up to three output arguments: ## ## @itemize ## @item ## @var{p} is the p-value of the null hypothesis that all group means are equal. ## @item ## @var{tbl} is a cell array containing the results in a standard ANOVA table. ## @item ## @var{stats} is a structure containing statistics useful for performing ## a multiple comparison of means with the MULTCOMPARE function. ## @end itemize ## ## If kruskalwallis is called without any output arguments, then it prints the ## results in a one-way ANOVA table to the standard output. It is also printed ## when @var{displayopt} is 'on'. ## ## Examples: ## ## @example ## x = meshgrid (1:6); ## x = x + normrnd (0, 1, 6, 6); ## [p, atab] = kruskalwallis(x); ## @end example ## ## ## @example ## x = ones (50, 4) .* [-2, 0, 1, 5]; ## x = x + normrnd (0, 2, 50, 4); ## group = @{"A", "B", "C", "D"@}; ## kruskalwallis (x, group); ## @end example ## ## @end deftypefn function [p, tbl, stats] = kruskalwallis (x, group, displayopt) ## check for valid number of input arguments narginchk (1, 3); ## add defaults if (nargin < 2) group = []; endif if (nargin < 3) displayopt = 'on'; endif plotdata = ~(strcmp (displayopt, 'off')); ## Convert group to cell array from character array, make it a column if (! isempty (group) && ischar (group)) group = cellstr(group); endif if (size (group, 1) == 1) group = group'; endif ## If X is a matrix, convert it to column vector and create a ## corresponging column vector for groups if (length (x) < prod (size (x))) [n, m] = size (x); x = x(:); gi = reshape (repmat ((1:m), n, 1), n*m, 1); if (length (group) == 0) ## no group names are provided group = gi; elseif (size (group, 1) == m) ## group names exist and match columns group = group(gi,:); else error("X columns and GROUP length do not match."); endif endif ## Identify NaN values (if any) and remove them from X along with ## their corresponding values from group vector nonan = ~isnan (x); x = x(nonan); group = group(nonan, :); ## Convert group to indices and separate names [group_id, group_names] = grp2idx (group); group_id = group_id(:); named = 1; ## Rank data for non-parametric analysis [xr, tieadj] = tieranks (x); ## Get group size and mean for each group groups = size (group_names, 1); xs = zeros (1, groups); xm = xs; for j = 1:groups group_size = find (group_id == j); xs(j) = length (group_size); xm(j) = mean (xr(group_size)); endfor ## Calculate statistics lx = length (xr); ## Number of samples in groups gm = mean (xr); ## Grand mean of groups dfm = length (xm) - 1; ## degrees of freedom for model dfe = lx - dfm - 1; ## degrees of freedom for error SSM = xs .* (xm - gm) * (xm - gm)'; ## Sum of Squares for Model SST = (xr(:) - gm)' * (xr(:) - gm); ## Sum of Squares Total SSE = SST - SSM; ## Sum of Squares Error if (dfm > 0) MSM = SSM / dfm; ## Mean Square for Model else MSM = NaN; endif if (dfe > 0) MSE = SSE / dfe; ## Mean Squared Error else MSE = NaN; endif ## Calculate Chi-sq statistic ChiSq = (12 * SSM) / (lx * (lx + 1)); if (tieadj > 0) ChiSq = ChiSq / (1 - 2 * tieadj / (lx ^ 3 - lx)); end p = 1 - chi2cdf (ChiSq, dfm); ## Create results table (if requested) if (nargout > 1) tbl = {"Source", "SS", "df", "MS", "Chi-sq", "Prob>Chi-sq"; ... "Groups", SSM, dfm, MSM, ChiSq, p; ... "Error", SSE, dfe, MSE, "", ""; ... "Total", SST, dfm + dfe, "", "", ""}; endif ## Create stats structure (if requested) for MULTCOMPARE if (nargout > 2) if (length (group_names) > 0) stats.gnames = group_names; else stats.gnames = strjust (num2str ((1:length (xm))'), 'left'); end stats.n = xs; stats.source = 'kruskalwallis'; stats.meanranks = xm; stats.sumt = 2 * tieadj; endif ## Print results table on screen if no output argument was requested if (nargout == 0 || plotdata) printf(" Kruskal-Wallis ANOVA Table\n"); printf("Source SS df MS Chi-sq Prob>Chi-sq\n"); printf("---------------------------------------------------------\n"); printf("Columns %10.2f %5.0f %10.2f %8.2f %11.5e\n", ... SSM, dfm, MSM, ChiSq, p); printf("Error %10.2f %5.0f %10.2f\n", SSE, dfe, MSE); printf("Total %10.2f %5.0f\n", SST, dfm + dfe); endif ## Plot data using BOXPLOT (unless opted out) if (plotdata) boxplot (x, group_id, 'Notch', "on", 'Labels', group_names); endif endfunction ## local function for computing tied ranks on column vectors function [r, tieadj] = tieranks (x) ## Sort data [value, x_idx] = sort (x); epsx = zeros (size (x)); epsx = epsx(x_idx); x_l = numel (x); ## Count ranks from start (min value) ranks = [1:x_l]'; ## Initialize tie adjustments tieadj = 0; ## Adjust for ties. ties = value(1:x_l-1) + epsx(1:x_l-1) >= value(2:x_l) - epsx(2:x_l); t_idx = find (ties); t_idx(end+1) = 0; maxTies = numel (t_idx); ## Calculate tie adjustments tiecount = 1; while (tiecount < maxTies) tiestart = t_idx(tiecount); ntied = 2; while (t_idx(tiecount+1) == t_idx(tiecount) + 1) tiecount = tiecount + 1; ntied = ntied + 1; endwhile ## Check for tieflag tieadj = tieadj + ntied * (ntied - 1) * (ntied + 1) / 2; ## Average tied ranks ranks(tiestart:tiestart + ntied - 1) = ... sum (ranks(tiestart:tiestart + ntied - 1)) / ntied; tiecount = tiecount + 1; endwhile ## Remap data to original dimensions r(x_idx) = ranks; endfunction %!demo %! x = meshgrid (1:6); %! x = x + normrnd (0, 1, 6, 6); %! kruskalwallis (x, [], 'off'); %!demo %! x = meshgrid (1:6); %! x = x + normrnd (0, 1, 6, 6); %! [p, atab] = kruskalwallis(x); %!demo %! x = ones (30, 4) .* [-2, 0, 1, 5]; %! x = x + normrnd (0, 2, 30, 4); %! group = {"A", "B", "C", "D"}; %! kruskalwallis (x, group); ## testing results against SPSS and R on the GEAR.DAT data file available from ## https://www.itl.nist.gov/div898/handbook/eda/section3/eda354.htm %!test %! data = [1.006, 0.996, 0.998, 1.000, 0.992, 0.993, 1.002, 0.999, 0.994, 1.000, ... %! 0.998, 1.006, 1.000, 1.002, 0.997, 0.998, 0.996, 1.000, 1.006, 0.988, ... %! 0.991, 0.987, 0.997, 0.999, 0.995, 0.994, 1.000, 0.999, 0.996, 0.996, ... %! 1.005, 1.002, 0.994, 1.000, 0.995, 0.994, 0.998, 0.996, 1.002, 0.996, ... %! 0.998, 0.998, 0.982, 0.990, 1.002, 0.984, 0.996, 0.993, 0.980, 0.996, ... %! 1.009, 1.013, 1.009, 0.997, 0.988, 1.002, 0.995, 0.998, 0.981, 0.996, ... %! 0.990, 1.004, 0.996, 1.001, 0.998, 1.000, 1.018, 1.010, 0.996, 1.002, ... %! 0.998, 1.000, 1.006, 1.000, 1.002, 0.996, 0.998, 0.996, 1.002, 1.006, ... %! 1.002, 0.998, 0.996, 0.995, 0.996, 1.004, 1.004, 0.998, 0.999, 0.991, ... %! 0.991, 0.995, 0.984, 0.994, 0.997, 0.997, 0.991, 0.998, 1.004, 0.997]; %! group = [1:10] .* ones (10,10); %! group = group(:); %! [p, tbl] = kruskalwallis (data, group, "off"); %! assert (p, 0.048229, 1e-6); %! assert (tbl{2,5}, 17.03124, 1e-5); %! assert (tbl{2,3}, 9, 0); %! assert (tbl{4,2}, 82655.5, 1e-16); %! data = reshape (data, 10, 10); %! [p, tbl, stats] = kruskalwallis (data, [], "off"); %! assert (p, 0.048229, 1e-6); %! assert (tbl{2,5}, 17.03124, 1e-5); %! assert (tbl{2,3}, 9, 0); %! assert (tbl{4,2}, 82655.5, 1e-16); %! means = [51.85, 60.45, 37.6, 51.1, 29.5, 54.25, 64.55, 66.7, 53.65, 35.35]; %! N = 10 * ones (1, 10); %! assert (stats.meanranks, means, 1e-6); %! assert (length (stats.gnames), 10, 0); %! assert (stats.n, N, 0); statistics-release-1.7.3/inst/kstest.m000066400000000000000000000422151475240274700200220ustar00rootroot00000000000000## Copyright (C) 2022-2025 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} kstest (@var{x}) ## @deftypefnx {statistics} {@var{h} =} kstest (@var{x}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{h}, @var{p}] =} kstest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{p}, @var{ksstat}, @var{cv}] =} kstest (@dots{}) ## ## Single sample Kolmogorov-Smirnov (K-S) goodness-of-fit hypothesis test. ## ## @code{@var{h} = kstest (@var{x})} performs a Kolmogorov-Smirnov (K-S) test to ## determine if a random sample @var{x} could have come from a standard normal ## distribution. @var{h} indicates the results of the null hypothesis test. ## ## @itemize ## @item @var{h} = 0 => Do not reject the null hypothesis at the 5% significance ## @item @var{h} = 1 => Reject the null hypothesis at the 5% significance ## @end itemize ## ## @var{x} is a vector representing a random sample from some unknown ## distribution with a cumulative distribution function F(X). Missing values ## declared as NaNs in @var{x} are ignored. ## ## @code{@var{h} = kstest (@var{x}, @var{name}, @var{value})} returns ## a test decision for a single-sample K-S test with additional options ## specified by one or more @var{Name}-@var{Value} pair arguments as shown ## below. ## ## @multitable @columnfractions 0.15 0.05 0.8 ## @headitem Name @tab @tab Value ## @item @qcode{"alpha"} @tab @tab A numeric scalar between 0 and 1 specifying th ## the significance level. Default is 0.05 for 5% significance. ## ## @item @qcode{"CDF"} @tab @tab The hypothesized CDF under the null hypothesis. ## It can be specified as a function handle of an existing cdf function, a ## character vector defining a probability distribution with default parameters, ## a probability distribution object, or a two-column matrix. If not provided, ## the default is the standard normal, @math{N(0,1)}. The one-sample ## Kolmogorov-Smirnov test is only valid for continuous cumulative distribution ## functions, and requires the CDF to be predetermined. The result is not ## accurate if CDF is estimated from the data. ## ## @item @qcode{"tail"} @tab @tab A string indicating the type of test: ## @end multitable ## @multitable @columnfractions 0.2 0.15 0.05 0.5 ## @item @tab @qcode{"unequal"} @tab @tab "F(X) not equal to CDF(X)" (two-sided) ## (Default) ## ## @item @tab @qcode{"larger"} @tab @tab "F(X) > CDF(X)" (one-sided) ## ## @item @tab @qcode{"smaller"} @tab @tab "F(X) < CDF(X)" (one-sided) ## @end multitable ## ## Let S(X) be the empirical c.d.f. estimated from the sample vector @var{x}, ## F(X) be the corresponding true (but unknown) population c.d.f., and CDF be ## the known input c.d.f. specified under the null hypothesis. ## For @code{tail} = "unequal", "larger", and "smaller", the test statistics are ## max|S(X) - CDF(X)|, max[S(X) - CDF(X)], and max[CDF(X) - S(X)], respectively. ## ## @code{[@var{h}, @var{p}] = kstest (@dots{})} also returns the asymptotic ## p-value @var{p}. ## ## @code{[@var{h}, @var{p}, @var{ksstat}] = kstest (@dots{})} returns the K-S ## test statistic @var{ksstat} defined above for the test type indicated by the ## "tail" option ## ## In the matrix version of CDF, column 1 contains the x-axis data and column 2 ## the corresponding y-axis c.d.f data. Since the K-S test statistic will ## occur at one of the observations in @var{x}, the calculation is most ## efficient when CDF is only specified at the observations in @var{x}. When ## column 1 of CDF represents x-axis points independent of @var{x}, CDF is ## linearly interpolated at the observations found in the vector @var{x}. In ## this case, the interval along the x-axis (the column 1 spread of CDF) must ## span the observations in @var{x} for successful interpolation. ## ## The decision to reject the null hypothesis is based on comparing the p-value ## @var{p} with the "alpha" value, not by comparing the statistic @var{ksstat} ## with the critical value @var{cv}. @var{cv} is computed separately using an ## approximate formula or by interpolation using Miller's approximation table. ## The formula and table cover the range 0.01 <= "alpha" <= 0.2 for two-sided ## tests and 0.005 <= "alpha" <= 0.1 for one-sided tests. CV is returned as NaN ## if "alpha" is outside this range. Since CV is approximate, a comparison of ## @var{ksstat} with @var{cv} may occasionally lead to a different conclusion ## than a comparison of @var{p} with "alpha". ## ## @seealso{kstest2, cdfplot} ## @end deftypefn function [H, pValue, ksstat, cV] = kstest (x, varargin) ## Check input parameters if (nargin < 1) error ("kstest: too few inputs."); endif if (! isvector (x) || ! isreal (x)) error ("kstest: X must be a vector of real numbers."); endif ## Add defaults alpha = 0.05; tail = "unequal"; CDF = []; ## Parse extra parameters if (length (varargin) > 0 && mod (numel (varargin), 2) == 0) [~, prop] = parseparams (varargin); while (!isempty (prop)) switch (lower (prop{1})) case "alpha" alpha = prop{2}; case "tail" tail = prop{2}; case "cdf" CDF = prop{2}; otherwise error ("kstest: unknown option '%s'.", prop{1}); endswitch prop = prop(3:end); endwhile elseif (mod (numel (varargin), 2) != 0) error ("kstest: optional parameters must be in name/value pairs."); endif ## Check for valid alpha and tail parameters if (! isnumeric (alpha) || isnan (alpha) || ! isscalar (alpha) ... || alpha <= 0 || alpha >= 1) error ("kstest: alpha must be a numeric scalar in the range (0,1)."); endif if (! isa (tail, 'char')) error ("kstest: tail argument must be a string."); elseif (sum (strcmpi (tail, {"unequal", "larger", "smaller"})) < 1) error ("kstest: tail value must be either 'both', right' or 'left'."); endif ## Remove NaNs, get sample size and compute empirical cdf x(isnan (x)) = []; n = length (x); [sampleCDF, x] = ecdf (x); ## Remove 1st element x = x(2:end); ## Check the hypothesized CDF specified under the null hypothesis. ## No CDF was provided (use default) if (isempty (CDF)) xCDF = x; yCDF = normcdf (x, 0, 1); ## If CDF is a function handle elseif (isa (CDF, "function_handle")) xCDF = x; yCDF = feval (CDF, x); if (! isequal (size (xCDF), size (yCDF))) error ("kstest: invalid function handle."); endif ## If CDF is character vector elseif (isa (CDF, "char") && isvector (CDF)) ## Check for supported distibutions PDO = makedist (); if (! any (strcmpi (PDO, CDF))) error ("kstest: '%s' is not a supported distribution.", CDF); endif pd = makedist (CDF); xCDF = x; yCDF = pd.cdf (x); ## If CDF is a probability distribution object elseif (isobject (CDF)) PDO = makedist (); PDO = cellfun (@(x) sprintf ("%sDistribution", x), PDO, ... "UniformOutput", false); if (! any (isa (CDF, PDO))) error ("kstest: 'CDF' must be a probability distribution object."); endif xCDF = x; yCDF = CDF.cdf (x); ## If CDF is numerical elseif (! isempty (CDF) && isnumeric (CDF)) if (size (CDF, 2) != 2) error ("kstest: numerical CDF should have only 2 columns."); endif CDF(isnan (sum (CDF, 2)),:) = []; if (size (CDF, 1) == 0) error ("kstest: numerical CDF should have at least one row."); endif ## Sort numerical CDF [xCDF, i] = sort (CDF(:,1)); yCDF = CDF(i,2); ## Check that numerical CDF is incrementally sorted ydiff = diff (yCDF); if (any (ydiff < 0)) error("kstest: non-incrementing numerical CDF."); endif ## Remove duplicates. Check for consistency rd = find (diff (xCDF) == 0); if (! isempty (rd)) if (! all (ydiff(rd) == 0)) error ("kstest: wrong duplicates in numerical CDF."); endif xCDF(rd) = []; yCDF(rd) = []; endif ## Invalid value parsed as CDF optinonal argument else error ("kstest: invalid value parsed as CDF optinonal argument."); endif ## Check if CDF is specified at the observations in X and assign 2nd column ## of numerical CDF to null CDF if (isequal (x, xCDF)) nCDF = yCDF; ## Otherwise interpolate the numerical CDF to assign values to the null CDF else ## Check that 1st column range bounds the observations in X if (x(1) < xCDF(1) || x(end) > xCDF(end)) error ("kstest: wrong span in CDF."); endif nCDF = interp1 (xCDF, yCDF, x); endif ## Calculate the suitable KS statistic according to tail switch (tail) case "unequal" # 2-sided test: T = max|S(x) - CDF(x)|. delta1 = sampleCDF(1:end - 1) - nCDF; delta2 = sampleCDF(2:end) - nCDF; deltaCDF = abs ([delta1; delta2]); case "smaller" # 1-sided test: T = max[CDF(x) - S(x)]. delta1 = nCDF - sampleCDF(1:end - 1); delta2 = nCDF - sampleCDF(2:end); deltaCDF = [delta1; delta2]; case "larger" # 1-sided test: T = max[S(x) - CDF(x)]. delta1 = sampleCDF(1:end - 1) - nCDF; delta2 = sampleCDF(2:end) - nCDF; deltaCDF = [delta1; delta2]; endswitch ksstat = max (deltaCDF); ## Compute the asymptotic P-value approximation if (strcmpi (tail, "unequal")) # 2-sided test s = n * ksstat ^ 2; ## For d values that are in the far tail of the distribution (i.e. ## p-values > .999), the following lines will speed up the computation ## significantly, and provide accuracy up to 7 digits. if ((s > 7.24) || ((s > 3.76) && (n > 99))) pValue = 2 * exp (-(2.000071 + 0.331 / sqrt (n) + 1.409 / n) * s); else ## Express d as d = (k-h)/n, where k is a +ve integer and 0 < h < 1. k = ceil (ksstat * n); h = k - ksstat * n; m = 2 * k - 1; ## Create the H matrix, according to Marsaglia et al. if (m > 1) c = 1 ./ gamma ((1:m)' + 1); r = zeros (1,m); r(1) = 1; r(2) = 1; T = toeplitz (c, r); T(:,1) = T(:,1) - (h .^ (1:m)') ./ gamma ((1:m)' + 1); T(m,:) = fliplr (T(:,1)'); T(m,1) = (1 - 2 * h ^ m + max (0, 2 * h - 1) ^m) / gamma (m+1); else T = (1 - 2 * h ^ m + max (0, 2 * h - 1) ^ m) / gamma (m+1); endif ## Scaling before raising the matrix to a power if (! isscalar (T)) lmax = max (eig (T)); T = (T ./ lmax) ^ n; else lmax = 1; endif pValue = 1 - exp (gammaln (n+1) + n * log (lmax) - n * log (n)) * T(k,k); endif else # 1-sided test t = n * ksstat; k = ceil (t):n; pValue = sum (exp (log (t) - n * log (n) + gammaln (n + 1) ... - gammaln (k + 1) - gammaln (n - k + 1) + k .* log (k - t) ... + (n - k - 1) .* log (t + n - k))); endif ## Return hypothesis test H = (pValue < alpha); ## Calculate critical Value (cV) if requested if (nargout > 3) ## The critical value table used below is expressed in reference to a ## 1-sided significance level. Hence alpha is halved for a two-sided test. if (strcmpi (tail, "unequal")) # 2-sided test alpha1 = alpha / 2; else # 1-sided test alpha1 = alpha; endif if ((alpha1 >= 0.005) && (alpha1 <= 0.10)) ## If the sample size 'n' is greater than 20, use Miller's approximation ## Otherwise interpolate into his 'exact' table. if (n <= 20) # Small sample exact values. % Exact K-S test critical values based on Miller's approximation. a1 = [0.00500, 0.01000, 0.02500, 0.05000, 0.10000]'; exact = [0.99500, 0.99000, 0.97500, 0.95000, 0.90000; ... 0.92929, 0.90000, 0.84189, 0.77639, 0.68377; ... 0.82900, 0.78456, 0.70760, 0.63604, 0.56481; ... 0.73424, 0.68887, 0.62394, 0.56522, 0.49265; ... 0.66853, 0.62718, 0.56328, 0.50945, 0.44698; ... 0.61661, 0.57741, 0.51926, 0.46799, 0.41037; ... 0.57581, 0.53844, 0.48342, 0.43607, 0.38148; ... 0.54179, 0.50654, 0.45427, 0.40962, 0.35831; ... 0.51332, 0.47960, 0.43001, 0.38746, 0.33910; ... 0.48893, 0.45662, 0.40925, 0.36866, 0.32260; ... 0.46770, 0.43670, 0.39122, 0.35242, 0.30829; ... 0.44905, 0.41918, 0.37543, 0.33815, 0.29577; ... 0.43247, 0.40362, 0.36143, 0.32549, 0.28470; ... 0.41762, 0.38970, 0.34890, 0.31417, 0.27481; ... 0.40420, 0.37713, 0.33760, 0.30397, 0.26588; ... 0.39201, 0.36571, 0.32733, 0.29472, 0.25778; ... 0.38086, 0.35528, 0.31796, 0.28627, 0.25039; ... 0.37062, 0.34569, 0.30936, 0.27851, 0.24360; ... 0.36117, 0.33685, 0.30143, 0.27136, 0.23735; ... 0.35241, 0.32866, 0.29408, 0.26473, 0.23156]; cV = spline (a1 , exact(n,:)' , alpha1); else # Large sample approximate values. A = 0.09037 * (-log10 (alpha1)) .^ 1.5 + 0.01515 * ... log10 (alpha1) .^ 2 - 0.08467 * alpha1 - 0.11143; asymptoticStat = sqrt (-0.5 * log (alpha1) ./ n); cV = asymptoticStat - 0.16693 ./ n - A ./ n .^ 1.5; cV = min (cV, 1 - alpha1); endif else cV = NaN; endif endif endfunction %!demo %! ## Use the stock return data set to test the null hypothesis that the data %! ## come from a standard normal distribution against the alternative %! ## hypothesis that the population CDF of the data is larger that the %! ## standard normal CDF. %! %! load stockreturns; %! x = stocks(:,2); %! [h, p, k, c] = kstest (x, "Tail", "larger") %! %! ## Compute the empirical CDF and plot against the standard normal CDF %! [f, x_values] = ecdf (x); %! h1 = plot (x_values, f); %! hold on; %! h2 = plot (x_values, normcdf (x_values), 'r--'); %! set (h1, "LineWidth", 2); %! set (h2, "LineWidth", 2); %! legend ([h1, h2], "Empirical CDF", "Standard Normal CDF", ... %! "Location", "southeast"); %! title ("Empirical CDF of stock return data against standard normal CDF") ## Test input %!error kstest () %!error kstest (ones (2, 4)) %!error kstest ([2, 3, 5, 3+3i]) %!error kstest ([2, 3, 4, 5, 6], "opt", 0.51) %!error ... %! kstest ([2, 3, 4, 5, 6], "tail") %!error ... %! kstest ([2,3,4,5,6],"alpha", [0.05, 0.05]) %!error ... %! kstest ([2, 3, 4, 5, 6], "alpha", NaN) %!error ... %! kstest ([2, 3, 4, 5, 6], "tail", 0) %!error ... %! kstest ([2,3,4,5,6], "tail", "whatever") %!error ... %! kstest ([1, 2, 3, 4, 5], "CDF", @(x) repmat (x, 2, 3)) %!error ... %! kstest ([1, 2, 3, 4, 5], "CDF", "somedist") %!error ... %! kstest ([1, 2, 3, 4, 5], "CDF", cvpartition (5)) %!error ... %! kstest ([2, 3, 4, 5, 6], "alpha", 0.05, "CDF", [2, 3, 4; 1, 3, 4; 1, 2, 1]) %!error ... %! kstest ([2, 3, 4, 5, 6], "alpha", 0.05, "CDF", nan (5, 2)) %!error ... %! kstest ([2, 3, 4, 5, 6], "CDF", [2, 3; 1, 4; 3, 2]) %!error ... %! kstest ([2, 3, 4, 5, 6], "CDF", [2, 3; 2, 4; 3, 5]) %!error ... %! kstest ([2, 3, 4, 5, 6], "CDF", {1, 2, 3, 4, 5}) ## Test results %!test %! load examgrades %! [h, p] = kstest (grades(:,1)); %! assert (h, true); %! assert (p, 7.58603305206105e-107, 1e-14); %!test %! load examgrades %! [h, p] = kstest (grades(:,1), "CDF", @(x) normcdf(x, 75, 10)); %! assert (h, false); %! assert (p, 0.5612, 1e-4); %!test %! load examgrades %! x = grades(:,1); %! test_cdf = makedist ("tlocationscale", "mu", 75, "sigma", 10, "nu", 1); %! [h, p] = kstest (x, "alpha", 0.01, "CDF", test_cdf); %! assert (h, true); %! assert (p, 0.0021, 1e-4); %!test %! load stockreturns %! x = stocks(:,3); %! [h,p,k,c] = kstest (x, "Tail", "larger"); %! assert (h, true); %! assert (p, 5.085438806199252e-05, 1e-14); %! assert (k, 0.2197, 1e-4); %! assert (c, 0.1207, 1e-4); statistics-release-1.7.3/inst/kstest2.m000066400000000000000000000166771475240274700201210ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} kstest2 (@var{x1}, @var{x2}) ## @deftypefnx {statistics} {@var{h} =} kstest2 (@var{x1}, @var{x2}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{h}, @var{p}] =} kstest2 (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{p}, @var{ks2stat}] =} kstest2 (@dots{}) ## ## Two-sample Kolmogorov-Smirnov goodness-of-fit hypothesis test. ## ## @code{@var{h} = kstest2 (@var{x1}, @var{x2})} returns a test decision for the ## null hypothesis that the data in vectors @var{x1} and @var{x2} are from the ## same continuous distribution, using the two-sample Kolmogorov-Smirnov test. ## The alternative hypothesis is that @var{x1} and @var{x2} are from different ## continuous distributions. The result @var{h} is 1 if the test rejects the ## null hypothesis at the 5% significance level, and 0 otherwise. ## ## @code{@var{h} = kstest2 (@var{x1}, @var{x2}, @var{name}, @var{value})} ## returns a test decision for a two-sample Kolmogorov-Smirnov test with ## additional options specified by one or more name-value pair arguments as ## shown below. ## ## @multitable @columnfractions 0.20 0.8 ## @item "alpha" @tab A value @var{alpha} between 0 and 1 specifying the ## significance level. Default is 0.05 for 5% significance. ## ## @item "tail" @tab A string indicating the type of test: ## @end multitable ## ## @multitable @columnfractions 0.03 0.2 0.77 ## @item @tab "unequal" @tab "F(X1) not equal to F(X2)" (two-sided) [Default] ## ## @item @tab "larger" @tab "F(X1) > F(X2)" (one-sided) ## ## @item @tab "smaller" @tab "F(X1) < F(X2)" (one-sided) ## @end multitable ## ## The two-sided test uses the maximum absolute difference between the cdfs of ## the distributions of the two data vectors. The test statistic is ## @code{D* = max(|F1(x) - F2(x)|)}, where F1(x) is the proportion of @var{x1} ## values less or equal to x and F2(x) is the proportion of @var{x2} values less ## than or equal to x. The one-sided test uses the actual value of the ## difference between the cdfs of the distributions of the two data vectors ## rather than the absolute value. The test statistic is ## @code{D* = max(F1(x) - F2(x))} or @code{D* = max(F2(x) - F1(x))} for ## @code{tail} = "larger" or "smaller", respectively. ## ## @code{[@var{h}, @var{p}] = kstest2 (@dots{})} also returns the ## asymptotic p-value @var{p}. ## ## @code{[@var{h}, @var{p}, @var{ks2stat}] = kstest2 (@dots{})} also returns ## the Kolmogorov-Smirnov test statistic @var{ks2stat} defined above for the ## test type indicated by @code{tail}. ## ## @seealso{kstest, cdfplot} ## @end deftypefn function [H, pValue, ks2stat] = kstest2 (x1, x2, varargin) ## Check input parameters if nargin < 2 error ("kstest2: Too few inputs."); endif if ! isvector (x1) || ! isreal (x1) || ! isvector (x2) || ! isreal (x2) error ("kstest2: X1 and X2 must be vectors of real numbers."); endif ## Add defaults alpha = 0.05; tail = "unequal"; ## Parse extra parameters if nargin > 2 && mod (numel (varargin), 2) == 0 [~, prop] = parseparams (varargin); while (!isempty (prop)) switch (lower (prop{1})) case "alpha" alpha = prop{2}; case "tail" tail = prop{2}; otherwise error ("kstest2: Unknown option %s", prop{1}); endswitch prop = prop(3:end); endwhile elseif nargin > 2 error ("kstest2: optional parameters must be in name/value pairs."); endif ## Check for valid alpha and tail parameters if (! isnumeric (alpha) || isnan (alpha) || ! isscalar (alpha) ... || alpha <= 0 || alpha >= 1) error ("kstest2: alpha must be a numeric scalar in the range (0,1)."); endif if ! isa (tail, 'char') error ("kstest2: tail argument must be a string"); elseif sum (strcmpi (tail, {"unequal", "larger", "smaller"})) < 1 error ("kstest2: tail value must be either 'both', right' or 'left'."); endif ## Make x1 and x2 column vectors x1 = x1(:); x2 = x2(:); ## Remove missing values (NaN) x1(isnan (x1)) = []; x2(isnan (x2)) = []; ## Check for remaining data in both vectors if isempty (x1) error ("kstest2: Not enough data in X1"); elseif isempty (x2) error ("kstest2: Not enough data in X2"); endif ## Calculate F1(x) and F2(x) binEdges = [-inf; sort([x1;x2]); inf]; binCounts1 = histc (x1 , binEdges, 1); binCounts2 = histc (x2 , binEdges, 1); sumCounts1 = cumsum (binCounts1) ./ sum (binCounts1); sumCounts2 = cumsum (binCounts2) ./ sum (binCounts2); sampleCDF1 = sumCounts1(1:end - 1); sampleCDF2 = sumCounts2(1:end - 1); ## Calculate the suitable KS statistic according to tail switch tail case "unequal" # 2-sided test: T = max|F1(x) - F2(x)|. deltaCDF = abs (sampleCDF1 - sampleCDF2); case "smaller" # 1-sided test: T = max[F2(x) - F1(x)]. deltaCDF = sampleCDF2 - sampleCDF1; case "larger" # 1-sided test: T = max[F1(x) - F2(x)]. deltaCDF = sampleCDF1 - sampleCDF2; endswitch ks2stat = max (deltaCDF); ## Compute the asymptotic P-value approximation n_x1 = length(x1); n_x2 = length(x2); n = n_x1 * n_x2 /(n_x1 + n_x2); lambda = max ((sqrt (n) + 0.12 + 0.11 / sqrt (n)) * ks2stat, 0); if strcmpi (tail, "unequal") # 2-sided test v = [1:101]; pValue = 2 * sum ((-1) .^ (v-1) .* exp (-2 * lambda * lambda * v .^ 2)); pValue = min (max (pValue, 0), 1); else # 1-sided test pValue = exp(-2 * lambda * lambda); endif ## Return hypothesis test H = (alpha >= pValue); endfunction ## Test input %!error kstest2 ([1,2,3,4,5,5]) %!error kstest2 (ones(2,4), [1,2,3,4,5,5]) %!error kstest2 ([2,3,5,7,3+3i], [1,2,3,4,5,5]) %!error kstest2 ([2,3,4,5,6],[3;5;7;8;7;6;5],"tail") %!error kstest2 ([2,3,4,5,6],[3;5;7;8;7;6;5],"tail", "whatever") %!error kstest2 ([2,3,4,5,6],[3;5;7;8;7;6;5],"badoption", 0.51) %!error kstest2 ([2,3,4,5,6],[3;5;7;8;7;6;5],"tail", 0) %!error kstest2 ([2,3,4,5,6],[3;5;7;8;7;6;5],"alpha", 0) %!error kstest2 ([2,3,4,5,6],[3;5;7;8;7;6;5],"alpha", NaN) %!error kstest2 ([NaN,NaN,NaN,NaN,NaN],[3;5;7;8;7;6;5],"tail", "unequal") ## Test results %!test %! load examgrades %! [h, p] = kstest2 (grades(:,1), grades(:,2)); %! assert (h, false); %! assert (p, 0.1222791870137312, 1e-14); %!test %! load examgrades %! [h, p] = kstest2 (grades(:,1), grades(:,2), "tail", "larger"); %! assert (h, false); %! assert (p, 0.1844421391011258, 1e-14); %!test %! load examgrades %! [h, p] = kstest2 (grades(:,1), grades(:,2), "tail", "smaller"); %! assert (h, false); %! assert (p, 0.06115357930171663, 1e-14); %!test %! load examgrades %! [h, p] = kstest2 (grades(:,1), grades(:,2), "tail", "smaller", "alpha", 0.1); %! assert (h, true); %! assert (p, 0.06115357930171663, 1e-14); statistics-release-1.7.3/inst/levene_test.m000066400000000000000000000271731475240274700210300ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} levene_test (@var{x}) ## @deftypefnx {statistics} {@var{h} =} levene_test (@var{x}, @var{group}) ## @deftypefnx {statistics} {@var{h} =} levene_test (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {@var{h} =} levene_test (@var{x}, @var{testtype}) ## @deftypefnx {statistics} {@var{h} =} levene_test (@var{x}, @var{group}, @var{alpha}) ## @deftypefnx {statistics} {@var{h} =} levene_test (@var{x}, @var{group}, @var{testtype}) ## @deftypefnx {statistics} {@var{h} =} levene_test (@var{x}, @var{group}, @var{alpha}, @var{testtype}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} levene_test (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{W}] =} levene_test (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{W}, @var{df}] =} levene_test (@dots{}) ## ## Perform a Levene's test for the homogeneity of variances. ## ## Under the null hypothesis of equal variances, the test statistic @var{W} ## approximately follows an F distribution with @var{df} degrees of ## freedom being a vector ([k-1, N-k]). ## ## The p-value (1 minus the CDF of this distribution at @var{W}) is returned in ## @var{pval}. @var{h} = 1 if the null hypothesis is rejected at the ## significance level of @var{alpha}. Otherwise @var{h} = 0. ## ## Input Arguments: ## ## @itemize ## @item ## @var{x} contains the data and it can either be a vector or matrix. ## If @var{x} is a matrix, then each column is treated as a separate group. ## If @var{x} is a vector, then the @var{group} argument is mandatory. ## NaN values are omitted. ## ## @item ## @var{group} contains the names for each group. If @var{x} is a vector, then ## @var{group} must be a vector of the same length, or a string array or cell ## array of strings with one row for each element of @var{x}. @var{x} values ## corresponding to the same value of @var{group} are placed in the same group. ## If @var{x} is a matrix, then @var{group} can either be a cell array of ## strings of a character array, with one row per column of @var{x} in the same ## way it is used in @code{anova1} function. If @var{x} is a matrix, then ## @var{group} can be omitted either by entering an empty array ([]) or by ## parsing only @var{alpha} as a second argument (if required to change its ## default value). ## ## @item ## @var{alpha} is the statistical significance value at which the null ## hypothesis is rejected. Its default value is 0.05 and it can be parsed ## either as a second argument (when @var{group} is omitted) or as a third ## argument. ## ## @item ## @var{testtype} is a string determining the type of Levene's test. By default ## it is set to "absolute", but the user can also parse "quadratic" in order to ## perform Levene's Quadratic test for equal variances or "median" in order to ## to perform the Brown-Forsythe's test. These options determine how the Z_ij ## values are computed. If an invalid name is parsed for @var{testtype}, then ## the Levene's Absolute test is performed. ## @end itemize ## ## @seealso{bartlett_test, vartest2, vartestn} ## @end deftypefn function [h, pval, W, df] = levene_test (x, varargin) ## Check for valid number of input arguments if (nargin < 1 || nargin > 4) error ("levene_test: invalid number of input arguments."); endif ## Add defaults group = []; alpha = 0.05; ttype = "absolute"; ## Check for 2nd argument being ALPHA, GROUP, or TESTTYPE if (nargin > 1) if (isscalar (varargin{1}) && isnumeric (varargin{1}) ... && numel (varargin{1}) == 1) alpha = varargin{1}; ## Check for valid alpha value if (alpha <= 0 || alpha >= 1) error ("levene_test: wrong value for alpha."); endif elseif (any (strcmpi (varargin{1}, {"absolute", "quadratic", "median"}))) ttype = varargin{1}; elseif (isvector (varargin{1}) && numel (varargin{1} > 1)) if ((size (x, 2) == 1 && size (x, 1) == numel (varargin{1})) || ... (size (x, 2) > 1 && size (x, 2) == numel (varargin{1}))) group = varargin{1}; else error ("levene_test: GROUP and X mismatch."); endif elseif (isempty (varargin{1})) ## Do nothing else error ("levene_test: invalid second input argument."); endif endif ## Check for 3rd argument if (nargin > 2) if (isscalar (varargin{2}) && isnumeric (varargin{2}) ... && numel (varargin{2} == 1)) alpha = varargin{2}; ## Check for valid alpha value if (alpha <= 0 || alpha >= 1) error ("levene_test: wrong value for alpha."); endif elseif (any (strcmpi (varargin{2}, {"absolute", "quadratic", "median"}))) ttype = varargin{2}; else error ("levene_test: invalid third input argument."); endif endif ## Check for 3rd argument if (nargin > 3) if (any (strcmpi (varargin{3}, {"absolute", "quadratic", "median"}))) ttype = varargin{3}; else error ("levene_test: invalid option for TESTTYPE as 4th argument."); endif endif ## Convert group to cell array from character array, make it a column if (! isempty (group) && ischar (group)) group = cellstr (group); endif if (size (group, 1) == 1) group = group'; endif ## If x is a matrix, convert it to column vector and create a ## corresponging column vector for groups if (length (x) < prod (size (x))) [n, m] = size (x); x = x(:); gi = reshape (repmat ((1:m), n, 1), n*m, 1); if (length (group) == 0) ## no group names are provided group = gi; elseif (size (group, 1) == m) ## group names exist and match columns group = group(gi,:); else error ("levene_test: columns in X and GROUP length do not match."); endif endif ## Check that x and group are the same size if (! all (numel (x) == numel (group))) error (srtcat (["levene_test: GROUP must be a vector with the same"], ... [" number of rows as x."])); endif ## Identify NaN values (if any) and remove them from X along with ## their corresponding values from group vector nonan = ! isnan (x); x = x(nonan); group = group(nonan, :); ## Convert group to indices and separate names [group_id, group_names] = grp2idx (group); group_id = group_id(:); ## Get sample size (N_i), mean (Y_i), median (Y_I) and sample values (Y_ij) ## for groups with more than one sample groups = size (group_names, 1); rgroup = []; N_i = zeros (1, groups); Y_i = N_i; Y_I = N_i; for k = 1:groups group_size = find (group_id == k); if (length (group_size) > 1) N_i(k) = length (group_size); Y_i(k) = mean (x(group_size)); Y_I(k) = median (x(group_size)); Y_ij{k} = x(group_size); else warning (strcat (sprintf ("levene_test: GROUP %s has a single", ... group_names{k}), [" sample and is not included in the test.\n"])); rgroup = [rgroup, k]; N_i(k) = 1; Y_i(k) = x(group_size); Y_I(k) = x(group_size); Y_ij{k} = x(group_size); endif endfor ## Remove groups with a single sample if (! isempty (rgroup)) N_i(rgroup) = []; Y_i(rgroup) = []; Y_I(rgroup) = []; Y_ij(rgroup) = []; k = k - numel (rgroup); endif ## Compute Z_ij for "absolute" or "quadratic" Levene's test switch (lower (ttype)) case "absolute" for i = 1:k Z_ij{i} = abs (Y_ij{i} - Y_i(i)); endfor case "quadratic" for i = 1:k Z_ij{i} = sqrt ((Y_ij{i} - Y_i(i)) .^ 2); endfor case "median" for i = 1:k Z_ij{i} = abs (Y_ij{i} - Y_I(i)); endfor endswitch ## Compute Z_i and Z_ Z_ = []; for i = 1:k Z_i(i) = mean (Z_ij{i}); Z_ = [Z_; Z_ij{i}(:)]; endfor Z_ = mean (Z_); ## Compute total sample size (N) N = sum (N_i); ## Calculate W statistic. termA = (N - k) / (k - 1); termB = sum (N_i .* ((Z_i - Z_) .^ 2)); termC = 0; for i = 1:k termC += sum ((Z_ij{i} - Z_i(i)) .^ 2); endfor W = termA * (termB / termC); ## Calculate p-value from the chi-square distribution pval = 1 - fcdf (W, k - 1, N - k); ## Save dfs df = [k-1, N-k]; ## Determine the test outcome h = double (pval < alpha); endfunction ## Test input validation %!error levene_test () %!error ... %! levene_test (1, 2, 3, 4, 5); %!error levene_test (randn (50, 2), 0); %!error ... %! levene_test (randn (50, 2), [1, 2, 3]); %!error ... %! levene_test (randn (50, 1), ones (55, 1)); %!error ... %! levene_test (randn (50, 1), ones (50, 2)); %!error ... %! levene_test (randn (50, 2), [], 1.2); %!error ... %! levene_test (randn (50, 2), "some_string"); %!error ... %! levene_test (randn (50, 2), [], "alpha"); %!error ... %! levene_test (randn (50, 1), [ones(25, 1); 2*ones(25, 1)], 1.2); %!error ... %! levene_test (randn (50, 1), [ones(25, 1); 2*ones(25, 1)], "err"); %!error ... %! levene_test (randn (50, 1), [ones(25, 1); 2*ones(25, 1)], 0.05, "type"); %!warning ... %! levene_test (randn (50, 1), [ones(24, 1); 2*ones(25, 1); 3]); ## Test results %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades); %! assert (h, 1); %! assert (pval, 9.523239714592791e-07, 1e-14); %! assert (W, 8.59529, 1e-5); %! assert (df, [4, 595]); %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades, [], "quadratic"); %! assert (h, 1); %! assert (pval, 9.523239714592791e-07, 1e-14); %! assert (W, 8.59529, 1e-5); %! assert (df, [4, 595]); %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades, [], "median"); %! assert (h, 1); %! assert (pval, 1.312093241723211e-06, 1e-14); %! assert (W, 8.415969, 1e-6); %! assert (df, [4, 595]); %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades(:,[1:3])); %! assert (h, 1); %! assert (pval, 0.004349390980463497, 1e-14); %! assert (W, 5.52139, 1e-5); %! assert (df, [2, 357]); %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades(:,[1:3]), "median"); %! assert (h, 1); %! assert (pval, 0.004355216763951453, 1e-14); %! assert (W, 5.52001, 1e-5); %! assert (df, [2, 357]); %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades(:,[3,4]), "quadratic"); %! assert (h, 0); %! assert (pval, 0.1807494957440653, 2e-14); %! assert (W, 1.80200, 1e-5); %! assert (df, [1, 238]); %!test %! load examgrades %! [h, pval, W, df] = levene_test (grades(:,[3,4]), "median"); %! assert (h, 0); %! assert (pval, 0.1978225622063785, 2e-14); %! assert (W, 1.66768, 1e-5); %! assert (df, [1, 238]); statistics-release-1.7.3/inst/linkage.m000066400000000000000000000253101475240274700201140ustar00rootroot00000000000000## Copyright (C) 2008 Francesco Potortì ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} linkage (@var{d}) ## @deftypefnx {statistics} {@var{y} =} linkage (@var{d}, @var{method}) ## @deftypefnx {statistics} {@var{y} =} linkage (@var{x}) ## @deftypefnx {statistics} {@var{y} =} linkage (@var{x}, @var{method}) ## @deftypefnx {statistics} {@var{y} =} linkage (@var{x}, @var{method}, @var{metric}) ## @deftypefnx {statistics} {@var{y} =} linkage (@var{x}, @var{method}, @var{arglist}) ## ## Produce a hierarchical clustering dendrogram. ## ## @var{d} is the dissimilarity matrix relative to n observations, ## formatted as a @math{(n-1)*n/2}x1 vector as produced by @code{pdist}. ## Alternatively, @var{x} contains data formatted for input to ## @code{pdist}, @var{metric} is a metric for @code{pdist} and ## @var{arglist} is a cell array containing arguments that are passed to ## @code{pdist}. ## ## @code{linkage} starts by putting each observation into a singleton ## cluster and numbering those from 1 to n. Then it merges two ## clusters, chosen according to @var{method}, to create a new cluster ## numbered n+1, and so on until all observations are grouped into ## a single cluster numbered 2(n-1). Row k of the ## (m-1)x3 output matrix relates to cluster n+k: the first ## two columns are the numbers of the two component clusters and column ## 3 contains their distance. ## ## @var{method} defines the way the distance between two clusters is ## computed and how they are recomputed when two clusters are merged: ## ## @table @samp ## @item "single" (default) ## Distance between two clusters is the minimum distance between two ## elements belonging each to one cluster. Produces a cluster tree ## known as minimum spanning tree. ## ## @item "complete" ## Furthest distance between two elements belonging each to one cluster. ## ## @item "average" ## Unweighted pair group method with averaging (UPGMA). ## The mean distance between all pair of elements each belonging to one ## cluster. ## ## @item "weighted" ## Weighted pair group method with averaging (WPGMA). ## When two clusters A and B are joined together, the new distance to a ## cluster C is the mean between distances A-C and B-C. ## ## @item "centroid" ## Unweighted Pair-Group Method using Centroids (UPGMC). ## Assumes Euclidean metric. The distance between cluster centroids, ## each centroid being the center of mass of a cluster. ## ## @item "median" ## Weighted pair-group method using centroids (WPGMC). ## Assumes Euclidean metric. Distance between cluster centroids. When ## two clusters are joined together, the new centroid is the midpoint ## between the joined centroids. ## ## @item "ward" ## Ward's sum of squared deviations about the group mean (ESS). ## Also known as minimum variance or inner squared distance. ## Assumes Euclidean metric. How much the moment of inertia of the ## merged cluster exceeds the sum of those of the individual clusters. ## @end table ## ## @strong{Reference} ## Ward, J. H. Hierarchical Grouping to Optimize an Objective Function ## J. Am. Statist. Assoc. 1963, 58, 236-244, ## @url{http://iv.slis.indiana.edu/sw/data/ward.pdf}. ## ## @seealso{pdist,squareform} ## @end deftypefn function dgram = linkage (d, method = "single", distarg, savememory) ## check the input if (nargin == 4) && (strcmpi (savememory, "savememory")) warning ("Octave:linkage_savemem", ... "linkage: option 'savememory' not implemented"); elseif (nargin < 1) || (nargin > 3) print_usage (); endif if (isempty (d)) error ("linkage: d cannot be empty"); endif methods = struct ... ("name", { "single"; "complete"; "average"; "weighted"; "centroid"; "median"; "ward" }, "distfunc", {(@(x) min(x)) # single (@(x) max(x)) # complete (@(x,i,j,w) sum(diag(w([i,j]))*x)/sum(w([i,j]))) # average (@(x) mean(x)) # weighted (@massdist) # centroid (@(x,i) massdist(x,i)) # median (@inertialdist) # ward }); mask = strcmp (lower (method), {methods.name}); if (! any (mask)) error ("linkage: %s: unknown method", method); endif dist = {methods.distfunc}{mask}; if (nargin >= 3 && ! isvector (d)) if (ischar (distarg)) d = pdist (d, distarg); elseif (iscell (distarg)) d = pdist (d, distarg{:}); else print_usage (); endif elseif (nargin < 3) if (! isvector (d)) d = pdist (d); endif else print_usage (); endif d = squareform (d, "tomatrix"); # dissimilarity NxN matrix n = rows (d); # the number of observations diagidx = sub2ind ([n,n], 1:n, 1:n); # indices of diagonal elements d(diagidx) = Inf; # consider a cluster as far from itself ## For equal-distance nodes, the order in which clusters are ## merged is arbitrary. Rotating the initial matrix produces an ## ordering similar to Matlab's. cname = n:-1:1; # cluster names in d d = rot90 (d, 2); # exchange low and high cluster numbers weight = ones (1, n); # cluster weights dgram = zeros (n-1, 3); # clusters from n+1 to 2*n-1 for cluster = n+1 : 2*n-1 ## Find the two nearest clusters [m midx] = min (d(:)); [r, c] = ind2sub (size (d), midx); ## Here is the new cluster dgram(cluster-n, :) = [cname(r) cname(c) d(r, c)]; ## Put it in place of the first one and remove the second cname(r) = cluster; cname(c) = []; ## Compute the new distances. ## (Octave-7+ needs switch stmt to avoid 'called with too many inputs' err.) switch find (mask) case {1, 2, 4} # 1 arg newd = dist (d([r c], :)); case {3, 5, 7} # 4 args newd = dist (d([r c], :), r, c, weight); case 6 # 2 args newd = dist (d([r c], :), r); otherwise endswitch newd(r) = Inf; # Take care of the diagonal element ## Put distances in place of the first ones, remove the second ones d(r,:) = newd; d(:,r) = newd'; d(c,:) = []; d(:,c) = []; ## The new weight is the sum of the components' weights weight(r) += weight(c); weight(c) = []; endfor ## Sort the cluster numbers, as Matlab does dgram(:,1:2) = sort (dgram(:,1:2), 2); ## Check that distances are monotonically increasing if (any (diff (dgram(:,3)) < 0)) warning ("Octave:clustering", "linkage: cluster distances do not monotonically increase\n\ you should probably use a method different from \"%s\"", method); endif endfunction ## Take two row vectors, which are the Euclidean distances of clusters I ## and J from the others. Column I of second row contains the distance ## between clusters I and J. The centre of gravity of the new cluster ## is on the segment joining the old ones. W are the weights of all ## clusters. Use the law of cosines to find the distances of the new ## cluster from all the others. function y = massdist (x, i, j, w) x .^= 2; # Squared Euclidean distances if (nargin == 2) # Median distance qi = 0.5; # Equal weights ("weighted") else # Centroid distance qi = 1 / (1 + w(j) / w(i)); # Proportional weights ("unweighted") endif y = sqrt (qi * x(1, :) + (1 - qi) * (x(2, :) - qi * x(2, i))); endfunction ## Take two row vectors, which are the inertial distances of clusters I ## and J from the others. Column I of second row contains the inertial ## distance between clusters I and J. The centre of gravity of the new ## cluster K is on the segment joining I and J. W are the weights of ## all clusters. Convert inertial to Euclidean distances, then use the ## law of cosines to find the Euclidean distances of K from all the ## other clusters, convert them back to inertial distances and return ## them. function y = inertialdist (x, i, j, w) wi = w(i); # The cluster wj = w(j); # weights. s = [wi + w; # Sum of weights for wj + w]; # all cluster pairs. p = [wi * w; # Product of weights for wj * w]; # all cluster pairs. x = x.^2 .* s ./ p; # Convert inertial dist. to squared Eucl. sij = wi + wj; # Sum of weights of I and J qi = wi / sij; # Normalise the weight of I ## Squared Euclidean distances between all clusters and new cluster K x = qi * x(1, :) + (1 - qi) * (x(2, :) - qi * x(2, i)); y = sqrt (x * sij .* w ./ (sij + w)); # convert Eucl. dist. to inertial endfunction %!shared x, t %! x = reshape (mod (magic (6),5), [], 3); %! t = 1e-6; %!assert (cond (linkage (pdist (x))), 34.119045, t); %!assert (cond (linkage (pdist (x), "complete")), 21.793345, t); %!assert (cond (linkage (pdist (x), "average")), 27.045012, t); %!assert (cond (linkage (pdist (x), "weighted")), 27.412889, t); %! lastwarn(); # Clear last warning before the test %!warning linkage (pdist (x), "centroid"); %!test %! warning off Octave:clustering %! assert (cond (linkage (pdist (x), "centroid")), 27.457477, t); %! warning on Octave:clustering %!warning linkage (pdist (x), "median"); %!test %! warning off Octave:clustering %! assert (cond (linkage (pdist (x), "median")), 27.683325, t); %! warning on Octave:clustering %!assert (cond (linkage (pdist (x), "ward")), 17.195198, t); %!assert (cond (linkage (x, "ward", "euclidean")), 17.195198, t); %!assert (cond (linkage (x, "ward", {"euclidean"})), 17.195198, t); %!assert (cond (linkage (x, "ward", {"minkowski", 2})), 17.195198, t); statistics-release-1.7.3/inst/loadmodel.m000066400000000000000000000063761475240274700204550ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {ClassificationSVM} {@var{obj} = } loadmodel (@var{filename}) ## ## Load a Classification or Regression model from a file. ## ## @code{@var{obj} = loadmodel (@var{filename})} loads a Classification or ## Regression object, @var{obj}, from a file defined in @var{filename}. ## ## @seealso{savemodel, ClassificationDiscriminant, ClassificationGAM, ## ClassificationKNN, ClassificationNeuralNetwork, ## ClassificationPartitionedModel, ClassificationSVM, RegressionGAM} ## @end deftypefn function obj = loadmodel (filename) ## Check input parameters if (nargin < 1) error ("loadmodel: too few arguments."); endif ## Supported Classification and Regression objects supported = {"ClassificationKNN"}; ## Read file into a structure data = load (filename); ## Check that 'classdef_name' variable exists and that it ## contains a valid Classification or Regression object if (! isfield (data, "classdef_name")) msg = " '%s' does not contain a Classification or Regression object."; error (strcat ("loadmodel:", msg), filename); endif ## Remove 'classdef_name' field from data structure classdef_name = data.classdef_name; data = rmfield (data, "classdef_name"); ## Parse data structure to the static load method of specified classdef switch (classdef_name) case "ClassificationDiscriminant" obj = ClassificationDiscriminant.load_model (filename, data); case "ClassificationGAM" obj = ClassificationGAM.load_model (filename, data); case "ClassificationKNN" obj = ClassificationKNN.load_model (filename, data); case "ClassificationNeuralNetwork" obj = ClassificationNeuralNetwork.load_model (filename, data); case "ClassificationPartitionedModel" obj = ClassificationPartitionedModel.load_model (filename, data); case "ClassificationSVM" obj = ClassificationSVM.load_model (filename, data); case "RegressionGAM" obj = RegressionGAM.load_model (filename, data); otherwise error ("loadmodel: '%s' is not supported.", classdef_name); endswitch endfunction ## Test input validation %!error loadmodel () %!error ... %! loadmodel ("fisheriris.mat") %!error ... %! loadmodel ("fail_loadmodel.mdl") %!error ... %! loadmodel ("fail_load_model.mdl") statistics-release-1.7.3/inst/logistic_regression.m000066400000000000000000000257021475240274700225640ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2022 Andrew Penn ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{intercept}, @var{slope}, @var{dev}, @var{dl}, @var{d2l}, @var{P}, @var{stats}] =} logistic_regression (@var{y}, @var{x}, @var{print}, @var{intercept}, @var{slope}) ## ## Perform ordinal logistic regression. ## ## Suppose @var{y} takes values in k ordered categories, and let ## @code{P_i (@var{x})} be the cumulative probability that @var{y} ## falls in one of the first i categories given the covariate ## @var{x}. Then ## ## @example ## [@var{intercept}, @var{slope}] = logistic_regression (@var{y}, @var{x}) ## @end example ## ## @noindent ## fits the model ## ## @example ## logit (P_i (@var{x})) = @var{x} * @var{slope} + @var{intercept}_i, i = 1 @dots{} k-1 ## @end example ## ## The number of ordinal categories, k, is taken to be the number ## of distinct values of @code{round (@var{y})}. If k equals 2, ## @var{y} is binary and the model is ordinary logistic regression. The ## matrix @var{x} is assumed to have full column rank. ## ## Given @var{y} only, @code{@var{intercept} = logistic_regression (@var{y})} ## fits the model with baseline logit odds only. ## ## The full form is ## ## @example ## @group ## [@var{intercept}, @var{slope}, @var{dev}, @var{dl}, @var{d2l}, @var{P}, @var{stats}] ## = logistic_regression (@var{y}, @var{x}, @var{print}, @var{intercept}, @var{slope}) ## @end group ## @end example ## ## @noindent ## in which all output arguments and all input arguments except @var{y} ## are optional. ## ## Setting @var{print} to 1 requests summary information about the fitted ## model to be displayed. Setting @var{print} to 2 requests information ## about convergence at each iteration. Other values request no ## information to be displayed. The input arguments @var{intercept} and ## @var{slope} give initial estimates for @var{intercept} and @var{slope}. ## ## The returned value @var{dev} holds minus twice the log-likelihood. ## ## The returned values @var{dl} and @var{d2l} are the vector of first ## and the matrix of second derivatives of the log-likelihood with ## respect to @var{intercept} and @var{slope}. ## ## @var{P} holds estimates for the conditional distribution of @var{y} ## given @var{x}. ## ## @var{stats} returns a structure that contains the following fields: ## @itemize ## @item ## "intercept": intercept coefficients ## @item ## "slope": slope coefficients ## @item ## "coeff": regression coefficients (intercepts and slops) ## @item ## "covb": estimated covariance matrix for coefficients (coeff) ## @item ## "coeffcorr": correlation matrix for coeff ## @item ## "se": standard errors of the coeff ## @item ## "z": z statistics for coeff ## @item ## "pval": p-values for coeff ## @end itemize ## @end deftypefn function [intercept, slope, dev, dl, d2l, P, stats] = logistic_regression (y, x, print, intercept, slope) ## check input y = round (y(:)); if (nargin < 2) x = zeros (length (y), 0); endif; xymissing = (isnan (y) | any (isnan (x), 2)); y(xymissing) = []; x(xymissing,:) = []; [my, ny] = size (y); [mx, nx] = size (x); if (mx != my) error ("logistic_regression: X and Y must have the same number of observations"); endif ## initial calculations tol = 1e-12; incr = 10; decr = 2; ymin = min (y); ymax = max (y); yrange = ymax - ymin; z = (y * ones (1, yrange)) == ((y * 0 + 1) * (ymin : (ymax - 1))); z1 = (y * ones (1, yrange)) == ((y * 0 + 1) * ((ymin + 1) : ymax)); z = z(:, any (z)); z1 = z1(:, any(z1)); [mz, nz] = size (z); ## starting values if (nargin < 3) print = 0; endif; if (nargin < 4) g = cumsum (sum (z))' ./ my; intercept = log (g ./ (1 - g)); endif; if (nargin < 5) slope = zeros (nx, 1); endif; tb = [intercept; slope]; ## likelihood and derivatives at starting values [g, g1, p, dev] = logistic_regression_likelihood (y, x, tb, z, z1); [dl, d2l] = logistic_regression_derivatives (x, z, z1, g, g1, p); epsilon = std (vec (d2l)) / 1000; ## maximize likelihood using Levenberg modified Newton's method iter = 0; while (abs (dl' * (d2l \ dl) / length (dl)) > tol) iter += 1; tbold = tb; devold = dev; tb = tbold - d2l \ dl; [g, g1, p, dev] = logistic_regression_likelihood (y, x, tb, z, z1); if ((dev - devold) / (dl' * (tb - tbold)) < 0) epsilon /= decr; else while ((dev - devold) / (dl' * (tb - tbold)) > 0) epsilon *= incr; if (epsilon > 1e+15) error ("logistic_regression: epsilon too large"); endif tb = tbold - (d2l - epsilon * eye (size (d2l))) \ dl; [g, g1, p, dev] = logistic_regression_likelihood (y, x, tb, z, z1); disp ("epsilon"); disp (epsilon); endwhile endif [dl, d2l] = logistic_regression_derivatives (x, z, z1, g, g1, p); if (print == 2) disp ("Iteration"); disp (iter); disp ("Deviance"); disp (dev); disp ("First derivative"); disp (dl'); disp ("Eigenvalues of second derivative"); disp (eig (d2l)'); endif endwhile ## tidy up output intercept = tb(1 : nz, 1); slope = tb((nz + 1) : (nz + nx), 1); cov = inv (-d2l); se = sqrt (diag (cov)); if (nargout > 5) ## Compute predicted probabilities (P) if (nx > 0) e = ((x * slope) * ones (1, nz)) + ((y * 0 + 1) * intercept'); else e = (y * 0 + 1) * intercept'; endif P = diff ([(y * 0), (exp (e) ./ (1 + exp (e))), (y * 0 + 1)]')'; endif if (nargout > 6) ## Create stats structure dfe = mx - nx - 1; zstat = tb ./ se; coeffcorr = cov2corr (cov); resid = y - P(:,2); stats = struct ("intercept", intercept, ... "slope", slope, ... "coeff", tb, ... "cov", cov, ... "coeffcorr", coeffcorr, ... "se", se, ... "z", zstat, ... "pval", 2 * normcdf (-abs (zstat))); endif if (print >= 1) printf ("\n"); printf ("Logistic Regression Results:\n"); printf ("\n"); printf ("Number of Iterations: %d\n", iter); printf ("Deviance: %f\n", dev); printf ("Parameter Estimates:\n"); printf (" Intercept S.E.\n"); for i = 1 : nz printf (" %8.4f %8.4f\n", tb (i), se (i)); endfor if (nx > 0) printf (" Slope S.E.\n"); for i = (nz + 1) : (nz + nx) printf (" %8.4f %8.4f\n", tb (i), se (i)); endfor endif endif endfunction function [g, g1, p, dev] = logistic_regression_likelihood (y, x, slope, z, z1) ## Calculate the likelihood for the ordinal logistic regression model. e = exp ([z, x] * slope); e1 = exp ([z1, x] * slope); g = e ./ (1 + e); g1 = e1 ./ (1 + e1); g = max (y == max (y), g); g1 = min (y > min (y), g1); p = g - g1; dev = -2 * sum (log (p)); endfunction function [dl, d2l] = logistic_regression_derivatives (x, z, z1, g, g1, p) ## Calculate derivatives of the log-likelihood for ordinal logistic regression ## first derivative v = g .* (1 - g) ./ p; v1 = g1 .* (1 - g1) ./ p; dlogp = [(diag (v) * z - diag (v1) * z1), (diag (v - v1) * x)]; dl = sum (dlogp)'; ## second derivative w = v .* (1 - 2 * g); w1 = v1 .* (1 - 2 * g1); d2l = [z, x]' * diag (w) * [z, x] - [z1, x]' * diag (w1) * [z1, x] ... - dlogp' * dlogp; endfunction function R = cov2corr (vcov) ## Convert covariance matrix to correlation matrix sed = sqrt (diag (vcov)); R = vcov ./ (sed * sed'); R = (R + R') / 2; # This step ensures that the matrix is positive definite endfunction %!test %! # Output compared to following MATLAB commands %! # [B, DEV, STATS] = mnrfit(X,Y+1,'model','ordinal'); %! # P = mnrval(B,X) %! X = [1.489381332449196, 1.1534152241851305; ... %! 1.8110085304863965, 0.9449666896938425; ... %! -0.04453299665130296, 0.34278203449678646; ... %! -0.36616019468850347, 1.130254275908322; ... %! 0.15339143291005095, -0.7921044310668951; ... %! -1.6031878794469698, -1.8343471035233376; ... %! -0.14349521143198166, -0.6762996896828459; ... %! -0.4403818557740143, -0.7921044310668951; ... %! -0.7372685001160434, -0.027793137932169563; ... %! -0.11875465773681024, 0.5512305689880763]; %! Y = [1,1,1,1,1,0,0,0,0,0]'; %! [INTERCEPT, SLOPE, DEV, DL, D2L, P] = logistic_regression (Y, X, false); #%! assert (DEV, 5.680728861124, 1e-05); #%! assert (INTERCEPT(1), -1.10999599948243, 1e-05); #%! assert (SLOPE(1), -9.12480634225699, 1e-05); #%! assert (SLOPE(2), -2.18746124517476, 1e-05); #%! assert (corr(P(:,1),Y), -0.786673288976468, 1e-05); %!test %! # Output compared to following MATLAB commands %! # [B, DEV, STATS] = mnrfit(X,Y+1,'model','ordinal'); %! load carbig %! X = [Acceleration Displacement Horsepower Weight]; %! miles = [1,1,1,1,1,1,1,1,1,1,NaN,NaN,NaN,NaN,NaN,1,1,NaN,1,1,2,2,1,2,2,2, ... %! 2,2,2,2,2,1,1,1,1,2,2,2,2,NaN,2,1,1,2,1,1,1,1,1,1,1,1,1,2,2,1,2, ... %! 2,3,3,3,3,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,2,2,2,2,2, ... %! 2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,2,2,2,1,2,2, ... %! 2,1,1,3,2,2,2,1,2,2,1,2,2,2,1,3,2,3,2,1,1,1,1,1,1,1,1,3,2,2,3,3, ... %! 2,2,2,2,2,3,2,1,1,1,1,1,1,1,1,1,1,1,2,2,1,3,2,2,2,2,2,2,1,3,2,2, ... %! 2,2,2,3,2,2,2,2,2,1,1,1,1,2,2,2,2,3,2,3,3,2,1,1,1,3,3,2,2,2,1,2, ... %! 2,1,1,1,1,1,3,3,3,2,3,1,1,1,1,1,2,2,1,1,1,1,1,3,2,2,2,3,3,3,3,2, ... %! 2,2,4,3,3,4,3,2,2,2,2,2,2,2,2,2,2,2,1,1,2,1,1,1,3,2,2,3,2,2,2,2, ... %! 2,1,2,1,3,3,2,2,2,2,2,1,1,1,1,1,1,2,1,3,3,3,2,2,2,2,2,3,3,3,3,2, ... %! 2,2,3,4,3,3,3,2,2,2,2,3,3,3,3,3,4,2,4,4,4,3,3,4,4,3,3,3,2,3,2,3, ... %! 2,2,2,2,3,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,NaN,3,2,2,2,2,2,1,2, ... %! 2,3,3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,3,3,2,2,4,3,2,3]'; %! [INTERCEPT, SLOPE, DEV, DL, D2L, P] = logistic_regression (miles, X, false); %! assert (DEV, 433.197174495549, 1e-05); %! assert (INTERCEPT(1), -16.6895155618903, 1e-05); %! assert (INTERCEPT(2), -11.7207818178493, 1e-05); %! assert (INTERCEPT(3), -8.0605768506075, 1e-05); %! assert (SLOPE(1), 0.104762463756714, 1e-05); %! assert (SLOPE(2), 0.0103357623191891, 1e-05); %! assert (SLOPE(3), 0.0645199313242276, 1e-05); %! assert (SLOPE(4), 0.00166377028388103, 1e-05); statistics-release-1.7.3/inst/logit.m000066400000000000000000000027251475240274700176250ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} logit (@var{p}) ## ## Compute the logit for each value of @var{p} ## ## The logit is defined as ## @tex ## $$ {\rm logit}(p) = \log\Big({p \over 1-p}\Big) $$ ## @end tex ## @ifnottex ## ## @example ## logit (@var{p}) = log (@var{p} / (1-@var{p})) ## @end example ## ## @end ifnottex ## @seealso{probit, logicdf} ## @end deftypefn function x = logit (p) if (nargin != 1) print_usage (); endif x = logiinv (p, 0, 1); endfunction %!test %! p = [0.01:0.01:0.99]; %! assert (logit (p), log (p ./ (1-p)), 25*eps); %!assert (logit ([-1, 0, 0.5, 1, 2]), [NaN, -Inf, 0, +Inf, NaN]) ## Test input validation %!error logit () %!error logit (1, 2) statistics-release-1.7.3/inst/mahal.m000066400000000000000000000051701475240274700175660ustar00rootroot00000000000000## Copyright (C) 2015 Lachlan Andrew ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## This program, is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{d} =} mahal (@var{y}, @var{x}) ## ## Mahalanobis' D-square distance. ## ## Return the Mahalanobis' D-square distance of the points in ## @var{y} from the distribution implied by points @var{x}. ## ## Specifically, it uses a Cholesky decomposition to set ## ## @example ## answer(i) = (@var{y}(i,:) - mean (@var{x})) * inv (A) * (@var{y}(i,:)-mean (@var{x}))' ## @end example ## ## where A is the covariance of @var{x}. ## ## The data @var{x} and @var{y} must have the same number of components ## (columns), but may have a different number of observations (rows). ## ## @end deftypefn function retval = mahal (y, x) if (nargin != 2) print_usage (); endif if (! (isnumeric (x) || islogical (x)) || ! (isnumeric (y) || islogical (y))) error ("mahal: X and Y must be numeric matrices or vectors"); endif if (! ismatrix (x) || ! ismatrix (y)) error ("mahal: X and Y must be 2-D matrices or vectors"); endif [xr, xc] = size (x); [yr, yc] = size (y); if (xc != yc) error ("mahal: X and Y must have the same number of columns"); endif if (isinteger (x)) x = double (x); endif xm = mean (x, 1); ## Center data by subtracting mean of x x = bsxfun (@minus, x, xm); y = bsxfun (@minus, y, xm); w = (x' * x) / (xr - 1); retval = sumsq (y / chol (w), 2); endfunction ## Test input validation %!error mahal () %!error mahal (1, 2, 3) %!error mahal ("A", "B") %!error mahal ([1, 2], ["A", "B"]) %!error mahal (ones (2, 2, 2)) %!error mahal (ones (2, 2), ones (2, 2, 2)) %!error mahal (ones (2, 2), ones (2, 3)) %!test %! X = [1 0; 0 1; 1 1; 0 0]; %! assert (mahal (X, X), [1.5; 1.5; 1.5; 1.5], 10*eps) %! assert (mahal (X, X+1), [7.5; 7.5; 1.5; 13.5], 10*eps) %!assert (mahal ([true; true], [false; true]), [0.5; 0.5], eps) statistics-release-1.7.3/inst/manova1.m000066400000000000000000000215461475240274700200530ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{d} =} manova1 (@var{x}, @var{group}) ## @deftypefnx {statistics} {@var{d} =} manova1 (@var{x}, @var{group}, @var{alpha}) ## @deftypefnx {statistics} {[@var{d}, @var{p}] =} manova1 (@dots{}) ## @deftypefnx {statistics} {[@var{d}, @var{p}, @var{stats}] =} manova1 (@dots{}) ## ## One-way multivariate analysis of variance (MANOVA). ## ## @code{@var{d} = manova1 (@var{x}, @var{group}, @var{alpha})} performs a ## one-way MANOVA for comparing the mean vectors of two or more groups of ## multivariate data. ## ## @var{x} is a matrix with each row representing a multivariate observation, ## and each column representing a variable. ## ## @var{group} is a numeric vector, string array, or cell array of strings with ## the same number of rows as @var{x}. @var{x} values are in the same group if ## they correspond to the same value of GROUP. ## ## @var{alpha} is the scalar significance level and is 0.05 by default. ## ## @var{d} is an estimate of the dimension of the group means. It is the ## smallest dimension such that a test of the hypothesis that the means lie on ## a space of that dimension is not rejected. If @var{d} = 0 for example, we ## cannot reject the hypothesis that the means are the same. If @var{d} = 1, we ## reject the hypothesis that the means are the same but we cannot reject the ## hypothesis that they lie on a line. ## ## @code{[@var{d}, @var{p}] = manova1 (@dots{})} returns P, a vector of p-values ## for testing the null hypothesis that the mean vectors of the groups lie on ## various dimensions. P(1) is the p-value for a test of dimension 0, P(2) for ## dimension 1, etc. ## ## @code{[@var{d}, @var{p}, @var{stats}] = manova1 (@dots{})} returns a STATS ## structure with the following fields: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab "W" @tab within-group sum of squares and products matrix ## @item @tab "B" @tab between-group sum of squares and products matrix ## @item @tab "T" @tab total sum of squares and products matrix ## @item @tab "dfW" @tab degrees of freedom for WSSP matrix ## @item @tab "dfB" @tab degrees of freedom for BSSP matrix ## @item @tab "dfT" @tab degrees of freedom for TSSP matrix ## @item @tab "lambda" @tab value of Wilk's lambda (the test statistic) ## @item @tab "chisq" @tab transformation of lambda to a chi-square distribution ## @item @tab "chisqdf" @tab degrees of freedom for chisq ## @item @tab "eigenval" @tab eigenvalues of (WSSP^-1) * BSSP ## @item @tab "eigenvec" @tab eigenvectors of (WSSP^-1) * BSSP; these are the ## coefficients for canonical variables, and they are scaled so the within-group ## variance of C is 1 ## @item @tab "canon" @tab canonical variables, equal to XC*eigenvec, where XC ## is X with columns centered by subtracting their means ## @item @tab "mdist" @tab Mahalanobis distance from each point to its group mean ## @item @tab "gmdist" @tab Mahalanobis distances between each pair of group means ## @item @tab "gnames" @tab Group names ## @end multitable ## ## The canonical variables C have the property that C(:,1) is the linear ## combination of the @var{x} columns that has the maximum separation between ## groups, C(:,2) has the maximum separation subject to it being orthogonal to ## C(:,1), and so on. ## ## @end deftypefn function [d, p, stats] = manova1 (x, group, alpha) ## Check input arguments narginchk(2,3) nargoutchk(1,3) ## Validate alpha value if parsed or add default if (nargin > 2) if (length (alpha) > 1 || ! isreal (alpha)) error ("manova1: Alpha must be a real scalar."); elseif (alpha <= 0 || alpha >= 1) error ("manova1: Alpha must be in the range (0,1)."); endif else alpha = 0.05; endif ## Convert group to cell array from character array if (ischar (group)) group = cellstr (group); endif ## Make group a column if (size (group, 1) == 1) group = group'; endif ## Check for equal size in samples between groups and data if (size (group, 1) != size (x, 1)) error("manova1: Samples in X and groups mismatch."); endif ## Remove samples (rows) in X and GROUP if there are missing values in X no_nan = (sum (isnan (x), 2) == 0); x = x(no_nan, :); group = group(no_nan, :); is_nan = ! no_nan; ## Get group names and indices [group_idx, group_names] = grp2idx (group); ngroups = length (group_names); ## Remove NaN values from updated GROUP no_nan = ! isnan (group_idx); if (! all (no_nan)) group_idx = group_idx(no_nan); x = x(no_nan,: ); is_nan(! is_nan) = ! no_nan; endif ## Get number of samples and variables [nsample, nvar] = size(x); realgroups = ismember(1:ngroups, group_idx); nrgroups = sum(realgroups); ## Calculate Total Sum of Squares and Products matrix xm = mean (x); x = x - xm; TSSP = x' * x; ## Calculate Within-samples Sum of Squares and Products matrix WSSP = zeros (size (TSSP)); for j = 1:ngroups row = find (group_idx == j); ## Only meaningful for groups with more than one samples if (length (row) > 1) group_x = x(row, :); group_x = group_x - mean (group_x); WSSP = WSSP + group_x' * group_x; endif endfor ## Calculate Between-samples Sum of Squares and Products matrix BSSP = TSSP - WSSP; ## Instead of simply computing `eig (BSSP / WSSP)` we use Matlab's technique ## with chol to insure v' * WSSP * v = I is met [R, p] = chol (WSSP); if (p > 0) error("manova1: Cannot factorize WSSP."); endif S = R' \ BSSP / R; ## Remove asymmetry caused by roundoff S = (S + S') / 2; [vv, ed] = eig (S); v = R \ vv; ## Sort in descending order [e,ei] = sort (diag (ed)); ## Check for valid eigevalues if (min(e) <= -1) error ("manova1: wrong value in eigenvector: singular sum of squares."); endif ## Compute Barlett's statistic for each dimension dims = 0:(min (nrgroups - 1, nvar) - 1); lambda = flipud (1 ./ cumprod (e + 1)); lambda = lambda(1 + dims); chistat = -(nsample - 1 - (nrgroups + nvar) / 2) .* log (lambda); chisqdf = ((nvar - dims) .* (nrgroups - 1 - dims))'; pp = 1 - chi2cdf (chistat, chisqdf); ## Get dimension where we can reject the null hypothesis d = dims(pp>alpha); if (length(d) > 0) d = d(1); else d = max(dims) + 1; end ## Create extra outputs as necessary if (nargout > 1) p = pp; endif if (nargout > 2) stats.W = WSSP; stats.B = BSSP; stats.T = TSSP; stats.dfW = nsample - nrgroups; stats.dfB = nrgroups - 1; stats.dfT = nsample - 1; stats.lambda = lambda; stats.chisq = chistat; stats.chisqdf = chisqdf; ## Reorder to increasing stats.eigenval = flipud(e); ## Flip so that it is in order of increasing eigenvalues v = v(:, flipud (ei)); ## Re-scale eigenvectors so the within-group variance is 1 vs = diag((v' * WSSP * v))' ./ (nsample - nrgroups); vs(vs<=0) = 1; v = v ./ repmat(sqrt(vs), size(v,1), 1); ## Flip sign so that the average element is positive j = (sum(v) < 0); v(:,j) = -v(:,j); stats.eigenvec = v; canon = x*v; if (any(is_nan)) tmp(~is_nan,:) = canon; tmp(is_nan,:) = NaN; stats.canon = tmp; else stats.canon = canon; endif ## Compute Mahalanobis distances from points to group means gmean = nan (ngroups, size (canon, 2)); gmean(realgroups,:) = grpstats (canon, group_idx); mdist = sum ((canon - gmean(group_idx,:)) .^ 2, 2); if (any (is_nan)) stats.mdist(! is_nan) = mdist; stats.mdist(is_nan) = NaN; else stats.mdist = mdist; endif ## Compute Mahalanobis distances between group means stats.gmdist = squareform (pdist (gmean)) .^ 2; stats.gnames = group_names; endif endfunction %!demo %! load carbig %! [d,p] = manova1([MPG, Acceleration, Weight, Displacement], Origin) %!test %! load carbig %! [d,p] = manova1([MPG, Acceleration, Weight, Displacement], Origin); %! assert (d, 3); %! assert (p, [0, 3.140583347827075e-07, 0.007510999577743149, ... %! 0.1934100745898493]', [1e-12, 1e-12, 1e-12, 1e-12]'); %!test %! load carbig %! [d,p] = manova1([MPG, Acceleration, Weight], Origin); %! assert (d, 2); %! assert (p, [0, 0.00516082975137544, 0.1206528056514453]', ... %! [1e-12, 1e-12, 1e-12]'); statistics-release-1.7.3/inst/manovacluster.m000066400000000000000000000067001475240274700213670ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} manovacluster (@var{stats}) ## @deftypefnx {statistics} {} manovacluster (@var{stats}, @var{method}) ## @deftypefnx {statistics} {@var{h} =} manovacluster (@var{stats}) ## @deftypefnx {statistics} {@var{h} =} manovacluster (@var{stats}, @var{method}) ## ## Cluster group means using manova1 output. ## ## @code{manovacluster (@var{stats})} draws a dendrogram showing the clustering ## of group means, calculated using the output STATS structure from ## @code{manova1} and applying the single linkage algorithm. See the ## @code{dendrogram} function for more information about the figure. ## ## @code{manovacluster (@var{stats}, @var{method})} uses the @var{method} ## algorithm in place of single linkage. The available methods are: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab "single" @tab --- nearest distance ## @item @tab "complete" @tab --- furthest distance ## @item @tab "average" @tab --- average distance ## @item @tab "centroid" @tab --- center of mass distance ## @item @tab "ward" @tab --- inner squared distance ## @end multitable ## ## @code{@var{h} = manovacluster (@dots{})} returns a vector of line handles. ## ## @seealso{manova1} ## @end deftypefn function h = manovacluster (stats, method) ## Check for valid input arguments narginchk (1, 2); if nargin > 1 valid_methods = {"single", "complete", "average", "centroid", "ward"}; if ! any (strcmpi (method, valid_methods)) error ("manovacluster: invalid method."); endif else method = "single"; end ## Get stats fields and create dendrogram dist = stats.gmdist; group_names = stats.gnames; [a, b] = meshgrid (1:length (dist)); hh = dendrogram (linkage (dist(a < b)', method), 0); ## Fix tick labels on x-axis oldlab = get (gca, "XTickLabel"); maxlen = max (cellfun ("length", group_names)); newlab = repmat(" ", size (oldlab, 1), maxlen); ng = size (group_names, 1); for j = 1:size (oldlab, 1) k = str2num (oldlab(j,:)); if (! isempty (k) & k > 0 & k <= ng) x = group_names{k,:}; newlab(j,1:length(x)) = x; endif endfor set(gca, "XtickLabel", newlab); ## Return plot handles if requested if nargout > 0 h = hh; endif endfunction %!demo %! load carbig %! X = [MPG Acceleration Weight Displacement]; %! [d, p, stats] = manova1 (X, Origin); %! manovacluster (stats) ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! load carbig %! X = [MPG Acceleration Weight Displacement]; %! [d, p, stats] = manova1 (X, Origin); %! manovacluster (stats); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error manovacluster (stats, "some"); statistics-release-1.7.3/inst/mcnemar_test.m000066400000000000000000000152551475240274700211720ustar00rootroot00000000000000## Copyright (C) 1996-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{chisq}] =} mcnemar_test (@var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{chisq}] =} mcnemar_test (@var{x}, @var{alpha}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{chisq}] =} mcnemar_test (@var{x}, @var{testtype}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{chisq}] =} mcnemar_test (@var{x}, @var{alpha}, @var{testtype}) ## ## Perform a McNemar's test on paired nominal data. ## ## @nospell{McNemar's} test is applied to a @math{2x2} contingency table @var{x} ## with a dichotomous trait, with matched pairs of subjects, of data ## cross-classified on the row and column variables to testing the null ## hypothesis of symmetry of the classification probabilities. More formally, ## the null hypothesis of marginal homogeneity states that the two marginal ## probabilities for each outcome are the same. ## ## Under the null, with a sufficiently large number of discordants ## (@qcode{@var{x}(1,2) + @var{x}(2,1) >= 25}), the test statistic, @var{chisq}, ## follows a chi-squared distribution with 1 degree of freedom. When the number ## of discordants is less than 25, then the mid-P exact McNemar test is used. ## ## @var{testtype} will force @code{mcnemar_test} to apply a particular method ## for testing the null hypothesis independently of the number of discordants. ## Valid options for @var{testtype}: ## @itemize ## @item @qcode{"asymptotic"} Original McNemar test statistic ## @item @qcode{"corrected"} Edwards' version with continuity correction ## @item @qcode{"exact"} An exact binomial test ## @item @qcode{"mid-p"} The mid-P McNemar test (mid-p binomial test) ## @end itemize ## ## The test decision is returned in @var{h}, which is 1 when the null hypothesis ## is rejected (@qcode{@var{pval} < @var{alpha}}) or 0 otherwise. @var{alpha} ## defines the critical value of statistical significance for the test. ## ## Further information about the McNemar's test can be found at ## @url{https://en.wikipedia.org/wiki/McNemar%27s_test} ## ## @seealso{crosstab, chi2test, fishertest} ## @end deftypefn function [h, pval, chisq] = mcnemar_test (x, varargin) ## Check for valid number of input arguments if (nargin > 3) error ("mcnemar_test: too many input arguments."); endif ## Check contigency table if (! isequal (size (x), [2, 2])) error ("mcnemar_test: X must be a 2x2 matrix."); elseif (! (all ((x(:) >= 0)) && all (x(:) == fix (x(:))))) error ("mcnemar_test: all entries of X must be non-negative integers."); endif ## Add defaults alpha = 0.05; b = x(1,2); c = x(2,1); if (b + c < 25) testtype = "mid-p"; else testtype = "asymptotic"; endif ## Parse optional arguments if (nargin == 2) if (isnumeric (varargin{1})) alpha = varargin{1}; elseif (ischar (varargin{1})) testtype = varargin{1}; else error ("mcnemar_test: invalid 2nd input argument."); endif elseif (nargin == 3) alpha = varargin{1}; testtype = varargin{2}; endif ## Check optional arguments if (! isscalar (alpha) || alpha <= 0 || alpha >= 1) error ("mcnemar_test: invalid value for ALPHA."); endif types = {"exact", "asymptotic", "mid-p", "corrected"}; if (! any (strcmpi (testtype, types))) error ("mcnemar_test: invalid value for TESTTYPE."); endif ## Calculate test switch (lower (testtype)) case "asymptotic" chisq = (b - c) .^2 / (b + c); pval = 1 - chi2cdf (chisq, 1); case "corrected" chisq = (abs (b - c) - 1) .^2 / (b + c); pval = 1 - chi2cdf (chisq, 1); case "exact" chisq = []; pval = 2 * (binocdf (b, b + c, 0.5)); case "mid-p" chisq = []; pval = 2 * (binocdf (b, b + c, 0.5)) - binopdf (b, b + c, 0.5); endswitch ## Get null hypothesis test result if (pval < alpha) h = 1; else h = 0; endif endfunction %!test %! [h, pval, chisq] = mcnemar_test ([101,121;59,33]); %! assert (h, 1); %! assert (pval, 3.8151e-06, 1e-10); %! assert (chisq, 21.356, 1e-3); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80]); %! assert (h, 1); %! assert (pval, 0.034690, 1e-6); %! assert (isempty (chisq), true); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80], 0.01); %! assert (h, 0); %! assert (pval, 0.034690, 1e-6); %! assert (isempty (chisq), true); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80], "mid-p"); %! assert (h, 1); %! assert (pval, 0.034690, 1e-6); %! assert (isempty (chisq), true); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80], "asymptotic"); %! assert (h, 1); %! assert (pval, 0.033006, 1e-6); %! assert (chisq, 4.5455, 1e-4); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80], "exact"); %! assert (h, 0); %! assert (pval, 0.052479, 1e-6); %! assert (isempty (chisq), true); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80], "corrected"); %! assert (h, 0); %! assert (pval, 0.055009, 1e-6); %! assert (chisq, 3.6818, 1e-4); %!test %! [h, pval, chisq] = mcnemar_test ([59,6;16,80], 0.1, "corrected"); %! assert (h, 1); %! assert (pval, 0.055009, 1e-6); %! assert (chisq, 3.6818, 1e-4); %!error mcnemar_test (59, 6, 16, 80) %!error mcnemar_test (ones (3, 3)) %!error ... %! mcnemar_test ([59,6;16,-80]) %!error ... %! mcnemar_test ([59,6;16,4.5]) %!error ... %! mcnemar_test ([59,6;16,80], {""}) %!error ... %! mcnemar_test ([59,6;16,80], -0.2) %!error ... %! mcnemar_test ([59,6;16,80], [0.05, 0.1]) %!error ... %! mcnemar_test ([59,6;16,80], 1) %!error ... %! mcnemar_test ([59,6;16,80], "") statistics-release-1.7.3/inst/mhsample.m000066400000000000000000000270021475240274700203100ustar00rootroot00000000000000## Copyright (C) 1995-2022 The Octave Project Developers ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{smpl}, @var{accept}] =} mhsample (@var{start}, @var{nsamples}, @var{property}, @var{value}, @dots{}) ## ## Draws @var{nsamples} samples from a target stationary distribution @var{pdf} ## using Metropolis-Hastings algorithm. ## ## Inputs: ## ## @itemize ## @item ## @var{start} is a @var{nchain} by @var{dim} matrix of starting points for each ## Markov chain. Each row is the starting point of a different chain and each ## column corresponds to a different dimension. ## ## @item ## @var{nsamples} is the number of samples, the length of each Markov chain. ## @end itemize ## ## Some property-value pairs can or must be specified, they are: ## ## (Required) One of: ## ## @itemize ## @item ## "pdf" @var{pdf}: a function handle of the target stationary distribution to ## be sampled. The function should accept different locations in each row and ## each column corresponds to a different dimension. ## ## or ## ## @item ## "logpdf" @var{logpdf}: a function handle of the log of the target stationary ## distribution to be sampled. The function should accept different locations ## in each row and each column corresponds to a different dimension. ## @end itemize ## ## In case optional argument @var{symmetric} is set to false (the default), one ## of: ## ## @itemize ## @item ## "proppdf" @var{proppdf}: a function handle of the proposal distribution that ## is sampled from with @var{proprnd} to give the next point in the chain. The ## function should accept two inputs, the random variable and the current ## location each input should accept different locations in each row and each ## column corresponds to a different dimension. ## ## or ## ## @item ## "logproppdf" @var{logproppdf}: the log of "proppdf". ## @end itemize ## ## The following input property/pair values may be needed depending on the ## desired outut: ## ## @itemize ## @item ## "proprnd" @var{proprnd}: (Required) a function handle which generates random ## numbers from @var{proppdf}. The function should accept different locations ## in each row and each column corresponds to a different dimension ## corresponding with the current location. ## ## @item ## "symmetric" @var{symmetric}: true or false based on whether @var{proppdf} is ## a symmetric distribution. If true, @var{proppdf} (or @var{logproppdf}) need ## not be specified. The default is false. ## ## @item ## "burnin" @var{burnin} the number of points to discard at the beginning, the ## default is 0. ## ## @item ## "thin" @var{thin}: omits @var{thin}-1 of every @var{thin} points in the ## generated Markov chain. The default is 1. ## ## @item ## "nchain" @var{nchain}: the number of Markov chains to generate. The default ## is 1. ## @end itemize ## ## Outputs: ## ## @itemize ## @item ## @var{smpl}: a @var{nsamples} x @var{dim} x @var{nchain} tensor of random ## values drawn from @var{pdf}, where the rows are different random values, the ## columns correspond to the dimensions of @var{pdf}, and the third dimension ## corresponds to different Markov chains. ## ## @item ## @var{accept} is a vector of the acceptance rate for each chain. ## @end itemize ## ## Example : Sampling from a normal distribution ## ## @example ## @group ## start = 1; ## nsamples = 1e3; ## pdf = @@(x) exp (-.5 * x .^ 2) / (pi ^ .5 * 2 ^ .5); ## proppdf = @@(x,y) 1 / 6; ## proprnd = @@(x) 6 * (rand (size (x)) - .5) + x; ## [smpl, accept] = mhsample (start, nsamples, "pdf", pdf, "proppdf", ... ## proppdf, "proprnd", proprnd, "thin", 4); ## histfit (smpl); ## @end group ## @end example ## ## @seealso{rand, slicesample} ## @end deftypefn function [smpl, accept] = mhsample (start, nsamples, varargin) if (nargin < 6) print_usage (); endif sizestart = size (start); pdf = []; proppdf = []; logpdf = []; logproppdf = []; proprnd = []; sym = false; K = 0; # burnin m = 1; # thin nchain = 1; for k = 1:2:length (varargin) if (ischar (varargin{k})) switch lower(varargin{k}) case "pdf" if (isa (varargin{k+1}, "function_handle")) pdf = varargin{k+1}; else error ("mhsample: pdf must be a function handle"); endif case "proppdf" if (isa (varargin{k+1}, "function_handle")) proppdf = varargin{k+1}; else error ("mhsample: proppdf must be a function handle"); endif case "logpdf" if (isa (varargin{k+1}, "function_handle")) pdf = varargin{k+1}; else error ("mhsample: logpdf must be a function handle"); endif case "logproppdf" if (isa (varargin{k+1}, "function_handle")) proppdf = varargin{k+1}; else error ("mhsample: logproppdf must be a function handle"); endif case "proprnd" if (isa (varargin{k+1}, "function_handle")) proprnd = varargin{k+1}; else error ("mhsample: proprnd must be a function handle"); endif case "symmetric" if (isa (varargin{k+1}, "logical")) sym = varargin{k+1}; else error ("mhsample: sym must be true or false"); endif case "burnin" if (varargin{k+1}>=0) K = varargin{k+1}; else error ("mhsample: K must be greater than or equal to 0"); endif case "thin" if (varargin{k+1} >= 1) m = varargin{k+1}; else error ("mhsample: m must be greater than or equal to 1"); endif case "nchain" if (varargin{k+1} >= 1) nchain = varargin{k+1}; else error ("mhsample: nchain must be greater than or equal to 1"); endif otherwise warning (["mhsample: Ignoring unknown option " varargin{k}]); endswitch else error (["mhsample: " varargin{k} " is not a valid property."]); endif endfor if (! isempty (pdf) && isempty (logpdf)) logpdf=@(x) rloge (pdf (x)); elseif (isempty (pdf) && isempty (logpdf)) error ("mhsample: pdf or logpdf must be input."); endif if (! isempty (proppdf) && isempty (logproppdf)) logproppdf = @(x, y) rloge (proppdf (x, y)); elseif (isempty (proppdf) && isempty (logproppdf) && ! sym) error ("mhsample: proppdf or logproppdf must be input unless 'symetrical' is true."); endif if (! isa (proprnd, "function_handle")) error ("mhsample: proprnd must be a function handle."); endif if (length (sizestart) == 2) sizestart = [sizestart 0]; end smpl = zeros (nsamples, sizestart(2), nchain); if (all (sizestart([1 3]) == [1 nchain])) ## Could remove, not Matlab compatable but allows continuing chains smpl(1, :, :) = start; elseif (all (sizestart([1 3]) == [nchain 0])) smpl(1, :, :) = permute (start, [3, 2, 1]); elseif (all (sizestart([1 3]) == [1 0])) ## Could remove, not Matlab compatable but allows all chains to start ## at the same location smpl(1, :, :) = repmat (start,[1, 1, nchain]); else error ("mhsample: start must be a nchain by dim matrix."); endif cx = permute (smpl(1, :, :),[3, 2, 1]); accept = zeros (nchain, 1); i = 1; rnd = log (rand (nchain, nsamples*m+K)); for k = 1:nsamples*m+K canacc = rem (k-K, m) == 0; px = proprnd (cx); if (sym) A = logpdf (px) - logpdf(cx); else A = (logpdf (px) + logproppdf (cx, px)) - (logpdf (cx) + logproppdf (px, cx)); endif ac = rnd(:, k) < min (A, 0); cx(ac, :) = px(ac, :); accept(ac)++; if (canacc) smpl(i, :, :) = permute (cx, [3, 2, 1]); end if (k > K && canacc) i++; endif endfor accept ./= (nsamples * m + K); endfunction function y = rloge (x) y = -inf (size (x)); xg0 = x > 0; y(xg0) = log (x(xg0)); endfunction %!demo %! ## Define function to sample %! d = 2; %! mu = [-1; 2]; %! rand ("seed", 5) # for reproducibility %! Sigma = rand (d); %! Sigma = (Sigma + Sigma'); %! Sigma += eye (d) * abs (eigs (Sigma, 1, "sa")) * 1.1; %! pdf = @(x)(2*pi)^(-d/2)*det(Sigma)^-.5*exp(-.5*sum((x.'-mu).*(Sigma\(x.'-mu)),1)); %! ## Inputs %! start = ones (1, 2); %! nsamples = 500; %! sym = true; %! K = 500; %! m = 10; %! rand ("seed", 8) # for reproducibility %! proprnd = @(x) (rand (size (x)) - .5) * 3 + x; %! [smpl, accept] = mhsample (start, nsamples, "pdf", pdf, "proprnd", proprnd, ... %! "symmetric", sym, "burnin", K, "thin", m); %! figure; %! hold on; %! plot (smpl(:, 1), smpl(:, 2), 'x'); %! [x, y] = meshgrid (linspace (-6, 4), linspace(-3, 7)); %! z = reshape (pdf ([x(:), y(:)]), size(x)); %! mesh (x, y, z, "facecolor", "None"); %! ## Using sample points to find the volume of half a sphere with radius of .5 %! f = @(x) ((.25-(x(:,1)+1).^2-(x(:,2)-2).^2).^.5.*(((x(:,1)+1).^2+(x(:,2)-2).^2)<.25)).'; %! int = mean (f (smpl) ./ pdf (smpl)); %! errest = std (f (smpl) ./ pdf (smpl)) / nsamples ^ .5; %! trueerr = abs (2 / 3 * pi * .25 ^ (3 / 2) - int); %! printf ("Monte Carlo integral estimate int f(x) dx = %f\n", int); %! printf ("Monte Carlo integral error estimate %f\n", errest); %! printf ("The actual error %f\n", trueerr); %! mesh (x, y, reshape (f([x(:), y(:)]), size(x)), "facecolor", "None"); %!demo %! ## Integrate truncated normal distribution to find normilization constant %! pdf = @(x) exp (-.5*x.^2)/(pi^.5*2^.5); %! nsamples = 1e3; %! rand ("seed", 5) # for reproducibility %! proprnd = @(x) (rand (size (x)) - .5) * 3 + x; %! [smpl, accept] = mhsample (1, nsamples, "pdf", pdf, "proprnd", proprnd, ... %! "symmetric", true, "thin", 4); %! f = @(x) exp(-.5 * x .^ 2) .* (x >= -2 & x <= 2); %! x = linspace (-3, 3, 1000); %! area(x, f(x)); %! xlabel ('x'); %! ylabel ('f(x)'); %! int = mean (f (smpl) ./ pdf (smpl)); %! errest = std (f (smpl) ./ pdf (smpl)) / nsamples^ .5; %! trueerr = abs (erf (2 ^ .5) * 2 ^ .5 * pi ^ .5 - int); %! printf ("Monte Carlo integral estimate int f(x) dx = %f\n", int); %! printf ("Monte Carlo integral error estimate %f\n", errest); %! printf ("The actual error %f\n", trueerr); ## Test output %!test %! nchain = 1e4; %! start = rand (nchain, 1); %! nsamples = 1e3; %! pdf = @(x) exp (-.5*(x-1).^2)/(2*pi)^.5; %! proppdf = @(x, y) 1/3; %! proprnd = @(x) 3 * (rand (size (x)) - .5) + x; %! [smpl, accept] = mhsample (start, nsamples, "pdf", pdf, "proppdf", proppdf, ... %! "proprnd", proprnd, "thin", 2, "nchain", nchain, ... %! "burnin", 0); %! assert (mean (mean (smpl, 1), 3), 1, .01); %! assert (mean (var (smpl, 1), 3), 1, .01) ## Test input validation %!error mhsample (); %!error mhsample (1); %!error mhsample (1, 1); %!error mhsample (1, 1, "pdf", @(x)x); %!error mhsample (1, 1, "pdf", @(x)x, "proprnd", @(x)x+rand(size(x))); statistics-release-1.7.3/inst/mnrfit.m000066400000000000000000000233711475240274700200060ustar00rootroot00000000000000## Copyright (C) 2024 Andrew C Penn ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{B} =} mnrfit (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{B} =} mnrfit (@var{X}, @var{Y}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{B}, @var{dev}] =} mnrrfit (@dots{}) ## @deftypefnx {statistics} {[@var{B}, @var{dev}, @var{stats}] =} mnrfit (@dots{}) ## ## Perform logistic regression for binomial responses or multiple ordinal ## responses. ## ## Note: This function is currently a wrapper for the @code{logistic_regression} ## function. It can only be used for fitting an ordinal logistic model and a ## nominal model with 2 categories (which is an ordinal case). Hierarchical ## models as well as nominal model with more than two classes are not currently ## supported. This function is a work in progress. ## ## @code{@var{B} = mnrfit (@var{X}, @var{Y})} returns a matrix, @var{B}, of ## coefficient estimates for a multinomial logistic regression of the nominal ## responses in @var{Y} on the predictors in @var{X}. @var{X} is an @math{NxP} ## numeric matrix the observations on predictor variables, where @math{N} ## corresponds to the number of observations and @math{P} corresponds to ## predictor variables. @var{Y} contains the response category labels and it ## either be an @math{NxP} categorical or numerical matrix (containing only 1s ## and 0s) or an @math{Nx1} numeric vector with positive integer values, a cell ## array of character vectors and a logical vector. @var{Y} can also be defined ## as a character matrix with each row corresponding to an observation of ## @var{X}. ## ## @code{@var{B} = mnrfit (@var{X}, @var{Y}, @var{name}, @var{value})} returns a ## matrix, @var{B}, of coefficient estimates for a multinomial model fit with ## additional parameterss specified @qcode{Name-Value} pair arguments. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"model"} @tab @tab Specifies the type of model to fit. ## Currently, only @qcode{"ordinal"} is fully supported. @qcode{"nominal"} is ## only supported for 2 classes in @var{Y}. ## ## @item @qcode{"display"} @tab @tab A flag to enable/disable displaying ## information about the fitted model. Default is @qcode{"off"}. ## @end multitable ## ## @code{[@var{B}, @var{dev}, @var{stats}] = mnrfit (@dots{}} also returns the ## deviance of the fit, @var{dev}, and the structure @var{stats} for any of the ## previous input arguments. @var{stats} currently only returns values for the ## fields @qcode{"beta"}, same as @var{B}, @qcode{"coeffcorr"}, the estimated ## correlation matrix for @var{B}, @qcode{"covd"}, the estimated covariance ## matrix for @var{B}, and @qcode{"se"}, the standard errors of the coefficient ## estimates @var{B}. ## ## @seealso{logistic_regression} ## @end deftypefn function [B, DEV, STATS] = mnrfit (X, Y, varargin) ## Check input arguments X and Y if (nargin < 2) error ("mnrfit: too few input arguments."); endif if (! isnumeric (X)) error ("mnrfit: Predictors must be numeric.") endif if (isscalar (X) || (ndims (X) > 2)) error ("mnrfit: Predictors must be a vector or a 2D matrix.") endif if (isscalar (Y) || (ndims (Y) > 2)) error ("mnrfit: Response must be a vector or a 2D matrix.") endif [N, P] = size (X); [n, K] = size (Y); if (N == 1) ## if X is a row vector, make it a column vector X = X(:); N = P; P = 1; endif if (n != N) error ("mnrfit: Y must have the same number of rows as X.") endif if (! (isnumeric (Y) || islogical (Y) || ischar (Y) || iscellstr (Y))) error (strcat (["mnrfit: Response labels must be a character array,"], ... [" a cell vector of strings, \nor a vector or"], ... [" matrix of doubles, singles or logical values."])); endif ## Check supplied parameters if (mod (numel (varargin), 2) != 0) error ("mnrfit: optional arguments must be in pairs.") endif MODELTYPE = "nominal"; DISPLAY = "off"; while (numel (varargin) > 0) name = varargin{1}; value = varargin{2}; switch (lower (name)) case "model" MODELTYPE = value; case "display" DISPLAY = value; otherwise warning (sprintf ("mnrfit: parameter %s will be ignored", name)); endswitch varargin (1:2) = []; endwhile ## Evaluate display input argument switch (lower (DISPLAY)) case "on" dispopt = true; case "off" dispopt = false; endswitch ## Categorize Y if it is a cellstring array if (iscellstr (Y)) if (K > 1) error ("mnrfit: Y must be a column vector when given as cellstr."); endif ## Get groups in Y [YN, ~, UY] = grp2idx (Y); # this will also catch "" as missing values ## Remove missing values from X and Y RowsUsed = ! logical (sum (isnan ([X, YN]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [YN, ~, UY] = grp2idx (Y); # in case a category is removed due to NaNs in X n = numel (UY); endif ## Categorize Y if it is a character array if (ischar (Y)) ## Get groups in Y [YN, ~, UY] = grp2idx (Y); # this will also catch "" as missing values ## Remove missing values from X and Y RowsUsed = ! logical (sum (isnan ([X, YN]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); ## Renew groups in Y [YN, ~, UY] = grp2idx (Y); # in case a category is removed due to NaNs in X n = numel (UY); endif if (K > 1) ## So far, if K > 1, Y must be a matrix of logical, singles or doubles if (! all (all (Y == 0 | Y == 1))) error ("mnrfit: Y must contain only 1 and 0 when given as a 2D matrix."); endif ## Convert Y to a vector of positive integer categories Y = sum (bsxfun (@times, (1:K), Y), 2); endif ## Categorize Y in all other cases if (! iscellstr (Y)) RowsUsed = ! logical (sum (isnan ([X, Y]), 2)); Y = Y (RowsUsed); X = X (RowsUsed, :); [UY, ~, YN] = unique (Y); ## find unique categories in the response n = numel (UY); ## number of unique response categories endif if (isnumeric (Y)) if (! (all (Y > 0) && all (fix (Y) == Y))) error ("mnrfit: Y must contain positive integer category numbers.") endif endif ## Evaluate model type input argument switch (lower (MODELTYPE)) case "nominal" if (n > 2) error ("mnrfit: fitting more than 2 nominal responses not supported."); else ## Y has two responses. Ordinal logistic regression can be used to fit ## models with binary nominal responses endif case "ordinal" ## Do nothing, ordinal responses are fully supported case "hierarchical" error ("mnrfit: fitting hierarchical responses not supported."); otherwise error ("mnrfit: model type not recognised."); endswitch ## Perform fit and reformat output [INTERCEPT, SLOPE, DEV, ~, ~, ~, S] = logistic_regression (YN - 1, X, dispopt); B = cat (1, INTERCEPT, SLOPE); STATS = struct ("beta", B, ... "dfe", [], ... ## Not used "s", [], ... ## Not used "sfit", [], ... ## Not used "estdisp", [], ... ## Not used "coeffcorr", S.coeffcorr, ... "covb", S.cov, ... "se", S.se, ... "t", [], ... ## Not used "p", [], ... ## Not used "resid", [], ... ## Not used "residp", [], ... ## Not used "residd", []); ## Not used endfunction ## Test input validation %!error mnrfit (ones (50,1)) %!error ... %! mnrfit ({1 ;2 ;3 ;4 ;5}, ones (5,1)) %!error ... %! mnrfit (ones (50, 4, 2), ones (50, 1)) %!error ... %! mnrfit (ones (50, 4), ones (50, 1, 3)) %!error ... %! mnrfit (ones (50, 4), ones (45,1)) %!error ... %! mnrfit (ones (5, 4), {1 ;2 ;3 ;4 ;5}) %!error ... %! mnrfit (ones (5, 4), ones (5, 1), "model") %!error ... %! mnrfit (ones (5, 4), {"q","q";"w","w";"q","q";"w","w";"q","q"}) %!error ... %! mnrfit (ones (5, 4), [1, 2; 1, 2; 1, 2; 1, 2; 1, 2]) %!error ... %! mnrfit (ones (5, 4), [1; -1; 1; 2; 1]) %!error ... %! mnrfit (ones (5, 4), [1; 2; 3; 2; 1], "model", "nominal") %!error ... %! mnrfit (ones (5, 4), [1; 2; 3; 2; 1], "model", "hierarchical") %!error ... %! mnrfit (ones (5, 4), [1; 2; 3; 2; 1], "model", "whatever") statistics-release-1.7.3/inst/monotone_smooth.m000066400000000000000000000146201475240274700217330ustar00rootroot00000000000000## Copyright (C) 2011 Nir Krakauer ## Copyright (C) 2011 Carnë Draug ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{yy} =} monotone_smooth (@var{x}, @var{y}, @var{h}) ## ## Produce a smooth monotone increasing approximation to a sampled functional ## dependence. ## ## A kernel method is used (an Epanechnikov smoothing kernel is applied to y(x); ## this is integrated to yield the monotone increasing form. See Reference 1 ## for details.) ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{x} is a vector of values of the independent variable. ## ## @item ## @var{y} is a vector of values of the dependent variable, of the same size as ## @var{x}. For best performance, it is recommended that the @var{y} already be ## fairly smooth, e.g. by applying a kernel smoothing to the original values if ## they are noisy. ## ## @item ## @var{h} is the kernel bandwidth to use. If @var{h} is not given, ## a "reasonable" value is computed. ## ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{yy} is the vector of smooth monotone increasing function values at ## @var{x}. ## ## @end itemize ## ## @subheading Examples ## ## @example ## @group ## x = 0:0.1:10; ## y = (x .^ 2) + 3 * randn(size(x)); # typically non-monotonic from the added ## noise ## ys = ([y(1) y(1:(end-1))] + y + [y(2:end) y(end)])/3; # crudely smoothed via ## moving average, but still typically non-monotonic ## yy = monotone_smooth(x, ys); # yy is monotone increasing in x ## plot(x, y, '+', x, ys, x, yy) ## @end group ## @end example ## ## @subheading References ## ## @enumerate ## @item ## Holger Dette, Natalie Neumeyer and Kay F. Pilz (2006), A simple nonparametric ## estimator of a strictly monotone regression function, @cite{Bernoulli}, ## 12:469-490 ## @item ## Regine Scheder (2007), R Package 'monoProc', Version 1.0-6, ## @url{http://cran.r-project.org/web/packages/monoProc/monoProc.pdf} (The ## implementation here is based on the monoProc function mono.1d) ## @end enumerate ## @end deftypefn function yy = monotone_smooth (x, y, h) if (nargin < 2 || nargin > 3) print_usage (); elseif (! isnumeric (x) || ! isvector (x)) error ("monotone_smooth: X must be a numeric vector."); elseif (! isnumeric (y) || ! isvector (y)) error ("monotone_smooth: Y must be a numeric vector."); elseif (numel (x) != numel (y)) error ("monotone_smooth: X and Y must have the same number of elements."); elseif (nargin == 3 && (! isscalar (h) || ! isnumeric (h))) error ("monotone_smooth: H (kernel bandwith) must a numeric scalar."); endif n = numel (x); ## Set filter bandwidth at a reasonable default value, if not specified if (nargin != 3) s = std (x); h = s / (n ^ 0.2); end x_min = min(x); x_max = max(x); y_min = min(y); y_max = max(y); ## Transform range of X to [0, 1] xl = (x - x_min) / (x_max - x_min); yy = ones(size(y)); ## Epanechnikov smoothing kernel (with finite support) ## K_epanech_kernel = @(z) (3/4) * ((1 - z).^2) .* (abs(z) < 1); K_epanech_int = @(z) mean(((abs(z) < 1)/2) - (3/4) * (z .* (abs(z) < 1) ... - (1/3) * (z.^3) .* (abs(z) < 1)) + (z < -1)); ## Integral of kernels up to t monotone_inverse = @(t) K_epanech_int((y - t) / h); ## Find the value of the monotone smooth function at each point in X niter_max = 150; # maxIter for estimating each value (adequate for most cases) for l = 1:n tmax = y_max; tmin = y_min; wmin = monotone_inverse(tmin); wmax = monotone_inverse(tmax); if (wmax == wmin) yy(l) = tmin; else wt = xl(l); iter_max_reached = 1; for i = 1:niter_max wt_scaled = (wt - wmin) / (wmax - wmin); tn = tmin + wt_scaled * (tmax - tmin) ; wn = monotone_inverse(tn); wn_scaled = (wn - wmin) / (wmax - wmin); ## if (abs(wt-wn) < 1E-4) || (tn < (y_min-0.1)) || (tn > (y_max+0.1)) ## criterion for break in the R code -- replaced by the following line ## to hopefully be less dependent on the scale of y if ((abs(wt_scaled-wn_scaled) < 1E-4) || (wt_scaled < -0.1) || (wt_scaled > 1.1)) iter_max_reached = 0; break endif if (wn > wt) tmax = tn; wmax = wn; else tmin = tn; wmin = wn; endif endfor if (iter_max_reached) msg = sprintf (strcat (["at x = %%g, maximum number of iterations"], ... [" %%d reached without convergence;"], ... [" approximation may not be optimal"])); warning (msg, x(l), niter_max) endif yy(l) = tmin + (wt - wmin) * (tmax - tmin) / (wmax - wmin); endif endfor endfunction ## Test input validation %!error ... %! monotone_smooth (1) %!error ... %! monotone_smooth ("char", 1) %!error ... %! monotone_smooth ({1,2,3}, 1) %!error ... %! monotone_smooth (ones(20,3), 1) %!error ... %! monotone_smooth (1, "char") %!error ... %! monotone_smooth (1, {1,2,3}) %!error ... %! monotone_smooth (1, ones(20,3)) %!error monotone_smooth (ones (10,1), ones(10,1), [1, 2]) %!error monotone_smooth (ones (10,1), ones(10,1), {2}) %!error monotone_smooth (ones (10,1), ones(10,1), "char") statistics-release-1.7.3/inst/multcompare.m000066400000000000000000001467131475240274700210450ustar00rootroot00000000000000## Copyright (C) 2022 Andrew Penn ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{C} =} multcompare (@var{STATS}) ## @deftypefnx {statistics} {@var{C} =} multcompare (@var{STATS}, "name", @var{value}) ## @deftypefnx {statistics} {[@var{C}, @var{M}] =} multcompare (...) ## @deftypefnx {statistics} {[@var{C}, @var{M}, @var{H}] =} multcompare (...) ## @deftypefnx {statistics} {[@var{C}, @var{M}, @var{H}, @var{GNAMES}] =} multcompare (...) ## @deftypefnx {statistics} {@var{padj} =} multcompare (@var{p}) ## @deftypefnx {statistics} {@var{padj} =} multcompare (@var{p}, "ctype", @var{CTYPE}) ## ## Perform posthoc multiple comparison tests or p-value adjustments to control ## the family-wise error rate (FWER) or false discovery rate (FDR). ## ## @code{@var{C} = multcompare (@var{STATS})} performs a multiple comparison ## using a @var{STATS} structure that is obtained as output from any of ## the following functions: anova1, anova2, anovan, kruskalwallis, and friedman. ## The return value @var{C} is a matrix with one row per comparison and six ## columns. Columns 1-2 are the indices of the two samples being compared. ## Columns 3-5 are a lower bound, estimate, and upper bound for their ## difference, where the bounds are for 95% confidence intervals. Column 6-8 are ## the multiplicity adjusted p-values for each individual comparison, the test ## statistic and the degrees of freedom. ## All tests by multcompare are two-tailed. ## ## @qcode{multcompare} can take a number of optional parameters as name-value ## pairs. ## ## @code{[@dots{}] = multcompare (@var{STATS}, "alpha", @var{ALPHA})} ## ## @itemize ## @item ## @var{ALPHA} sets the significance level of null hypothesis significance ## tests to ALPHA, and the central coverage of two-sided confidence intervals to ## 100*(1-@var{ALPHA})%. (Default ALPHA is 0.05). ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "ControlGroup", @var{REF})} ## ## @itemize ## @item ## @var{REF} is the index of the control group to limit comparisons to. The ## index must be a positive integer scalar value. For each dimension (d) listed ## in @var{DIM}, multcompare uses STATS.grpnames@{d@}(idx) as the control group. ## (Default is empty, i.e. [], for full pairwise comparisons) ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "ctype", @var{CTYPE})} ## ## @itemize ## @item ## @var{CTYPE} is the type of comparison test to use. In order of increasing ## power, the choices are: "bonferroni", "scheffe", "mvt", "holm" (default), ## "hochberg", "fdr", or "lsd". The first five methods control the family-wise ## error rate. The "fdr" method controls false discovery rate (by the original ## Benjamini-Hochberg step-up procedure). The final method, "lsd" (or "none"), ## makes no attempt to control the Type 1 error rate of multiple comparisons. ## The coverage of confidence intervals are only corrected for multiple ## comparisons in the cases where @var{CTYPE} is "bonferroni", "scheffe" or ## "mvt", which control the Type 1 error rate for simultaneous inference. ## ## The "mvt" method uses the multivariate t distribution to assess the ## probability or critical value of the maximum statistic across the tests, ## thereby accounting for correlations among comparisons in the control of the ## family-wise error rate with simultaneous inference. In the case of pairwise ## comparisons, it simulates Tukey's (or the Games-Howell) test, in the case of ## comparisons with a single control group, it simulates Dunnett's test. ## @var{CTYPE} values "tukey-kramer" and "hsd" are recognised but set the value ## of @var{CTYPE} and @var{REF} to "mvt" and empty respectively. A @var{CTYPE} ## value "dunnett" is recognised but sets the value of @var{CTYPE} to "mvt", and ## if @var{REF} is empty, sets @var{REF} to 1. Since the algorithm uses a Monte ## Carlo method (of 1e+06 random samples), you can expect the results to ## fluctuate slightly with each call to multcompare and the calculations may be ## slow to complete for a large number of comparisons. If the parallel package ## is installed and loaded, @qcode{multcompare} will automatically accelerate ## computations by parallel processing. Note that p-values calculated by the ## "mvt" are truncated at 1e-06. ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "df", @var{DF})} ## ## @itemize ## @item ## @var{DF} is an optional scalar value to set the number of degrees of freedom ## in the calculation of p-values for the multiple comparison tests. By default, ## this value is extracted from the @var{STATS} structure of the ANOVA test, but ## setting @var{DF} maybe necessary to approximate Satterthwaite correction if ## @qcode{anovan} was performed using weights. ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "dim", @var{DIM})} ## ## @itemize ## @item ## @var{DIM} is a vector specifying the dimension or dimensions over which the ## estimated marginal means are to be calculated. Used only if STATS comes from ## anovan. The value [1 3], for example, computes the estimated marginal mean ## for each combination of the first and third predictor values. The default is ## to compute over the first dimension (i.e. 1). If the specified dimension is, ## or includes, a continuous factor then @qcode{multcompare} will return an ## error. ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "estimate", @var{ESTIMATE})} ## ## @itemize ## @item ## @var{ESTIMATE} is a string specifying the estimates to be compared when ## computing multiple comparisons after anova2; this argument is ignored by ## anovan and anova1. Accepted values for @var{ESTIMATE} are either "column" ## (default) to compare column means, or "row" to compare row means. If the ## model type in anova2 was "linear" or "nested" then only "column" is accepted ## for @var{ESTIMATE} since the row factor is assumed to be a random effect. ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "display", @var{DISPLAY})} ## ## @itemize ## @item ## @var{DISPLAY} is either "on" (the default): to display a table and graph of ## the comparisons (e.g. difference between means), their 100*(1-@var{ALPHA})% ## intervals and multiplicity adjusted p-values in APA style; or "off": to omit ## the table and graph. On the graph, markers and error bars colored red have ## multiplicity adjusted p-values < ALPHA, otherwise the markers and error bars ## are blue. ## @end itemize ## ## @code{[@dots{}] = multcompare (@var{STATS}, "seed", @var{SEED})} ## ## @itemize ## @item ## @var{SEED} is a scalar value used to initialize the random number generator ## so that @var{CTYPE} "mvt" produces reproducible results. ## @end itemize ## ## @code{[@var{C}, @var{M}, @var{H}, @var{GNAMES}] = multcompare (@dots{})} ## returns additional outputs. @var{M} is a matrix where columns 1-2 are the ## estimated marginal means and their standard errors, and columns 3-4 are lower ## and upper bounds of the confidence intervals for the means; the critical ## value of the test statistic is scaled by a factor of 2^(-0.5) before ## multiplying by the standard errors of the group means so that the intervals ## overlap when the difference in means becomes significant at approximately ## the level @var{ALPHA}. When @var{ALPHA} is 0.05, this corresponds to ## confidence intervals with 83.4% central coverage. @var{H} is a handle to the ## figure containing the graph. @var{GNAMES} is a cell array with one row for ## each group, containing the names of the groups. ## ## @code{@var{padj} = multcompare (@var{p})} calculates and returns adjusted ## p-values (@var{padj}) using the Holm-step down Bonferroni procedure to ## control the family-wise error rate. ## ## @code{@var{padj} = multcompare (@var{p}, "ctype", @var{CTYPE})} calculates ## and returns adjusted p-values (@var{padj}) computed using the method ## @var{CTYPE}. In order of increasing power, @var{CTYPE} for p-value adjustment ## can be either "bonferroni", "holm" (default), "hochberg", or "fdr". See ## above for further information about the @var{CTYPE} methods. ## ## @seealso{anova1, anova2, anovan, kruskalwallis, friedman, fitlm} ## @end deftypefn function [C, M, H, GNAMES] = multcompare (STATS, varargin) if (nargin < 1) error (strcat (["multcompare usage: ""multcompare (ARG)""; "], ... [" atleast 1 input argument required"])); endif ## Check supplied parameters if ((numel (varargin) / 2) != fix (numel (varargin) / 2)) error ("multcompare: wrong number of arguments.") endif ALPHA = 0.05; REF = []; CTYPE = "holm"; DISPLAY = "on"; DIM = 1; ESTIMATE = "column"; DFE = []; for idx = 3:2:nargin name = varargin{idx-2}; value = varargin{idx-1}; switch (lower (name)) case "alpha" ALPHA = value; case {"controlgroup","ref"} REF = value; case {"ctype","criticalvaluetype"} CTYPE = lower (value); case {"display","displayopt"} DISPLAY = lower (value); case {"dim","dimension"} DIM = value; case "estimate" ESTIMATE = lower (value); case {"df","dfe"} DFE = value; case {"seed"} SEED = value; ## Set random seed for mvtrnd and mvnrnd randn ('seed', SEED); randg ('seed', SEED); otherwise error (sprintf ("multcompare: parameter %s is not supported", name)); endswitch endfor ## Evaluate ALPHA input argument if (! isa (ALPHA,"numeric") || numel (ALPHA) != 1) error("anovan:alpha must be a numeric scalar value"); endif if ((ALPHA <= 0) || (ALPHA >= 1)) error("anovan: alpha must be a value between 0 and 1"); endif ## Evaluate CTYPE input argument if (ismember (CTYPE, {"tukey-kramer", "hsd"})) CTYPE = "mvt"; REF = []; elseif (strcmp (CTYPE, "dunnett")) CTYPE = "mvt"; if (isempty (REF)) REF = 1; endif elseif (strcmp (CTYPE, "none")) CTYPE = "lsd"; endif if (! ismember (CTYPE, ... {"bonferroni","scheffe","mvt","holm","hochberg","fdr","lsd"})) error ("multcompare: '%s' is not a supported value for CTYPE", CTYPE) endif ## Evaluate DFE input argument if (! isempty (DFE)) if (! isscalar (DFE)) error ("multcompare: df must be a scalar value."); endif if (!(DFE > 0) || isinf (DFE)) error ("multcompare: df must be a positive finite value."); endif endif ## If STATS is numeric, assume it is a vector of p-values if (isnumeric (STATS)) if (nargout > 1) error (strcat (["multcompare: invalid number of output arguments"], ... [" if only used to adjust p-values"])) endif if (!isempty (varargin)) if (!any (strcmpi (varargin{1}, {"ctype","criticalvaluetype"})) ... || (nargin > 3) ) error (strcat(["multcompare: invalid input arguments if only"], ... [" used to adjust p-values"])) endif endif if (! ismember (CTYPE, {"bonferroni","holm","hochberg","fdr"})) error ("multcompare: '%s' is not a supported p-adjustment method", CTYPE) endif p = STATS; if (all (size (p) > 1)) error ("multcompare: p-values must be a vector") endif padj = feval (CTYPE, p); if (size (p, 1) > 1) C = padj; else C = padj'; endif return endif ## Perform test specific calculations switch (STATS.source) case "anova1" ## Make matrix of requested comparisons (pairs) ## Also return the corresponding hypothesis matrix (L) n = STATS.n(:); Ng = numel (n); if (isempty (REF)) ## Pairwise comparisons [pairs, L] = pairwise (Ng); else ## Treatment vs. Control comparisons [pairs, L] = trt_vs_ctrl (Ng, REF); endif Np = size (pairs, 1); switch (STATS.vartype) case "equal" ## Calculate estimated marginal means and their standard errors gmeans = STATS.means(:); gvar = (STATS.s^2) ./ n; # Sampling variance gcov = diag (gvar); Ng = numel (gmeans); M = zeros (Ng, 4); M(:,1:2) = cat (2, gmeans, sqrt(gvar)); ## Get the error degrees of freedom from anova1 output if (isempty (DFE)) DFE = STATS.df; endif case "unequal" ## Error checking if (strcmp (CTYPE, "scheffe")) error (strcat (["multcompare: the CTYPE value 'scheffe'"], ... [" does not support tests with varying degrees of freedom "])); endif ## Calculate estimated marginal means and their standard errors gmeans = STATS.means(:); gvar = STATS.vars(:) ./ n; # Sampling variance gcov = diag (gvar); Ng = numel (gmeans); M = zeros (Ng, 4); M(:,1:2) = cat (2, gmeans, sqrt (gvar)); ## Calculate Welch's corrected degrees of freedom if (isempty (DFE)) DFE = sum (gvar(pairs), 2).^2 ./ ... sum ((gvar(pairs).^2 ./ (n(pairs) - 1)), 2); endif endswitch ## Calculate t statistics corresponding to the comparisons defined in L [mean_diff, sed, t] = tValue (gmeans, gcov, L); ## Calculate correlation matrix vcov = L * gcov * L'; R = cov2corr (vcov); ## Create cell array of group names corresponding to each row of m GNAMES = STATS.gnames; case "anova2" ## Fetch estimate specific information from the STATS structure switch (ESTIMATE) case {"column","columns","col","cols"} gmeans = STATS.colmeans(:); Ng = numel (gmeans); n = STATS.coln; case {"row","rows"} if (ismember (STATS.model, {"linear","nested"})) error (strcat (["multcompare: no support for the row factor"],... [" (random effect) in a 'nested' or 'linear' anova2 model"])); endif gmeans = STATS.rowmeans(:); Ng = numel (gmeans); n = STATS.rown; endswitch ## Make matrix of requested comparisons (pairs) ## Also return the corresponding hypothesis matrix (L) if (isempty (REF)) ## Pairwise comparisons [pairs, L, R] = pairwise (Ng); else ## Treatment vs. Control comparisons [pairs, L, R] = trt_vs_ctrl (Ng, REF); endif Np = size (pairs, 1); ## Calculate estimated marginal means and their standard errors gvar = ((STATS.sigmasq) / n) * ones (Ng, 1); # Sampling variance gcov = diag (gvar); M = zeros (Ng, 4); M(:,1:2) = cat (2, gmeans, sqrt (gvar)); ## Get the error degrees of freedom from anova2 output if (isempty (DFE)) DFE = STATS.df; endif ## Calculate t statistics corresponding to the comparisons defined in L [mean_diff, sed, t] = tValue (gmeans, gcov, L); ## Create character array of group names corresponding to each row of m GNAMES = cellstr (num2str ([1:Ng]')); case {"anovan","fitlm"} ## Our calculations treat all effects as fixed if (ismember (STATS.random, DIM)) warning (strcat (["multcompare: ignoring random effects"], ... [" (all effects treated as fixed)"])); endif ## Check what type of factor is requested in DIM if (any (STATS.nlevels(DIM) < 2)) error (strcat (["multcompare: DIM must specify only categorical"], ... [" factors with 2 or more degrees of freedom."])); endif ## Check that all continuous variables were centered msg = strcat (["multcompare: use a STATS structure from a model"], ... [" refit with a sum-to-zero contrast coding"]); if (any (STATS.continuous - STATS.center_continuous)) error (msg) endif ## Check that the columns sum to 0 N = numel (STATS.contrasts); for j = 1:N if (isnumeric (STATS.contrasts{j})) if (any (abs (sum (STATS.contrasts{j})) > eps("single"))) error (msg); endif endif endfor ## Calculate estimated marginal means and their standard errors Nd = numel (DIM); n = numel (STATS.resid); df = STATS.df; if (isempty (DFE)) DFE = STATS.dfe; endif i = 1 + cumsum(df); k = find (sum (STATS.terms(:,DIM), 2) == sum (STATS.terms, 2)); Nb = 1 + sum(df(k)); Nt = numel (k); L = zeros (n, sum (df) + 1); for j = 1:Nt L(:, i(k(j)) - df(k(j)) + 1 : i(k(j))) = STATS.X(:,i(k(j)) - ... df(k(j)) + 1 : i(k(j))); endfor L(:,1) = 1; U = unique (L, "rows", "stable"); Ng = size (U, 1); idx = zeros (Ng, 1); for k = 1:Ng idx(k) = find (all (L == U(k, :), 2),1); endfor gmeans = U * STATS.coeffs(:,1); # Estimated marginal means gcov = U * STATS.vcov * U'; gvar = diag (gcov); # Sampling variance M = zeros (Ng, 4); M(:,1:2) = cat (2, gmeans, sqrt(gvar)); ## Create cell array of group names corresponding to each row of m GNAMES = cell (Ng, 1); for i = 1:Ng str = ""; for j = 1:Nd str = sprintf("%s%s=%s, ", str, ... num2str(STATS.varnames{DIM(j)}), ... num2str(STATS.grpnames{DIM(j)}{STATS.grps(idx(i),DIM(j))})); endfor GNAMES{i} = str(1:end-2); str = ""; endfor ## Make matrix of requested comparisons (pairs) ## Also return the corresponding hypothesis matrix (L) if (isempty (REF)) ## Pairwise comparisons [pairs, L] = pairwise (Ng); else ## Treatment vs. Control comparisons [pairs, L] = trt_vs_ctrl (Ng, REF); endif Np = size (pairs, 1); ## Calculate t statistics corresponding to the comparisons defined in L [mean_diff, sed, t] = tValue (gmeans, gcov, L); ## Calculate correlation matrix. vcov = L * gcov * L'; R = cov2corr (vcov); case "friedman" ## Get stats from structure gmeans = STATS.meanranks(:); Ng = length (gmeans); sigma = STATS.sigma; ## Make group names GNAMES = strjust (num2str ((1:Ng)'), "left"); ## Make matrix of requested comparisons (pairs) ## Also return the corresponding hypothesis matrix (L) if (isempty (REF)) ## Pairwise comparisons [pairs, L, R] = pairwise (Ng); else ## Treatment vs. Control comparisons [pairs, L, R] = trt_vs_ctrl (Ng, REF); endif Np = size (pairs, 1); ## Calculate covariance matrix gcov = ((sigma ^ 2) / STATS.n) * eye (Ng); ## Create matrix with group means and standard errors M = cat (2, gmeans, sqrt (diag (gcov))); ## Calculate t statistics corresponding to the comparisons defined in L [mean_diff, sed, t] = tValue (gmeans, gcov, L); # z-statistic (not t) ## Calculate degrees of freedom from number of groups if (isempty (DFE)) DFE = inf; # this is a z-statistic so infinite degrees of freedom endif case "kruskalwallis" ## Get stats from structure gmeans = STATS.meanranks(:); sumt = STATS.sumt; Ng = length (gmeans); n = STATS.n(:); N = sum (n); ## Make group names GNAMES = STATS.gnames; ## Make matrix of requested comparisons (pairs) ## Also return the corresponding hypothesis matrix (L) if (isempty (REF)) ## Pairwise comparisons [pairs, L] = pairwise (Ng); else ## Treatment vs. Control comparisons [pairs, L] = trt_vs_ctrl (Ng, REF); endif Np = size (pairs, 1); ## Calculate covariance matrix gcov = diag (((N * (N + 1) / 12) - (sumt / (12 * (N - 1)))) ./ n); ## Create matrix with group means and standard errors M = cat (2, gmeans, sqrt (diag (gcov))); ## Calculate t statistics corresponding to the comparisons defined in L [mean_diff, sed, t] = tValue (gmeans, gcov, L); # z-statistic (not t) ## Calculate correlation matrix vcov = L * gcov * L'; R = cov2corr (vcov); ## Calculate degrees of freedom from number of groups if (isempty (DFE)) DFE = inf; # this is a z-statistic so infinite degrees of freedom endif otherwise error (strcat (sprintf ("multcompare: the STATS structure from %s", ... STATS.source), [" is not currently supported"])) endswitch ## The test specific code above needs to create the following variables in ## order to proceed with the remainder of the function tasks ## - Ng: number of groups involved in comparisons ## - M: Ng-by-2 matrix of group means (col 1) and standard errors (col 2) ## - Np: number of comparisons (pairs of groups being compaired) ## - pairs: Np-by-2 matrix of numeric group IDs - each row is a comparison ## - R: correlation matrix for the requested comparisons ## - sed: vector containing SE of the difference for each comparisons ## - t: vector containing t for the difference relating to each comparisons ## - DFE: residual/error degrees of freedom ## - GNAMES: a cell array containing the names of the groups being compared ## Create matrix of comparisons and calculate confidence intervals and ## multiplicity adjusted p-values for the comparisons. C = zeros (Np, 8); C(:,1:2) = pairs; C(:,4) = (M(pairs(:, 1),1) - M(pairs(:, 2),1)); C(:,7) = t; # Unlike Matlab, we include the t statistic C(:,8) = DFE; # Unlike Matlab, we include the degrees of freedom if (any (isinf (DFE))) p = 2 * (1 - normcdf (abs (t))); else p = 2 * (1 - tcdf (abs (t), DFE)); endif [C(:,6), critval, C(:,8)] = feval (CTYPE, p, t, Ng, DFE, R, ALPHA); C(:,3) = C(:,4) - sed .* critval; C(:,5) = C(:,4) + sed .* critval; ## Calculate confidence intervals of the estimated marginal means with ## central coverage such that the intervals start to overlap where the ## difference reaches a two-tailed p-value of ALPHA. When ALPHA is 0.05, ## central coverage is approximately 83.4% if (! isscalar(DFE)) # Upper bound critval (corresponding to lower bound DFE) critval = max (critval); endif M(:,3) = M(:,1) - M(:,2) .* critval / sqrt(2); M(:,4) = M(:,1) + M(:,2) .* critval / sqrt(2); ## If requested, plot graph of the difference means for each comparison ## with central coverage of confidence intervals at 100*(1-alpha)% switch (lower (DISPLAY)) case {'on',true} H = figure; plot ([0; 0], [0; Np + 1]',"k:"); # Plot vertical dashed line at 0 effect set (gca, "Ydir", "reverse") # Flip y-axis direction ylim ([0.5, Np + 0.5]); # Set y-axis limits hold on # Plot on the same axis for j = 1:Np if (C(j,6) < ALPHA) ## Plot marker for the difference in means plot (C(j,4), j,"or","MarkerFaceColor", "r"); ## Plot line for each confidence interval plot ([C(j,3), C(j,5)], j * ones(2,1), "r-"); else ## Plot marker for the difference in means plot (C(j,4), j,"ob","MarkerFaceColor", "b"); ## Plot line for each confidence interval plot ([C(j,3), C(j,5)], j * ones(2,1), "b-"); endif endfor hold off xlabel (sprintf ("%g%% confidence interval for the difference",... 100 * (1 - ALPHA))); ylabel ("Row number in matrix of comparisons (C)"); case {'off',false} H = []; endswitch ## Print multcompare table on screen if no output argument was requested if (nargout == 0 || strcmp (DISPLAY, "on")) printf ("\n %s Multiple Comparison (Post Hoc) Test for %s\n\n", ... upper (CTYPE), upper (STATS.source)); header = strcat (["Group ID Group ID LBoundDiff EstimatedDiff"],... [" UBoundDiff p-value\n"], ... ["-------------------------------------------------"],... ["---------------------\n"]); printf ("%s", header); for j = 1:Np if (C(j,6) < 0.001) printf ("%5i %5i %10.3f %10.3f %10.3f <.001\n",... C(j,1), C(j,2), C(j,3), C(j,4), C(j,5)); elseif (C(j,6) < 0.9995) printf ("%5i %5i %10.3f %10.3f %10.3f .%03u\n",... C(j,1), C(j,2), C(j,3), C(j,4), C(j,5), round (C(j,6) * 1e+03)); else printf ("%5i %5i %10.3f %10.3f %10.3f 1.000\n",... C(j,1), C(j,2), C(j,3), C(j,4), C(j,5)); endif endfor printf ("\n"); endif endfunction ## Posthoc comparisons function [pairs, L, R] = pairwise (Ng) ## Create pairs matrix for pairwise comparisons gid = [1:Ng]'; # Create numeric group ID A = ones (Ng, 1) * gid'; B = tril (gid * ones(1, Ng),-1); pairs = [A(:), B(:)]; ridx = (pairs(:, 2) == 0); pairs(ridx, :) = []; ## Calculate correlation matrix (required for CTYPE "mvt") Np = size (pairs, 1); L = zeros (Np, Ng); for j = 1:Np L(j, pairs(j,:)) = [1,-1]; # Hypothesis matrix endfor R = corr (L'); # Correlation matrix endfunction function [pairs, L, R] = trt_vs_ctrl (Ng, REF) ## Create pairs matrix for comparisons with control (REF) gid = [1:Ng]'; # Create numeric group ID pairs = zeros (Ng - 1, 2); pairs(:, 1) = REF; pairs(:, 2) = gid(gid != REF); ## Calculate correlation matrix (required for CTYPE "mvt") Np = size (pairs, 1); L = zeros (Np, Ng); for j = 1:Np L(j, pairs(j,:)) = [1,-1]; # Hypothesis matrix endfor R = corr (L'); # Correlation matrix endfunction function [mn, se, t] = tValue (gmeans, gcov, L) ## Calculate means, standard errors and t (or z) statistics ## corresponding to the comparisons defined in L. mn = sum (L * diag (gmeans), 2); se = sqrt (diag (L * gcov * L')); t = mn ./ se; endfunction function R = cov2corr (vcov) ## Convert covariance matrix to correlation matrix sed = sqrt (diag (vcov)); R = vcov ./ (sed * sed'); R = (R + R') / 2; # This step ensures that the matrix is positive definite endfunction ## Methods to control family-wise error rate in multiple comparisons function [padj, critval, dfe] = scheffe (p, t, Ng, dfe, R, ALPHA) ## Calculate the p-value if (isinf (dfe)) padj = 1 - chi2cdf (t.^2, Ng - 1); else padj = 1 - fcdf ((t.^2) / (Ng - 1), Ng - 1, dfe); endif ## Calculate critical value at Scheffe-adjusted ALPHA level if (isinf (dfe)) tmp = chi2inv (1 - ALPHA, Ng - 1) / (Ng - 1); else tmp = finv (1 - ALPHA, Ng - 1, dfe); end critval = sqrt ((Ng - 1) * tmp); endfunction function [padj, critval, dfe] = bonferroni (p, t, Ng, dfe, R, ALPHA) ## Bonferroni procedure Np = numel (p); padj = min (p * Np, 1.0); ## If requested, calculate critical value at Bonferroni-adjusted ALPHA level if (nargout > 1) critval = tinv (1 - ALPHA / Np * 0.5, dfe); endif endfunction function [padj, critval, dfe] = mvt (p, t, Ng, dfe, R, ALPHA) ## Monte Carlo simulation of the maximum test statistic in random samples ## generated from a multivariate t distribution. This method accounts for ## correlations among comparisons. This method simulates Tukey's test in the ## case of pairwise comparisons or Dunnett's tests in the case of trt_vs_ctrl. ## The "mvt" method is equivalent to methods used in the following R packages: ## - emmeans: the "mvt" adjust method in functions within emmeans ## - glht: the "single-step" adjustment in the multcomp.function ## Lower bound for error degrees of freedom to ensure type 1 error rate isn't ## exceeded for any test if (! isscalar(dfe)) dfe = max (1, round (min (dfe))); fprintf ("Note: df set to %u (lower bound)\n", dfe); endif ## Check if we can use parallel processing to accelerate computations pat = '^parallel'; software = pkg('list'); names = cellfun (@(S) S.name, software, 'UniformOutput', false); status = cellfun (@(S) S.loaded, software, 'UniformOutput', false); index = find (! cellfun (@isempty, regexpi (names, pat))); if (! isempty (index)) if (logical (status{index})) PARALLEL = true; else PARALLEL = false; endif else PARALLEL = false; endif ## Generate the distribution of (correlated) t statistics under the null, and ## calculate the maximum test statistic for each random sample. Computations ## are performed in chunks to prevent memory issues when the number of ## comparisons is large. chunkSize = 1000; numChunks = 1000; nsim = chunkSize * numChunks; if (isinf (dfe)) # Multivariate z-statistics func = @(jnk) max (abs (mvnrnd (0, R, chunkSize)'), [], 1); else # Multivariate t-statistics func = @(jnk) max (abs (mvtrnd (R, dfe, chunkSize)'), [], 1); endif if (PARALLEL) maxT = cell2mat (parcellfun (nproc, func, ... cell (1, numChunks), 'UniformOutput', false)); else maxT = cell2mat (cellfun (func, cell (1, numChunks), 'UniformOutput', false)); endif ## Calculate multiplicity adjusted p-values (two-tailed) padj = max (sum (bsxfun (@ge, maxT, abs (t)), 2) / nsim, nsim^-1); ## Calculate critical value adjusted by the maxT procedure critval = quantile (maxT, 1 - ALPHA); endfunction function [padj, critval, dfe] = holm (p, t, Ng, dfe, R, ALPHA) ## Holm's step-down Bonferroni procedure ## Order raw p-values [ps, idx] = sort (p, "ascend"); Np = numel (ps); ## Implement Holm's step-down Bonferroni procedure padj = nan (Np,1); padj(1) = Np * ps(1); for i = 2:Np padj(i) = max (padj(i - 1), (Np - i + 1) * ps(i)); endfor ## Reorder the adjusted p-values to match the order of the original p-values [jnk, original_order] = sort (idx, "ascend"); padj = padj(original_order); ## Truncate adjusted p-values to 1.0 padj(padj>1) = 1; ## If requested, calculate critical value at ALPHA ## No adjustment to confidence interval coverage if (nargout > 1) critval = tinv (1 - ALPHA / 2, dfe); endif endfunction function [padj, critval, dfe] = hochberg (p, t, Ng, dfe, R, ALPHA) ## Hochberg's step-up Bonferroni procedure ## Order raw p-values [ps, idx] = sort (p, "ascend"); Np = numel (ps); ## Implement Hochberg's step-down Bonferroni procedure padj = nan (Np,1); padj(Np) = ps(Np); for j = 1:Np-1 i = Np - j; padj(i) = min (padj(i + 1), (Np -i + 1) * ps(i)); endfor ## Reorder the adjusted p-values to match the order of the original p-values [jnk, original_order] = sort (idx, "ascend"); padj = padj(original_order); ## Truncate adjusted p-values to 1.0 padj(padj>1) = 1; ## If requested, calculate critical value at ALPHA ## No adjustment to confidence interval coverage if (nargout > 1) critval = tinv (1 - ALPHA / 2, dfe); endif endfunction function [padj, critval, dfe] = fdr (p, t, Ng, dfe, R, ALPHA) ## Benjamini-Hochberg procedure to control the false discovery rate (FDR) ## This procedure does not control the family-wise error rate ## Order raw p-values [ps, idx] = sort (p, "ascend"); Np = numel (ps); ## Initialize padj = nan (Np,1); alpha = nan (Np,1); ## Benjamini-Hochberg step-up procedure to control the false discovery rate padj = nan (Np,1); padj(Np) = ps(Np); for j = 1:Np-1 i = Np - j; padj(i) = min (padj(i + 1), Np / i * ps(i)); endfor ## Reorder the adjusted p-values to match the order of the original p-values [jnk, original_order] = sort (idx, "ascend"); padj = padj(original_order); ## Truncate adjusted p-values to 1.0 padj(padj>1) = 1; ## If requested, calculate critical value at ALPHA ## No adjustment to confidence interval coverage if (nargout > 1) critval = tinv (1 - ALPHA / 2, dfe); endif endfunction function [padj, critval, dfe] = lsd (p, t, Ng, dfe, R, ALPHA) ## Fisher's Least Significant Difference ## No control of the type I error rate across multiple comparisons padj = p; ## Calculate critical value at ALPHA ## No adjustment to confidence interval coverage critval = tinv (1 - ALPHA / 2, dfe); endfunction %!demo %! %! ## Demonstration using balanced one-way ANOVA from anova1 %! %! x = ones (50, 4) .* [-2, 0, 1, 5]; %! randn ("seed", 1); # for reproducibility %! x = x + normrnd (0, 2, 50, 4); %! groups = {"A", "B", "C", "D"}; %! [p, tbl, stats] = anova1 (x, groups, "off"); %! multcompare (stats); %!demo %! %! ## Demonstration using unbalanced one-way ANOVA example from anovan %! %! dv = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! g = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 ... %! 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! %! [P,ATAB, STATS] = anovan (dv, g, "varnames", "score", "display", "off"); %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "holm", ... %! "ControlGroup", 1, "display", "on") %! %!demo %! %! ## Demonstration using factorial ANCOVA example from anovan %! %! score = [95.6 82.2 97.2 96.4 81.4 83.6 89.4 83.8 83.3 85.7 ... %! 97.2 78.2 78.9 91.8 86.9 84.1 88.6 89.8 87.3 85.4 ... %! 81.8 65.8 68.1 70.0 69.9 75.1 72.3 70.9 71.5 72.5 ... %! 84.9 96.1 94.6 82.5 90.7 87.0 86.8 93.3 87.6 92.4 ... %! 100. 80.5 92.9 84.0 88.4 91.1 85.7 91.3 92.3 87.9 ... %! 91.7 88.6 75.8 75.7 75.3 82.4 80.1 86.0 81.8 82.5]'; %! treatment = {"yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" ... %! "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" ... %! "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" "yes" ... %! "no" "no" "no" "no" "no" "no" "no" "no" "no" "no" ... %! "no" "no" "no" "no" "no" "no" "no" "no" "no" "no" ... %! "no" "no" "no" "no" "no" "no" "no" "no" "no" "no"}'; %! exercise = {"lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" ... %! "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" ... %! "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" ... %! "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" "lo" ... %! "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" "mid" ... %! "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi"}'; %! age = [59 65 70 66 61 65 57 61 58 55 62 61 60 59 55 57 60 63 62 57 ... %! 58 56 57 59 59 60 55 53 55 58 68 62 61 54 59 63 60 67 60 67 ... %! 75 54 57 62 65 60 58 61 65 57 56 58 58 58 52 53 60 62 61 61]'; %! %! [P, ATAB, STATS] = anovan (score, {treatment, exercise, age}, "model", ... %! [1 0 0; 0 1 0; 0 0 1; 1 1 0], "continuous", 3, ... %! "sstype", "h", "display", "off", "contrasts", ... %! {"simple","poly",""}); %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", [1 2], "ctype", "holm", ... %! "display", "on") %! %!demo %! %! ## Demonstration using one-way ANOVA from anovan, with fit by weighted least %! ## squares to account for heteroskedasticity. %! %! g = [1, 1, 1, 1, 1, 1, 1, 1, ... %! 2, 2, 2, 2, 2, 2, 2, 2, ... %! 3, 3, 3, 3, 3, 3, 3, 3]'; %! %! y = [13, 16, 16, 7, 11, 5, 1, 9, ... %! 10, 25, 66, 43, 47, 56, 6, 39, ... %! 11, 39, 26, 35, 25, 14, 24, 17]'; %! %! [P,ATAB,STATS] = anovan(y, g, "display", "off"); %! fitted = STATS.X * STATS.coeffs(:,1); # fitted values %! b = polyfit (fitted, abs (STATS.resid), 1); %! v = polyval (b, fitted); # Variance as a function of the fitted values %! [P,ATAB,STATS] = anovan (y, g, "weights", v.^-1, "display", "off"); %! [C, M] = multcompare (STATS, "display", "on", "ctype", "mvt") %!demo %! %! ## Demonstration of p-value adjustments to control the false discovery rate %! ## Data from Westfall (1997) JASA. 92(437):299-306 %! %! p = [.005708; .023544; .024193; .044895; ... %! .048805; .221227; .395867; .693051; .775755]; %! %! padj = multcompare(p,'ctype','fdr') %!test %! %! ## Tests using unbalanced one-way ANOVA example from anovan and anova1 %! %! ## Test for anovan - compare pairwise comparisons with matlab for CTYPE "lsd" %! %! dv = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! g = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 ... %! 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! %! [P, ATAB, STATS] = anovan (dv, g, "varnames", "score", "display", "off"); %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "lsd", ... %! "display", "off"); %! assert (C(1,6), 2.85812420217898e-05, 1e-09); %! assert (C(2,6), 5.22936741204085e-07, 1e-09); %! assert (C(3,6), 2.12794763209146e-08, 1e-09); %! assert (C(4,6), 7.82091664406946e-15, 1e-09); %! assert (C(5,6), 0.546591417210693, 1e-09); %! assert (C(6,6), 0.0845897945254446, 1e-09); %! assert (C(7,6), 9.47436557975328e-08, 1e-09); %! assert (C(8,6), 0.188873478781067, 1e-09); %! assert (C(9,6), 4.08974010364197e-08, 1e-09); %! assert (C(10,6), 4.44427348175241e-06, 1e-09); %! assert (M(1,1), 10, 1e-09); %! assert (M(2,1), 18, 1e-09); %! assert (M(3,1), 19, 1e-09); %! assert (M(4,1), 21.0001428571429, 1e-09); %! assert (M(5,1), 29.0001111111111, 1e-09); %! assert (M(1,2), 1.0177537954095, 1e-09); %! assert (M(2,2), 1.28736803631001, 1e-09); %! assert (M(3,2), 1.0177537954095, 1e-09); %! assert (M(4,2), 1.0880245732889, 1e-09); %! assert (M(5,2), 0.959547480416536, 1e-09); %! %! ## Compare "fdr" adjusted p-values to those obtained using p.adjust in R %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "fdr", ... %! "display", "off"); %! assert (C(1,6), 4.08303457454140e-05, 1e-09); %! assert (C(2,6), 1.04587348240817e-06, 1e-09); %! assert (C(3,6), 1.06397381604573e-07, 1e-09); %! assert (C(4,6), 7.82091664406946e-14, 1e-09); %! assert (C(5,6), 5.46591417210693e-01, 1e-09); %! assert (C(6,6), 1.05737243156806e-01, 1e-09); %! assert (C(7,6), 2.36859139493832e-07, 1e-09); %! assert (C(8,6), 2.09859420867852e-01, 1e-09); %! assert (C(9,6), 1.36324670121399e-07, 1e-09); %! assert (C(10,6), 7.40712246958735e-06, 1e-09); %! %! ## Compare "hochberg" adjusted p-values to those obtained using p.adjust in R %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "hochberg", ... %! "display", "off"); %! assert (C(1,6), 1.14324968087159e-04, 1e-09); %! assert (C(2,6), 3.13762044722451e-06, 1e-09); %! assert (C(3,6), 1.91515286888231e-07, 1e-09); %! assert (C(4,6), 7.82091664406946e-14, 1e-09); %! assert (C(5,6), 5.46591417210693e-01, 1e-09); %! assert (C(6,6), 2.53769383576334e-01, 1e-09); %! assert (C(7,6), 6.63205590582730e-07, 1e-09); %! assert (C(8,6), 3.77746957562134e-01, 1e-09); %! assert (C(9,6), 3.27179208291358e-07, 1e-09); %! assert (C(10,6), 2.22213674087620e-05, 1e-09); %! %! ## Compare "holm" adjusted p-values to those obtained using p.adjust in R %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "holm", ... %! "display", "off"); %! assert (C(1,6), 1.14324968087159e-04, 1e-09); %! assert (C(2,6), 3.13762044722451e-06, 1e-09); %! assert (C(3,6), 1.91515286888231e-07, 1e-09); %! assert (C(4,6), 7.82091664406946e-14, 1e-09); %! assert (C(5,6), 5.46591417210693e-01, 1e-09); %! assert (C(6,6), 2.53769383576334e-01, 1e-09); %! assert (C(7,6), 6.63205590582730e-07, 1e-09); %! assert (C(8,6), 3.77746957562134e-01, 1e-09); %! assert (C(9,6), 3.27179208291358e-07, 1e-09); %! assert (C(10,6), 2.22213674087620e-05, 1e-09); %! %! ## Compare "scheffe" adjusted p-values to those obtained using 'scheffe' in Matlab %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "scheffe", ... %! "display", "off"); %! assert (C(1,6), 0.00108105386141085, 1e-09); %! assert (C(2,6), 2.7779386789517e-05, 1e-09); %! assert (C(3,6), 1.3599854038198e-06, 1e-09); %! assert (C(4,6), 7.58830197867751e-13, 1e-09); %! assert (C(5,6), 0.984039948220281, 1e-09); %! assert (C(6,6), 0.539077018557706, 1e-09); %! assert (C(7,6), 5.59475764460574e-06, 1e-09); %! assert (C(8,6), 0.771173490574105, 1e-09); %! assert (C(9,6), 2.52838425729905e-06, 1e-09); %! assert (C(10,6), 0.000200719143889168, 1e-09); %! %! ## Compare "bonferroni" adjusted p-values to those obtained using p.adjust in R %! %! [C, M, H, GNAMES] = multcompare (STATS, "dim", 1, "ctype", "bonferroni", ... %! "display", "off"); %! assert (C(1,6), 2.85812420217898e-04, 1e-09); %! assert (C(2,6), 5.22936741204085e-06, 1e-09); %! assert (C(3,6), 2.12794763209146e-07, 1e-09); %! assert (C(4,6), 7.82091664406946e-14, 1e-09); %! assert (C(5,6), 1.00000000000000e+00, 1e-09); %! assert (C(6,6), 8.45897945254446e-01, 1e-09); %! assert (C(7,6), 9.47436557975328e-07, 1e-09); %! assert (C(8,6), 1.00000000000000e+00, 1e-09); %! assert (C(9,6), 4.08974010364197e-07, 1e-09); %! assert (C(10,6), 4.44427348175241e-05, 1e-09); %! %! ## Test for anova1 ("equal")- comparison of results from Matlab %! %! [P, ATAB, STATS] = anova1 (dv, g, "off", "equal"); %! [C, M, H, GNAMES] = multcompare (STATS, "ctype", "lsd", "display", "off"); %! assert (C(1,6), 2.85812420217898e-05, 1e-09); %! assert (C(2,6), 5.22936741204085e-07, 1e-09); %! assert (C(3,6), 2.12794763209146e-08, 1e-09); %! assert (C(4,6), 7.82091664406946e-15, 1e-09); %! assert (C(5,6), 0.546591417210693, 1e-09); %! assert (C(6,6), 0.0845897945254446, 1e-09); %! assert (C(7,6), 9.47436557975328e-08, 1e-09); %! assert (C(8,6), 0.188873478781067, 1e-09); %! assert (C(9,6), 4.08974010364197e-08, 1e-09); %! assert (C(10,6), 4.44427348175241e-06, 1e-09); %! assert (M(1,1), 10, 1e-09); %! assert (M(2,1), 18, 1e-09); %! assert (M(3,1), 19, 1e-09); %! assert (M(4,1), 21.0001428571429, 1e-09); %! assert (M(5,1), 29.0001111111111, 1e-09); %! assert (M(1,2), 1.0177537954095, 1e-09); %! assert (M(2,2), 1.28736803631001, 1e-09); %! assert (M(3,2), 1.0177537954095, 1e-09); %! assert (M(4,2), 1.0880245732889, 1e-09); %! assert (M(5,2), 0.959547480416536, 1e-09); %! %! ## Test for anova1 ("unequal") - comparison with results from GraphPad Prism 8 %! [P, ATAB, STATS] = anova1 (dv, g, "off", "unequal"); %! [C, M, H, GNAMES] = multcompare (STATS, "ctype", "lsd", "display", "off"); %! assert (C(1,6), 0.001247025266382, 1e-09); %! assert (C(2,6), 0.000018037115146, 1e-09); %! assert (C(3,6), 0.000002974595187, 1e-09); %! assert (C(4,6), 0.000000000786046, 1e-09); %! assert (C(5,6), 0.5693192886650109, 1e-09); %! assert (C(6,6), 0.110501699029776, 1e-09); %! assert (C(7,6), 0.000131226488700, 1e-09); %! assert (C(8,6), 0.1912101409715992, 1e-09); %! assert (C(9,6), 0.000005385256394, 1e-09); %! assert (C(10,6), 0.000074089106171, 1e-09); %!test %! %! ## Test for anova2 ("interaction") - comparison with results from Matlab for column effect %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! [P, ATAB, STATS] = anova2 (popcorn, 3, "off"); %! [C, M, H, GNAMES] = multcompare (STATS, "estimate", "column",... %! "ctype", "lsd", "display", "off"); %! assert (C(1,6), 1.49311100811177e-05, 1e-09); %! assert (C(2,6), 2.20506904243535e-07, 1e-09); %! assert (C(3,6), 0.00449897860490058, 1e-09); %! assert (M(1,1), 6.25, 1e-09); %! assert (M(2,1), 4.75, 1e-09); %! assert (M(3,1), 4, 1e-09); %! assert (M(1,2), 0.152145154862547, 1e-09); %! assert (M(2,2), 0.152145154862547, 1e-09); %! assert (M(3,2), 0.152145154862547, 1e-09); %!test %! %! ## Test for anova2 ("linear") - comparison with results from GraphPad Prism 8 %! words = [10 13 13; 6 8 8; 11 14 14; 22 23 25; 16 18 20; ... %! 15 17 17; 1 1 4; 12 15 17; 9 12 12; 8 9 12]; %! [P, ATAB, STATS] = anova2 (words, 1, "off", "linear"); %! [C, M, H, GNAMES] = multcompare (STATS, "estimate", "column",... %! "ctype", "lsd", "display", "off"); %! assert (C(1,6), 0.000020799832702, 1e-09); %! assert (C(2,6), 0.000000035812410, 1e-09); %! assert (C(3,6), 0.003038942449215, 1e-09); %!test %! %! ## Test for anova2 ("nested") - comparison with results from GraphPad Prism 8 %! data = [4.5924 7.3809 21.322; -0.5488 9.2085 25.0426; ... %! 6.1605 13.1147 22.66; 2.3374 15.2654 24.1283; ... %! 5.1873 12.4188 16.5927; 3.3579 14.3951 10.2129; ... %! 6.3092 8.5986 9.8934; 3.2831 3.4945 10.0203]; %! [P, ATAB, STATS] = anova2 (data, 4, "off", "nested"); %! [C, M, H, GNAMES] = multcompare (STATS, "estimate", "column",... %! "ctype", "lsd", "display", "off"); %! assert (C(1,6), 0.261031111511073, 1e-09); %! assert (C(2,6), 0.065879755907745, 1e-09); %! assert (C(3,6), 0.241874613529270, 1e-09); %!shared visibility_setting %! visibility_setting = get (0, "DefaultFigureVisible"); %!test %! set (0, "DefaultFigureVisible", "off"); %! %! ## Test for kruskalwallis - comparison with results from MATLAB %! data = [3,2,4; 5,4,4; 4,2,4; 4,2,4; 4,1,5; ... %! 4,2,3; 4,3,5; 4,2,4; 5,2,4; 5,3,3]; %! group = [1:3] .* ones (10,3); %! [P, ATAB, STATS] = kruskalwallis (data(:), group(:), "off"); %! C = multcompare (STATS, "ctype", "lsd", "display", "off"); %! assert (C(1,6), 0.000163089828959986, 1e-09); %! assert (C(2,6), 0.630298044801257, 1e-09); %! assert (C(3,6), 0.00100567660695682, 1e-09); %! C = multcompare (STATS, "ctype", "bonferroni", "display", "off"); %! assert (C(1,6), 0.000489269486879958, 1e-09); %! assert (C(2,6), 1, 1e-09); %! assert (C(3,6), 0.00301702982087047, 1e-09); %! C = multcompare(STATS, "ctype", "scheffe", "display", "off"); %! assert (C(1,6), 0.000819054880289573, 1e-09); %! assert (C(2,6), 0.890628039849261, 1e-09); %! assert (C(3,6), 0.00447816059021654, 1e-09); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! ## Test for friedman - comparison with results from MATLAB %! popcorn = [5.5, 4.5, 3.5; 5.5, 4.5, 4.0; 6.0, 4.0, 3.0; ... %! 6.5, 5.0, 4.0; 7.0, 5.5, 5.0; 7.0, 5.0, 4.5]; %! [P, ATAB, STATS] = friedman (popcorn, 3, "off"); %! C = multcompare(STATS, "ctype", "lsd", "display", "off"); %! assert (C(1,6), 0.227424558028569, 1e-09); %! assert (C(2,6), 0.0327204848315735, 1e-09); %! assert (C(3,6), 0.353160353315988, 1e-09); %! C = multcompare(STATS, "ctype", "bonferroni", "display", "off"); %! assert (C(1,6), 0.682273674085708, 1e-09); %! assert (C(2,6), 0.0981614544947206, 1e-09); %! assert (C(3,6), 1, 1e-09); %! C = multcompare(STATS, "ctype", "scheffe", "display", "off"); %! assert (C(1,6), 0.482657360384373, 1e-09); %! assert (C(2,6), 0.102266573027672, 1e-09); %! assert (C(3,6), 0.649836502233148, 1e-09); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! set (0, "DefaultFigureVisible", "off"); %! ## Test for fitlm - same comparisons as for first anovan example %! y = [ 8.706 10.362 11.552 6.941 10.983 10.092 6.421 14.943 15.931 ... %! 22.968 18.590 16.567 15.944 21.637 14.492 17.965 18.851 22.891 ... %! 22.028 16.884 17.252 18.325 25.435 19.141 21.238 22.196 18.038 ... %! 22.628 31.163 26.053 24.419 32.145 28.966 30.207 29.142 33.212 ... %! 25.694 ]'; %! X = [1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5]'; %! [TAB,STATS] = fitlm (X,y,"linear","categorical",1,"display","off",... %! "contrasts","simple"); %! [C, M] = multcompare(STATS, "ctype", "lsd", "display", "off"); %! assert (C(1,6), 2.85812420217898e-05, 1e-09); %! assert (C(2,6), 5.22936741204085e-07, 1e-09); %! assert (C(3,6), 2.12794763209146e-08, 1e-09); %! assert (C(4,6), 7.82091664406946e-15, 1e-09); %! assert (C(5,6), 0.546591417210693, 1e-09); %! assert (C(6,6), 0.0845897945254446, 1e-09); %! assert (C(7,6), 9.47436557975328e-08, 1e-09); %! assert (C(8,6), 0.188873478781067, 1e-09); %! assert (C(9,6), 4.08974010364197e-08, 1e-09); %! assert (C(10,6), 4.44427348175241e-06, 1e-09); %! assert (M(1,1), 10, 1e-09); %! assert (M(2,1), 18, 1e-09); %! assert (M(3,1), 19, 1e-09); %! assert (M(4,1), 21.0001428571429, 1e-09); %! assert (M(5,1), 29.0001111111111, 1e-09); %! assert (M(1,2), 1.0177537954095, 1e-09); %! assert (M(2,2), 1.28736803631001, 1e-09); %! assert (M(3,2), 1.0177537954095, 1e-09); %! assert (M(4,2), 1.0880245732889, 1e-09); %! assert (M(5,2), 0.959547480416536, 1e-09); %! set (0, "DefaultFigureVisible", visibility_setting); %!test %! ## Test p-value adjustments compared to R stats package function p.adjust %! ## Data from Westfall (1997) JASA. 92(437):299-306 %! p = [.005708; .023544; .024193; .044895; ... %! .048805; .221227; .395867; .693051; .775755]; %! padj = multcompare (p); %! assert (padj(1), 0.051372, 1e-06); %! assert (padj(2), 0.188352, 1e-06); %! assert (padj(3), 0.188352, 1e-06); %! assert (padj(4), 0.269370, 1e-06); %! assert (padj(5), 0.269370, 1e-06); %! assert (padj(6), 0.884908, 1e-06); %! assert (padj(7), 1.000000, 1e-06); %! assert (padj(8), 1.000000, 1e-06); %! assert (padj(9), 1.000000, 1e-06); %! padj = multcompare(p,'ctype','holm'); %! assert (padj(1), 0.051372, 1e-06); %! assert (padj(2), 0.188352, 1e-06); %! assert (padj(3), 0.188352, 1e-06); %! assert (padj(4), 0.269370, 1e-06); %! assert (padj(5), 0.269370, 1e-06); %! assert (padj(6), 0.884908, 1e-06); %! assert (padj(7), 1.000000, 1e-06); %! assert (padj(8), 1.000000, 1e-06); %! assert (padj(9), 1.000000, 1e-06); %! padj = multcompare(p,'ctype','hochberg'); %! assert (padj(1), 0.051372, 1e-06); %! assert (padj(2), 0.169351, 1e-06); %! assert (padj(3), 0.169351, 1e-06); %! assert (padj(4), 0.244025, 1e-06); %! assert (padj(5), 0.244025, 1e-06); %! assert (padj(6), 0.775755, 1e-06); %! assert (padj(7), 0.775755, 1e-06); %! assert (padj(8), 0.775755, 1e-06); %! assert (padj(9), 0.775755, 1e-06); %! padj = multcompare(p,'ctype','fdr'); %! assert (padj(1), 0.0513720, 1e-07); %! assert (padj(2), 0.0725790, 1e-07); %! assert (padj(3), 0.0725790, 1e-07); %! assert (padj(4), 0.0878490, 1e-07); %! assert (padj(5), 0.0878490, 1e-07); %! assert (padj(6), 0.3318405, 1e-07); %! assert (padj(7), 0.5089719, 1e-07); %! assert (padj(8), 0.7757550, 1e-07); %! assert (padj(9), 0.7757550, 1e-07); statistics-release-1.7.3/inst/nanmax.m000066400000000000000000000200161475240274700177620ustar00rootroot00000000000000## Copyright (C) 2001 Paul Kienzle ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{v} =} nanmax (@var{x}) ## @deftypefnx {statistics} {@var{v} =} nanmax (@var{x}, [], @var{dim}) ## @deftypefnx {statistics} {[@var{v}, @var{idx}] =} nanmax (@dots{}) ## @deftypefnx {statistics} {@var{v} =} nanmax (@var{x}, [], @qcode{'all'}) ## @deftypefnx {statistics} {@var{v} =} nanmax (@var{x}, [], @var{vecdim}) ## @deftypefnx {statistics} {@var{v} =} nanmax (@var{x}, @var{y}) ## ## Find the maximum while ignoring NaN values. ## ## @code{@var{v} = nanmax (@var{x})} returns the maximum of @var{x}, after ## removing @qcode{NaN} values. If @var{x} is a vector, a scalar maximum value ## is returned. If @var{x} is a matrix, a row vector of column maxima is ## returned. If @var{x} is a multidimentional array, the @code{nanmax} operates ## along the fisrt nonsigleton dimension. If all values in a column are ## @qcode{NaN}, the maximum is returned as @qcode{NaN} rather than @qcode{[]}. ## ## @code{@var{v} = nanmax (@var{x}, [], @var{dim})} operates along the dimension ## @var{dim} of @var{x}. ## ## @code{[@var{v}, @var{idx}] = nanmax (@dots{})} also returns the row indices ## of the maximum values for each column in the vector @var{idx}. When @var{x} ## is a vector, then @var{idx} is a scalar value as @var{v}. ## ## @code{@var{v} = nanmax (@var{x}, [], @qcode{'all'})} returns the maximum of ## all elements of @var{x}, after removing @qcode{NaN} values. It is the ## equivalent of @code{nanmax (@var{x}(:))}. The optional flag @qcode{'all'} ## cannot be used together with @var{dim} or @var{vecdim} input arguments. ## ## @code{@var{v} = nanmax (@var{x}, [], @var{vecdim})} returns the maximum over ## the dimensions specified in the vector @var{vecdim}. Each element of ## @var{vecdim} represents a dimension of the input array @var{x} and the output ## @var{v} has length 1 in the specified operating dimensions. The lengths of ## the other dimensions are the same for @var{x} and @var{y}. For example, if ## @var{x} is a 2-by-3-by-4 array, then @code{nanmax (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the maximum of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to ## @code{nanmax (@var{x}, @qcode{'all'})}. Any dimension in @var{vecdim} ## greater than @code{ndims (@var{x})} is ignored. ## ## @seealso{max, nanmin, nansum} ## @end deftypefn function [v, idx] = nanmax (x, y, dim) if (nargin < 1 || nargin > 3) print_usage; elseif (nargin == 1 || (nargin == 2 && isempty (y))) nanvals = isnan (x); x(nanvals) = -Inf; [v, idx] = max (x); v(all (nanvals)) = NaN; elseif (nargin == 3 && strcmpi (dim, "all") && isempty (y)) x = x(:); nanvals = isnan (x); x(nanvals) = -Inf; [v, idx] = max (x); v(all (nanvals)) = NaN; elseif (nargin == 3 && isempty (y)) if (isscalar (dim)) nanvals = isnan (x); x(nanvals) = -Inf; [v, idx] = max (x, [], dim); v(all (nanvals, dim)) = NaN; else vecdim = sort (dim); if (! all (diff (vecdim))) error ("nanmax: VECDIM must contain non-repeating positive integers."); endif ## Ignore dimensions in VECDIM larger than actual array vecdim(find (vecdim > ndims (x))) = []; if (isempty (vecdim)) v = x; if (nargout > 1) idx = reshape ([1:numel(x)], size (x)); endif else ## Calculate permutation vector szx = size (x); remdims = 1:ndims (x); # All dimensions remdims(vecdim) = []; # Delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is equivalent to 'all' flag if (nremd == 0) x = x(:); nanvals = isnan (x); x(nanvals) = -Inf; [v, idx] = max (x); v(all (nanvals)) = NaN; else ## Permute to push vecdims to back perm = [remdims, vecdim]; x = permute (x, perm); ## Reshape to squash all vecdims in final dimension sznew = [szx(remdims), prod(szx(vecdim))]; x = reshape (x, sznew); ## Calculate nanmax on final dimension dim = nremd + 1; nanvals = isnan (x); x(nanvals) = -Inf; [v, idx] = max (x, [], dim); v(all (nanvals, dim)) = NaN; ## Inverse permute back to correct dimensions v = ipermute (v, perm); idx = ipermute (idx, perm); endif endif endif else if (nargout > 1) error ("nanmax: a second output is not supported with this syntax."); endif Xnan = isnan (x); Ynan = isnan (y); x(Xnan) = -Inf; y(Ynan) = -Inf; v = max (x, y); v(Xnan & Ynan) = NaN; endif endfunction %!demo %! ## Find the column maximum values and their indices %! ## for matrix data with missing values. %! %! x = magic (3); %! x([1, 6:9]) = NaN %! [y, ind] = nanmax (x) %!demo %! ## Find the maximum of all the values in an array, ignoring missing values. %! ## Create a 2-by-5-by-3 array x with some missing values. %! %! x = reshape (1:30, [2, 5, 3]); %! x([10:12, 25]) = NaN %! %! ## Find the maximum of the elements of x. %! %! y = nanmax (x, [], 'all') ## Test output %!assert (nanmax ([2, 4, NaN, 7]), 7) %!assert (nanmax ([2, 4, NaN, Inf]), Inf) %!assert (nanmax ([1, NaN, 3; NaN, 5, 6; 7, 8, NaN]), [7, 8, 6]) %!assert (nanmax ([1, NaN, 3; NaN, 5, 6; 7, 8, NaN]'), [3, 6, 8]) %!assert (nanmax (single ([1, NaN, 3; NaN, 5, 6; 7, 8, NaN])), single ([7, 8, 6])) %!shared x, y %! x(:,:,1) = [1.77, -0.005, NaN, -2.95; NaN, 0.34, NaN, 0.19]; %! x(:,:,2) = [1.77, -0.005, NaN, -2.95; NaN, 0.34, NaN, 0.19] + 5; %! y = x; %! y(2,3,1) = 0.51; %!assert (nanmax (x, [], [1, 2])(:), [1.77;6.77]) %!assert (nanmax (x, [], [1, 3])(:), [6.77;5.34;NaN;5.19]) %!assert (nanmax (x, [], [2, 3])(:), [6.77;5.34]) %!assert (nanmax (x, [], [1, 2, 3]), 6.77) %!assert (nanmax (x, [], 'all'), 6.77) %!assert (nanmax (y, [], [1, 3])(:), [6.77;5.34;0.51;5.19]) %!assert (nanmax (x(1,:,1), x(2,:,1)), [1.77, 0.34, NaN, 0.19]) %!assert (nanmax (x(1,:,2), x(2,:,2)), [6.77, 5.34, NaN, 5.19]) %!assert (nanmax (y(1,:,1), y(2,:,1)), [1.77, 0.34, 0.51, 0.19]) %!assert (nanmax (y(1,:,2), y(2,:,2)), [6.77, 5.34, NaN, 5.19]) ## Test dimension indexing with vecdim in N-dimensional arrays %!test %! xx = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (nanmax (xx, [], [3, 2])), [10, 1, 1, 3]); %! assert (size (nanmax (xx, [], [1, 2])), [1, 1, 6, 3]); %! assert (size (nanmax (xx, [], [1, 2, 4])), [1, 1, 6]); %! assert (size (nanmax (xx, [], [1, 4, 3])), [1, 40]); %! assert (size (nanmax (xx, [], [1, 2, 3, 4])), [1, 1]); ## Test exceeding dimensions %!assert (nanmax (ones (2), [], 3), ones (2, 2)) %!assert (nanmax (ones (2, 2, 2), [], 99), ones (2, 2, 2)) %!assert (nanmax (magic (3), [], 3), magic (3)) %!assert (nanmax (magic (3), [], [1, 3]), [8, 9, 7]) %!assert (nanmax (magic (3), [], [1, 99]), [8, 9, 7]) ## Test comparisons %!assert (nanmax (ones (2), 3), 3 * ones (2,2)) ## Test input validation %!error ... %! nanmax (y, [], [1, 1, 2]) %!error ... %! [v, idx] = nanmax(x, y, [1 2]) statistics-release-1.7.3/inst/nanmin.m000066400000000000000000000201441475240274700177620ustar00rootroot00000000000000## Copyright (C) 2001 Paul Kienzle ## Copyright (C) 2003 Alois Schloegl ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{v} =} nanmin (@var{x}) ## @deftypefnx {statistics} {@var{v} =} nanmin (@var{x}, [], @var{dim}) ## @deftypefnx {statistics} {[@var{v}, @var{idx}] =} nanmin (@dots{}) ## @deftypefnx {statistics} {@var{v} =} nanmin (@var{x}, [], @qcode{'all'}) ## @deftypefnx {statistics} {@var{v} =} nanmin (@var{x}, [], @var{vecdim}) ## @deftypefnx {statistics} {@var{v} =} nanmin (@var{x}, @var{y}) ## ## Find the minimum while ignoring NaN values. ## ## @code{@var{v} = nanmin (@var{x})} returns the minimum of @var{x}, after ## removing @qcode{NaN} values. If @var{x} is a vector, a scalar minimum value ## is returned. If @var{x} is a matrix, a row vector of column maxima is ## returned. If @var{x} is a multidimentional array, the @code{nanmin} operates ## along the fisrt nonsigleton dimension. If all values in a column are ## @qcode{NaN}, the minimum is returned as @qcode{NaN} rather than @qcode{[]}. ## ## @code{@var{v} = nanmin (@var{x}, [], @var{dim})} operates along the dimension ## @var{dim} of @var{x}. ## ## @code{[@var{v}, @var{idx}] = nanmin (@dots{})} also returns the row indices ## of the minimum values for each column in the vector @var{idx}. When @var{x} ## is a vector, then @var{idx} is a scalar value as @var{v}. ## ## @code{@var{v} = nanmin (@var{x}, [], @qcode{'all'})} returns the minimum of ## all elements of @var{x}, after removing @qcode{NaN} values. It is the ## equivalent of @code{nanmin (@var{x}(:))}. The optional flag @qcode{'all'} ## cannot be used together with @var{dim} or @var{vecdim} input arguments. ## ## @code{@var{v} = nanmin (@var{x}, [], @var{vecdim})} returns the minimum over ## the dimensions specified in the vector @var{vecdim}. Each element of ## @var{vecdim} represents a dimension of the input array @var{x} and the output ## @var{v} has length 1 in the specified operating dimensions. The lengths of ## the other dimensions are the same for @var{x} and @var{y}. For example, if ## @var{x} is a 2-by-3-by-4 array, then @code{nanmin (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the minimum of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to ## @code{nanmin (@var{x}, @qcode{'all'})}. Any dimension in @var{vecdim} ## greater than @code{ndims (@var{x})} is ignored. ## ## @seealso{min, nanmax, nansum} ## @end deftypefn function [v, idx] = nanmin (x, y, dim) if (nargin < 1 || nargin > 3) print_usage; elseif (nargin == 1 || (nargin == 2 && isempty (y))) nanvals = isnan (x); x(nanvals) = Inf; [v, idx] = min (x); v(all (nanvals)) = NaN; elseif (nargin == 3 && strcmpi (dim, "all") && isempty (y)) x = x(:); nanvals = isnan (x); x(nanvals) = Inf; [v, idx] = min (x); v(all (nanvals)) = NaN; elseif (nargin == 3 && isempty (y)) if (isscalar (dim)) nanvals = isnan (x); x(nanvals) = Inf; [v, idx] = min (x, [], dim); v(all (nanvals, dim)) = NaN; else vecdim = sort (dim); if (! all (diff (vecdim))) error ("nanmin: VECDIM must contain non-repeating positive integers."); endif ## Ignore dimensions in VECDIM larger than actual array vecdim(find (vecdim > ndims (x))) = []; if (isempty (vecdim)) v = x; if (nargout > 1) idx = reshape ([1:numel(x)], size (x)); endif else ## Calculate permutation vector szx = size (x); remdims = 1:ndims (x); # All dimensions remdims(vecdim) = []; # Delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is equivalent to 'all' flag if (nremd == 0) x = x(:); nanvals = isnan (x); x(nanvals) = Inf; [v, idx] = min (x); v(all (nanvals)) = NaN; else ## Permute to push vecdims to back perm = [remdims, vecdim]; x = permute (x, perm); ## Reshape to squash all vecdims in final dimension sznew = [szx(remdims), prod(szx(vecdim))]; x = reshape (x, sznew); ## Calculate nanmin on final dimension dim = nremd + 1; nanvals = isnan (x); x(nanvals) = Inf; [v, idx] = min (x, [], dim); v(all (nanvals, dim)) = NaN; ## Inverse permute back to correct dimensions v = ipermute (v, perm); idx = ipermute (idx, perm); endif endif endif else if (nargout > 1) error ("nanmin: a second output is not supported with this syntax."); endif Xnan = isnan (x); Ynan = isnan (y); x(Xnan) = Inf; y(Ynan) = Inf; v = min (x, y); v(Xnan & Ynan) = NaN; endif endfunction %!demo %! ## Find the column minimum values and their indices %! ## for matrix data with missing values. %! %! x = magic (3); %! x([1, 6:9]) = NaN %! [y, ind] = nanmin (x) %!demo %! ## Find the minimum of all the values in an array, ignoring missing values. %! ## Create a 2-by-5-by-3 array x with some missing values. %! %! x = reshape (1:30, [2, 5, 3]); %! x([10:12, 25]) = NaN %! %! ## Find the minimum of the elements of x. %! %! y = nanmin (x, [], 'all') ## Test output %!assert (nanmin ([2, 4, NaN, 7]), 2) %!assert (nanmin ([2, 4, NaN, -Inf]), -Inf) %!assert (nanmin ([1, NaN, 3; NaN, 5, 6; 7, 8, NaN]), [1, 5, 3]) %!assert (nanmin ([1, NaN, 3; NaN, 5, 6; 7, 8, NaN]'), [1, 5, 7]) %!assert (nanmin (single ([1, NaN, 3; NaN, 5, 6; 7, 8, NaN])), single ([1, 5, 3])) %!shared x, y %! x(:,:,1) = [1.77, -0.005, NaN, -2.95; NaN, 0.34, NaN, 0.19]; %! x(:,:,2) = [1.77, -0.005, NaN, -2.95; NaN, 0.34, NaN, 0.19] + 5; %! y = x; %! y(2,3,1) = 0.51; %!assert (nanmin (x, [], [1, 2])(:), [-2.95; 2.05]) %!assert (nanmin (x, [], [1, 3])(:), [1.77; -0.005; NaN; -2.95]) %!assert (nanmin (x, [], [2, 3])(:), [-2.95; 0.19]) %!assert (nanmin (x, [], [1, 2, 3]), -2.95) %!assert (nanmin (x, [], 'all'), -2.95) %!assert (nanmin (y, [], [1, 3])(:), [1.77; -0.005; 0.51; -2.95]) %!assert (nanmin (x(1,:,1), x(2,:,1)), [1.77, -0.005, NaN, -2.95]) %!assert (nanmin (x(1,:,2), x(2,:,2)), [6.77, 4.995, NaN, 2.05]) %!assert (nanmin (y(1,:,1), y(2,:,1)), [1.77, -0.005, 0.51, -2.95]) %!assert (nanmin (y(1,:,2), y(2,:,2)), [6.77, 4.995, NaN, 2.05]) ## Test dimension indexing with vecdim in N-dimensional arrays %!test %! xx = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (nanmin (xx, [], [3, 2])), [10, 1, 1, 3]); %! assert (size (nanmin (xx, [], [1, 2])), [1, 1, 6, 3]); %! assert (size (nanmin (xx, [], [1, 2, 4])), [1, 1, 6]); %! assert (size (nanmin (xx, [], [1, 4, 3])), [1, 40]); %! assert (size (nanmin (xx, [], [1, 2, 3, 4])), [1, 1]); ## Test exceeding dimensions %!assert (nanmin (ones (2), [], 3), ones (2, 2)) %!assert (nanmin (ones (2, 2, 2), [], 99), ones (2, 2, 2)) %!assert (nanmin (magic (3), [], 3), magic (3)) %!assert (nanmin (magic (3), [], [1, 3]), [3, 1, 2]) %!assert (nanmin (magic (3), [], [1, 99]), [3, 1, 2]) ## Test comparisons %!assert (nanmin (ones (2), 3), ones (2,2)) ## Test input validation %!error ... %! nanmin (y, [], [1, 1, 2]) %!error ... %! [v, idx] = nanmin(x, y, [1 2]) statistics-release-1.7.3/inst/nansum.m000066400000000000000000000040561475240274700200070ustar00rootroot00000000000000## Copyright (C) 2001 Paul Kienzle ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {s =} nansum (@var{x}) ## @deftypefnx {statistics} {s =} nansum (@var{x}, @var{dim}) ## @deftypefnx {statistics} {s =} nansum (@dots{}, @qcode{"native"}) ## @deftypefnx {statistics} {s =} nansum (@dots{}, @qcode{"double"}) ## @deftypefnx {statistics} {s =} nansum (@dots{}, @qcode{"extra"}) ## Compute the sum while ignoring NaN values. ## ## @code{nansum} is identical to the @code{sum} function except that NaN ## values are treated as 0 and so ignored. If all values are NaN, the sum is ## returned as 0. ## ## See help text of @code{sum} for details on the options. ## ## @seealso{sum, nanmin, nanmax} ## @end deftypefn function v = nansum (X, varargin) if (nargin < 1) print_usage (); else X(isnan (X)) = 0; v = sum (X, varargin{:}); endif endfunction %!assert (nansum ([2 4 NaN 7]), 13) %!assert (nansum ([2 4 NaN Inf]), Inf) %!assert (nansum ([1 NaN 3; NaN 5 6; 7 8 NaN]), [8 13 9]) %!assert (nansum ([1 NaN 3; NaN 5 6; 7 8 NaN], 2), [4; 11; 15]) %!assert (nansum (single ([1 NaN 3; NaN 5 6; 7 8 NaN])), single ([8 13 9])) %!assert (nansum (single ([1 NaN 3; NaN 5 6; 7 8 NaN]), "double"), [8 13 9]) %!assert (nansum (uint8 ([2 4 1 7])), 14) %!assert (nansum (uint8 ([2 4 1 7]), "native"), uint8 (14)) %!assert (nansum (uint8 ([2 4 1 7])), 14) statistics-release-1.7.3/inst/normalise_distribution.m000066400000000000000000000220251475240274700232720ustar00rootroot00000000000000## Copyright (C) 2011 Alexander Klein ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{NORMALISED} =} normalise_distribution (@var{DATA}) ## @deftypefnx {statistics} {@var{NORMALISED} =} normalise_distribution (@var{DATA}, @var{DISTRIBUTION}) ## @deftypefnx {statistics} {@var{NORMALISED} =} normalise_distribution (@var{DATA}, @var{DISTRIBUTION}, @var{DIMENSION}) ## ## Transform a set of data so as to be N(0,1) distributed according to an idea ## by van Albada and Robinson. ## ## This is achieved by first passing it through its own cumulative distribution ## function (CDF) in order to get a uniform distribution, and then mapping ## the uniform to a normal distribution. ## ## The data must be passed as a vector or matrix in @var{DATA}. ## If the CDF is unknown, then [] can be passed in @var{DISTRIBUTION}, and in ## this case the empirical CDF will be used. ## Otherwise, if the CDFs for all data are known, they can be passed in ## @var{DISTRIBUTION}, ## either in the form of a single function name as a string, ## or a single function handle, ## or a cell array consisting of either all function names as strings, ## or all function handles. ## In the latter case, the number of CDFs passed must match the number ## of rows, or columns respectively, to normalise. ## If the data are passed as a matrix, then the transformation will ## operate either along the first non-singleton dimension, ## or along @var{DIMENSION} if present. ## ## Notes: ## The empirical CDF will map any two sets of data ## having the same size and their ties in the same places after sorting ## to some permutation of the same normalised data: ## @example ## @code{normalise_distribution([1 2 2 3 4])} ## @result{} -1.28 0.00 0.00 0.52 1.28 ## ## @code{normalise_distribution([1 10 100 10 1000])} ## @result{} -1.28 0.00 0.52 0.00 1.28 ## @end example ## ## Original source: ## S.J. van Albada, P.A. Robinson ## "Transformation of arbitrary distributions to the ## normal distribution with application to EEG ## test-retest reliability" ## Journal of Neuroscience Methods, Volume 161, Issue 2, ## 15 April 2007, Pages 205-211 ## ISSN 0165-0270, 10.1016/j.jneumeth.2006.11.004. ## (http://www.sciencedirect.com/science/article/pii/S0165027006005668) ## @end deftypefn function normalised = normalise_distribution (data, distribution, dimension) if (nargin < 1 || nargin > 3) print_usage; elseif (! ismatrix (data) || length (size (data)) > 2) error (strcat (["normalise_distribution: first argument"], ... [" must be a vector or matrix."])); endif if (nargin >= 2) if (! isempty (distribution)) ## Wrap a single handle in a cell array. if (strcmp (typeinfo (distribution), typeinfo (@(x)(x)))) distribution = {distribution}; ## Do we have a string argument instead? elseif (ischar (distribution)) ## Is it a single string? if (rows (distribution) == 1) temp = str2func ([distribution]); distribution = {temp}; else error (strcat (["normalise_distribution: second argument cannot"], ... [" contain more than one string unless in a cell"], ... [" array."])); endif ## Do we have a cell array of distributions instead? elseif (iscell (distribution)) ## Does it consist of strings only? if (all (cellfun (@ischar, distribution))) distribution = cellfun (@str2func, distribution, ... "UniformOutput", false ); endif ## Does it eventually consist of function handles only if (! all (cellfun (@(h) (strcmp (typeinfo (h), typeinfo ... (@(x)(x)))), distribution))) error (strcat (["normalise_distribution: second argument must"], ... [" contain either a single function name or"], ... [" handle or a cell array of either all function"], ... [" names or handles!"])); endif else error ( "Illegal second argument: ", typeinfo ( distribution ) ); endif endif else distribution = []; endif if (nargin == 3) if (! isscalar (dimension) || (dimension != 1 && dimension != 2)) error ("normalise_distribution: third argument must be either 1 or 2."); endif else if (isvector (data) && rows (data) == 1) dimension = 2; else dimension = 1; endif endif trp = (dimension == 2); if (trp) data = data'; endif r = rows (data); c = columns (data); normalised = NA (r, c); ## Do we know the distribution of the sample? if (isempty (distribution)) precomputed_normalisation = []; for k = 1 : columns ( data ) ## Note that this line is in accordance with equation (16) in the ## original text. The author's original program, however, produces ## different values in the presence of ties, namely those you'd ## get replacing "last" by "first". [uniq, indices] = unique (sort (data(:, k)), "last"); ## Does the sample have ties? if (rows (uniq) != r) ## Transform to uniform, then normal distribution. uniform = ( indices - 1/2 ) / r; normal = norminv ( uniform ); else ## Without ties everything is pretty much straightforward as ## stated in the text. if (isempty (precomputed_normalisation)) precomputed_normalisation = norminv (1 / (2*r) : 1/r : 1 - 1 / (2*r)); endif normal = precomputed_normalisation; endif ## Find the original indices in the unsorted sample. ## This somewhat quirky way of doing it is still faster than ## using a for-loop. [ ignore, ignore, target_indices ] = unique ( data (:, k ) ); ## Put normalised values in the places where they belong. f_remap = @( k ) ( normal ( k ) ); normalised ( :, k ) = arrayfun ( f_remap, target_indices ); endfor else ## With known distributions, everything boils down to a few lines of code ##The same distribution for all data? if (all (size (distribution) == 1)) normalised = norminv (distribution{1,1}(data)); elseif (length (vec (distribution)) == c) for k = 1 : c normalised (:, k) = norminv (distribution{k}(data)(:, k)); endfor else error (strcat (["normalise_distribution: number of distributions"], ... [" does not match data size!"])); endif endif if (trp) normalised = normalised'; endif endfunction %!test %! v = normalise_distribution ([1 2 3], [], 1); %! assert (v, [0 0 0]) %!test %! v = normalise_distribution ([1 2 3], [], 2); %! assert (v, norminv ([1 3 5] / 6), 3 * eps) %!test %! v = normalise_distribution ([1 2 3]', [], 2); %! assert (v, [0 0 0]') %!test %! v = normalise_distribution ([1 2 3]', [], 1); %! assert (v, norminv ([1 3 5]' / 6), 3 * eps) %!test %! v = normalise_distribution ([1 1 2 2 3 3], [], 2); %! assert (v, norminv ([3 3 7 7 11 11] / 12), 3 * eps) %!test %! v = normalise_distribution ([1 1 2 2 3 3]', [], 1); %! assert (v, norminv ([3 3 7 7 11 11]' / 12), 3 * eps) %!test %! A = randn ( 10 ); %! N = normalise_distribution (A, @normcdf); %! assert (A, N, 10000 * eps) %!test %! A = exprnd (1, 100); %! N = normalise_distribution (A, @(x)(expcdf (x, 1))); %! assert (mean (vec (N)), 0, 0.1) %! assert (std (vec (N)), 1, 0.1) %!test %! A = rand (1000,1); %! N = normalise_distribution (A, {@(x)(unifcdf (x, 0, 1))}); %! assert (mean (vec (N)), 0, 0.2) %! assert (std (vec (N)), 1, 0.1) %!test %! A = [rand(1000,1), randn(1000, 1)]; %! N = normalise_distribution (A, {@(x)(unifcdf (x, 0, 1)), @normcdf}); %! assert (mean (N), [0, 0], 0.2) %! assert (std (N), [1, 1], 0.1) %!test %! A = [rand(1000,1), randn(1000, 1), exprnd(1, 1000, 1)]'; %! N = normalise_distribution (A, {@(x)(unifcdf (x, 0, 1)); @normcdf; @(x)(expcdf (x, 1))}, 2); %! assert (mean (N, 2), [0, 0, 0]', 0.2); %! assert (std (N, [], 2), [1, 1, 1]', 0.1); %!xtest %! A = exprnd (1, 1000, 9); A (300:500, 4:6) = 17; %! N = normalise_distribution (A); %! assert (mean (N), [0 0 0 0.38 0.38 0.38 0 0 0], 0.1); %! assert (var (N), [1 1 1 2.59 2.59 2.59 1 1 1], 0.1); %!test %!error normalise_distribution (zeros (3, 4), ... %! {@(x)(unifcdf (x, 0, 1)); @normcdf; @(x)(expcdf (x,1))}); statistics-release-1.7.3/inst/normplot.m000066400000000000000000000140551475240274700203600ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## Based on previous work by Paul Kienzle originally ## granted to the public domain. ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} normplot (@var{x}) ## @deftypefnx {Function File} {} normplot (@var{ax}, @var{x}) ## @deftypefnx {Function File} {@var{h} =} normplot (@dots{}) ## ## Produce normal probability plot of the data in @var{x}. If @var{x} is a ## matrix, @code{normplot} plots the data for each column. NaN values are ## ignored. ## ## @code{@var{h} = normplot (@var{ax}, @var{x})} takes a handle @var{ax} in ## addition to the data in @var{x} and it uses that axes for ploting. You may ## get this handle of an existing plot with @code{gca}. ## ## The line joing the 1st and 3rd quantile is drawn solid whereas its extensions ## to both ends are dotted. If the underlying distribution is normal, the ## points will cluster around the solid part of the line. Other distribution ## types will introduce curvature in the plot. ## ## @seealso{cdfplot, wblplot} ## @end deftypefn function h = normplot (varargin) ## Check for valid input arguments narginchk (1, 2); ## Parse input arguments if (nargin == 1) ax = []; x = varargin{1}; else ax = varargin{1}; ## Check that ax is a valid axis handle try isstruct (get (ax)); catch error ("normplot: invalid handle %f.", ax); end_try_catch x = varargin{2}; endif ## Check that x is a vector or a 2-D matrix if (isscalar (x) || ndims (x) > 2) error ("normplot: x must be a vecctor or a 2-D matrix handle."); endif ## If x is a vector, make it a column vector if (rows (x) == 1) x = x(:); endif ## If ax is empty, create a new axes if (isempty (ax)) ax = newplot(); end ## Get number of column vectors in x col = size (x, 2); ## Process each column and plot data and fit lines color = {"blue", "black", "cyan", "green", "magenta", "red", "white", "yellow"}; hold on; for i = 1:col xc = x(:,i); ## Remove NaNs, get min, max, and range xc(isnan(xc)) = []; if (isempty (xc)) break; endif ## Transform data row_xc = rows (xc); yc = norminv (([1:row_xc]' - 0.5) / row_xc); xc = sort(xc); ## Find quartiles q1x = prctile(xc,25); q3x = prctile(xc,75); q1y = prctile(yc,25); q3y = prctile(yc,75); qx = [q1x; q3x]; qy = [q1y; q3y]; ## Calculate coordinates and limits for fitting lines dx = q3x - q1x; dy = q3y - q1y; slope = dy ./ dx; centerx = (q1x + q3x)/2; centery = (q1y + q3y)/2; maxx = max (xc); minx = min (xc); maxy = centery + slope.*(maxx - centerx); miny = centery - slope.*(centerx - minx); yinter = centery - slope.*(centerx); mx = [minx; maxx]; my = [miny; maxy]; ## Plot data and corresponding reference lines in the same color, ## following the default color order. Plot reference line first, ## followed by the data, so that data will be on top of reference line. h_end(i) = line (ax, mx, my, "LineStyle", "-.", "Marker", "none", ... "color", color{mod(i,8)}); h_mid(i) = line (ax, qx, qy, "LineStyle", "-", "Marker", "none", ... "color", color{mod(i,8)}); h_dat(i) = line (ax, xc, yc, "LineStyle", "none", "Marker", "+", ... "color", color{mod(i,8)}); endfor hold off; ## Change colors for single column vector if (i == 1) set (h_dat, "Color", "b"); set (h_mid, "Color", "r"); set (h_end, "Color", "r"); endif ## Bundle handles together if output requested if (nargout > 0) h = [h_dat, h_mid, h_end]'; endif ## Plot labels title "Normal Probability Plot" ylabel "Probability" xlabel "Data" ## Plot grid p = [0.001, 0.003, 0.01, 0.02, 0.05, 0.10, 0.25, 0.5, ... 0.75, 0.90, 0.95, 0.98, 0.99, 0.997, 0.999]; label = {"0.001", "0.003", "0.01", "0.02", "0.05", "0.10", "0.25", "0.50", ... "0.75", "0.90", "0.95", "0.98", "0.99", "0.997", "0.999"}; tick = norminv (p, 0, 1); set (ax, "ytick", tick, "yticklabel", label); ## Set view range with a bit of space around data range = nanmax (x(:)) - nanmin (x(:)); if (range > 0) minxaxis = nanmin (x(:)) - 0.025 * range; maxxaxis = nanmax (x(:)) + 0.025 * range; else minxaxis = nanmin (x(:)) - 1; maxxaxis = nanmax (x(:)) + 1; end minyaxis = norminv (0.25 ./ row_xc, 0, 1); maxyaxis = norminv ((row_xc - 0.25) ./ row_xc, 0, 1); set (ax, "ylim", [minyaxis, maxyaxis], "xlim", [minxaxis, maxxaxis]); grid (ax, "on"); box (ax, "off"); endfunction %!demo %! h = normplot([1:20]); %!demo %! h = normplot([1:20;5:2:44]'); %!demo %! ax = newplot(); %! h = normplot(ax, [1:20]); %! ax = gca; %! h = normplot(ax, [-10:10]); %! set (ax, "xlim", [-11, 21]); ## Test input validation %!error normplot (); %!error normplot (23); %!error normplot (23, [1:20]); %!error normplot (ones(3,4,5)); ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! ax = newplot (hf); %! h = normplot (ax, [1:20]); %! ax = gca; %! h = normplot(ax, [-10:10]); %! set (ax, "xlim", [-11, 21]); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! h = normplot([1:20;5:2:44]'); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect statistics-release-1.7.3/inst/optimalleaforder.m000066400000000000000000000234171475240274700220410ustar00rootroot00000000000000## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{leafOrder} =} optimalleaforder (@var{tree}, @var{D}) ## @deftypefnx {statistics} {@var{leafOrder} =} optimalleaforder (@dots{}, @var{Name}, @var{Value}) ## ## Compute the optimal leaf ordering of a hierarchical binary cluster tree. ## ## The optimal leaf ordering of a tree is the ordering which minimizes the sum ## of the distances between each leaf and its adjacent leaves, without altering ## the structure of the tree, that is without redefining the clusters of the ## tree. ## ## Required inputs: ## @itemize ## @item ## @var{tree}: a hierarchical cluster tree @var{tree} generated by the ## @code{linkage} function. ## ## @item ## @var{D}: a matrix of distances as computed by @code{pdist}. ## @end itemize ## ## Optional inputs can be the following property/value pairs: ## @itemize ## @item ## property 'Criteria' at the moment can only have the value 'adjacent', ## for minimizing the distances between leaves. ## ## @item ## property 'Transformation' can have one of the values 'linear', 'inverse' ## or a handle to a custom function which computes @var{S} the similarity ## matrix. ## @end itemize ## ## optimalleaforder's output @var{leafOrder} is the optimal leaf ordering. ## ## @strong{Reference} ## Bar-Joseph, Z., Gifford, D.K., and Jaakkola, T.S. Fast optimal leaf ordering ## for hierarchical clustering. Bioinformatics vol. 17 suppl. 1, 2001. ## @end deftypefn ## ## @seealso{dendrogram,linkage,pdist} function leafOrder = optimalleaforder ( varargin ) ## check the input if ( nargin < 2 ) print_usage (); endif tree = varargin{1}; D = varargin{2}; criterion = "adjacent"; # default and only value at the moment transformation = "linear"; if ((columns (tree) != 3) || (! isnumeric (tree)) || ... (! (max (tree(end, 1:2)) == rows (tree) * 2))) error (["optimalleaforder: tree must be a matrix as generated by the " ... "linkage function"]); endif ## read the paired arguments if (! all (cellfun ("ischar", varargin(3:end)))) error ("optimalleaforder: character inputs expected for arguments 3 and up"); else varargin(3:end) = lower (varargin(3:end)); endif pair_index = 3; while (pair_index <= (nargin - 1)) switch (varargin{pair_index}) case "criteria" criterion = varargin{pair_index + 1}; if (strcmp (criterion, "group")) ## MATLAB compatibility: ## the 'group' criterion is not implemented error ("optimalleaforder: unavailable criterion 'group'"); elseif (! strcmp (criterion, "adjacent")) error ("optimalleaforder: invalid criterion %s", criterion); endif case "transformation" transformation = varargin{pair_index + 1}; otherwise error ("optimalleaforder: unknown property %s", varargin{pair_index}); endswitch pair_index += 2; endwhile ## D can be either a vector or a matrix, ## but it is easier to work with a matrix if (isvector (D)) D = squareform (D); endif n = rows (D); m = rows (tree); if (n != (m + 1)) error (["optimalleaforder: D must be a matrix or vector generated by " ... "the pdist function"]); endif ## the similarity matrix, basically an inverted distance matrix S = zeros (n); if (strcmpi (transformation, "linear")) ## linear similarity maxD = max (max (D)); S = maxD - D; elseif (strcmpi (transformation, "inverse")) ## similarity as inverted distance S = 1 ./ D; elseif (is_function_handle (transformation)) ## custom similarity S = feval (transformation, D); else error ("optimalleaforder: invalid transformation %s", transformation); endif ## main body ## for each node v we compute the maximum similarity of the subtree M(w,u,v), ## where the leftmost leaf is w and the rightmost is u; remember that ## M(w,u,v) = M(u,w,v) M = zeros (n, n, n + m); ## O is a utility matrix: for each node of the tree we store the left and ## right leaves of the optimal subtree O = [1:( n + m ); 1:( n + m ); (zeros (1, (n + m)))]'; ## compute M for every node v for iter = 1 : m v = iter + n; # current node l = optimalleaforder_getLeafList (tree(iter, 1)); # the left subtree r = optimalleaforder_getLeafList (tree(iter, 2)); # the right subtree if (tree(iter,1) > n) l_l = optimalleaforder_getLeafList (tree(tree(iter, 1) - n, 1)); l_r = optimalleaforder_getLeafList (tree(tree(iter, 1) - n, 2)); else l_l = l_r = l; endif if (tree(iter,2) > n) r_l = optimalleaforder_getLeafList (tree(tree(iter, 2) - n, 1)); r_r = optimalleaforder_getLeafList (tree(tree(iter, 2) - n, 2)); else r_l = r_r = r; endif ## let's find the maximum value of M(w,u,v) when: w is a leaf of the left ## subtree of v and u is a leaf of the right subtree of v for i = 1 : length (l) if (isempty (find (l(i) == l_l))) x = l_l; else x = l_r; endif for j = 1 : length (r) if (isempty (find (r(j) == r_l))) y = r_l; else y = r_r; endif ## max(M(w,u,v)) = max(M(w,k,v_l)) + max(M(h,u,v_r)) + S(k,h) ## where: v_l is the left child of v and v_r the right child of v M_tmp = repmat (M(l(i), x(:), tree(iter, 1)), length (y), 1) + ... repmat (M(y(:), r(j), tree(iter, 2)), 1, length (x)) + ... S(y(:), x(:)); M_max = max (max (M_tmp)); # this is M(l(i), r(j), v) [h, k] = find (M_tmp == M_max); M(l(i), r(j), v) = M_max; M(r(j), l(i), v) = M(l(i), r(j), v); if (M_max > O(v,3)) O(v, 1) = l(i); # this is w O(v, 2) = r(j); # this is u O(v, 3) = M_max; # this is M(w, u, v) endif endfor endfor endfor ## reordering: ## we found the M(w,u,v) corresponding to the optimal leaf order, now we can ## compute the optimal leaf order given our M(w,u,v) ## the return value leafOrder = zeros ( 1, n ); leafOrder(1) = O(end, 1); leafOrder(n) = O(end, 2); ## the inverse operation, only easier, to get the leaf order: now we know the ## leftmost and rightmost leaves of the best subtree, we may have to flip it ## though for iter = m : -1 : 1 v = iter + n; extremes = O(v, [1, 2]); l_node = tree(iter, 1); r_node = tree(iter, 2); l = optimalleaforder_getLeafList (l_node); r = optimalleaforder_getLeafList (r_node); if (l_node > n) l_l = optimalleaforder_getLeafList (tree(l_node - n, 1)); l_r = optimalleaforder_getLeafList (tree(l_node - n, 2)); else l_l = l_r = l; endif if (r_node > n) r_l = optimalleaforder_getLeafList (tree(r_node - n, 1)); r_r = optimalleaforder_getLeafList (tree(r_node - n, 2)); else r_l = r_r = r; endif ## this means that we need to flip the subtree if (isempty (find (extremes(1) == l))) l_tmp = l; l_l_tmp = l_l; l_r_tmp = l_r; l = r; l_l = r_l; l_r = r_r; r = l_tmp; r_l = l_l_tmp; r_r = l_r_tmp; node_tmp = l_node; l_node = r_node; r_node = node_tmp; endif if (isempty (find (extremes(1) == l_l))) x = l_l; else x = l_r; endif if (isempty (find (extremes(2) == r_l))) y = r_l; else y = r_r; endif M_tmp = repmat (M(extremes(1), x(:), l_node), length (y), 1) + ... repmat (M(y(:), extremes(2), r_node), 1, length (x)) + ... S(y(:), x(:)); M_max = max (max (M_tmp)); [h, k] = find (M_tmp == M_max); O(l_node, 1) = extremes(1); O(l_node, 2) = x(k); O(r_node, 1) = y(h); O(r_node, 2) = extremes(2); p_1 = find (leafOrder == extremes(1)); p_2 = find (leafOrder == extremes(2)); leafOrder (p_1 + (length (l)) - 1) = x(k); leafOrder (p_1 + (length (l))) = y(h); endfor ## function: optimalleaforder_getLeafList ## get the list of leaves under a given node function vector = optimalleaforder_getLeafList (nodes_to_visit) vector = []; while (! isempty (nodes_to_visit)) currentnode = nodes_to_visit(1); nodes_to_visit(1) = []; if (currentnode > n) node = currentnode - n; nodes_to_visit = [tree(node, [2 1]) nodes_to_visit]; endif if (currentnode <= n) vector = [vector currentnode]; endif endwhile endfunction endfunction %!demo %! randn ("seed", 5) # for reproducibility %! X = randn (10, 2); %! D = pdist (X); %! tree = linkage(D, 'average'); %! optimalleaforder (tree, D, 'Transformation', 'linear') ## Test input validation %!error optimalleaforder () %!error optimalleaforder (1) %!error optimalleaforder (ones (2, 2), 1) %!error optimalleaforder ([1 2 3], [1 2; 3 4], "criteria", 5) %!error optimalleaforder ([1 2 1], [1 2 3]) %!error optimalleaforder ([1 2 1], 1, "xxx", "xxx") %!error optimalleaforder ([1 2 1], 1, "Transformation", "xxx") statistics-release-1.7.3/inst/pca.m000066400000000000000000000463661475240274700172630ustar00rootroot00000000000000## Copyright (C) 2013-2019 Fernando Damian Nieuwveldt ## Copyright (C) 2021 Stefano Guidoni ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 3 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{coeff} =} pca (@var{x}) ## @deftypefnx {statistics} {@var{coeff} =} pca (@var{x}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{coeff}, @var{score}, @var{latent}] =} pca (@dots{}) ## @deftypefnx {statistics} {[@var{coeff}, @var{score}, @var{latent}, @var{tsquared}] =} pca (@dots{}) ## @deftypefnx {statistics} {[@var{coeff}, @var{score}, @var{latent}, @var{tsquared}, @var{explained}, @var{mu}] =} pca (@dots{}) ## ## Performs a principal component analysis on a data matrix. ## ## A principal component analysis of a data matrix of @math{N} observations in a ## @math{D} dimensional space returns a @math{DxD} transformation matrix, to ## perform a change of basis on the data. The first component of the new basis ## is the direction that maximizes the variance of the projected data. ## ## Input argument: ## @itemize @bullet ## @item ## @var{x} : a @math{NxD} data matrix ## @end itemize ## ## The following @var{Name}, @var{Value} pair arguments can be used: ## @itemize @bullet ## @item ## @qcode{"Algorithm"} defines the algorithm to use: ## @itemize ## @item @qcode{"svd"} (default), for singular value decomposition ## @item @qcode{"eig"} for eigenvalue decomposition ## @end itemize ## ## @item ## @qcode{"Centered"} is a boolean indicator for centering the observation data. ## It is @code{true} by default. ## @item ## ## @qcode{"Economy"} is a boolean indicator for the economy size output. It is ## @code{true} by default. Hence, @code{pca} returns only the elements of ## @var{latent} that are not necessarily zero, and the corresponding columns of ## @var{coeff} and @var{score}, that is, when @math{N <= D}, only the first ## @math{N - 1}. ## ## @item ## @qcode{"NumComponents"} defines the number of components @math{k} to return. ## If @math{k < p}, then only the first @math{k} columns of @var{coeff} and ## @var{score} are returned. ## ## @item ## @qcode{"Rows"} defines how to handle missing values: ## @itemize ## @item @qcode{"complete"} (default), missing values are removed before ## computation. ## @item @qcode{"pairwise"} (only valid when @qcode{"Algorithm"} is ## @qcode{"eig"}), the covariance of rows with missing data is computed using ## the available data, but the covariance matrix could be not positive definite, ## which triggers the termination of @code{pca}. ## @item @qcode{"complete"}, missing values are not allowed, @code{pca} ## terminates with an error if there are any. ## @end itemize ## ## @item ## @qcode{"Weights"} defines observation weights as a vector of positive values ## of length @math{N}. ## ## @item ## @qcode{"VariableWeights"} defines variable weights: ## @itemize ## @item a @var{vector} of positive values of length @math{D}. ## @item the string @qcode{"variance"} to use the sample variance as weights. ## @end itemize ## @end itemize ## ## Return values: ## @itemize @bullet ## @item ## @var{coeff} : the principal component coefficients, a @math{DxD} ## transformation matrix ## @item ## @var{score} : the principal component scores, the representation of @var{x} ## in the principal component space ## @item ## @var{latent} : the principal component variances, i.e., the eigenvalues of ## the covariance matrix of @var{x} ## @item ## @var{tsquared} : Hotelling's T-squared Statistic for each observation in ## @var{x} ## @item ## @var{explained} : the percentage of the variance explained by each principal ## component ## @item ## @var{mu} : the estimated mean of each variable of @var{x}, it is zero if the ## data are not centered ## @end itemize ## ## Matlab compatibility note: the alternating least square method 'als' and ## associated options 'Coeff0', 'Score0', and 'Options' are not yet implemented ## ## @subheading References ## @enumerate ## @item ## Jolliffe, I. T., Principal Component Analysis, 2nd Edition, Springer, 2002 ## @end enumerate ## ## @seealso{barttest, factoran, pcacov, pcares} ## @end deftypefn ## BUGS: ## - tsquared with weights (xtest below) ##FIXME ## -- can change isnan to ismissing once the latter is implemented in Octave ## -- change mystd to std and remove the helper function mystd once weighting is available in the Octave function function [coeff, score, latent, tsquared, explained, mu] = pca (x, varargin) if (nargin < 1) print_usage (); endif [nobs, nvars] = size (x); ## default options optAlgorithmS = "svd"; optCenteredB = true; optEconomyB = true; optNumComponentsI = nvars; optWeights = []; optVariableWeights = []; optRowsB = false; TF = []; ## parse parameters pair_index = 1; while (pair_index <= (nargin - 1)) switch (lower (varargin{pair_index})) ## decomposition algorithm: singular value decomposition, eigenvalue ## decomposition or (currently unavailable) alternating least square case "algorithm" optAlgorithmS = varargin{pair_index + 1}; switch (optAlgorithmS) case {"svd", "eig"} ; case "als" error ("pca: alternating least square algorithm not implemented"); otherwise error ("pca: invalid algorithm %s", optAlgorithmS); endswitch ## centering of the columns, around the mean case "centered" if (isbool (varargin{pair_index + 1})) optCenteredB = varargin{pair_index + 1}; else error ("pca: 'centered' requires a boolean value"); endif ## limit the size of the output to the degrees of freedom, when a smaller ## number than the number of variables case "economy" if (isbool (varargin{pair_index + 1})) optEconomyB = varargin{pair_index + 1}; else error ("pca: 'economy' requires a boolean value"); endif ## choose the number of components to show case "numcomponents" optNumComponentsI = varargin{pair_index + 1}; if ((! isscalar (optNumComponentsI)) || (! isnumeric (optNumComponentsI)) || optNumComponentsI != floor (optNumComponentsI) || optNumComponentsI <= 0 || optNumComponentsI > nvars) error (["pca: the number of components must be a positive integer"... "number smaller or equal to the number of variables"]); endif ## observation weights: some observations can be more accurate than others case "weights" optWeights = varargin{pair_index + 1}; if ((! isvector (optWeights)) || length (optWeights) != nobs || length (find (optWeights < 0)) > 0) error ("pca: weights must be a numerical array of positive numbers"); endif if (rows (optWeights) == 1 ) optWeights = transpose (optWeights); endif ## variable weights: weights used for the variables case "variableweights" optVariableWeights = varargin{pair_index + 1}; if (ischar (optVariableWeights) && strcmpi (optVariableWeights, "variance")) optVariableWeights = "variance"; # take care of this later elseif ((! isvector (optVariableWeights)) || length (optVariableWeights) != nvars || (! isnumeric (optVariableWeights)) || length (find (optVariableWeights < 0)) > 0) error (["pca: variable weights must be a numerical array of "... "positive numbers or the string 'variance'"]); else optVariableWeights = 1 ./ sqrt (optVariableWeights); ## it is used as a row vector if (columns (optVariableWeights) == 1 ) optVariableWeights = transpose (optVariableWeights); endif endif ## rows: policy for missing values case "rows" switch (varargin{pair_index + 1}) case "complete" optRowsB = false; case "pairwise" optRowsB = true; case "all" if (any (isnan (x))) error (["pca: when all rows are requested the dataset cannot"... " include NaN values"]); endif otherwise error ("pca: %s is an invalid value for rows", ... varargin{pair_index + 1}); endswitch case {"coeff0", "score0", "options"} error (strcat (["pca: parameter %s is only valid with the 'als'"], ... [" method, which is not yet implemented"]), ... varargin{pair_index}); otherwise error ("pca: unknown property %s", varargin{pair_index}); endswitch pair_index += 2; endwhile ## Preparing the dataset according to the chosen policy for missing values if (optRowsB) if (! strcmp (optAlgorithmS, "eig")) optAlgorithmS = "eig"; warning (["pca: setting algorithm to 'eig' because 'rows' option is "... "set to 'pairwise'"]); endif TF = isnan (x); missingRows = zeros (nobs, 1); nmissing = 0; else ## "complete": remove all the rows with missing values TF = isnan (x); missingRows = any (TF, 2); nmissing = sum (missingRows); endif ## indices of the available rows ridcs = find (missingRows == 0); ## Center the columns to mean zero if requested if (optCenteredB) if (isempty (optWeights) && nmissing == 0 && ! optRowsB) ## no weights and no missing values mu = mean (x); elseif (nmissing == 0 && ! optRowsB) ## weighted observations: some observations are more valuable, i.e. they ## can be trusted more mu = sum (optWeights .* x) ./ sum (optWeights); else ## missing values: the mean is computed column by column mu = zeros (1, nvars); if (isempty (optWeights)) for iter = 1 : nvars mu(iter) = mean (x(find (TF(:, iter) == 0), iter)); endfor else ## weighted mean with missing data for iter = 1 : nvars mu(iter) = sum (x(find (TF(:, iter) == 0), iter) .* ... optWeights(find (TF(:, iter) == 0))) ./ ... sum (optWeights(find (TF(:, iter) == 0))); endfor endif endif Xc = x - mu; else Xc = x; ## The mean of the variables of the original dataset: ## return zero if the dataset is not centered mu = zeros (1, nvars); endif ## Change the columns according to the variable weights if (! isempty (optVariableWeights)) if (ischar (optVariableWeights)) if (isempty (optWeights)) sqrtBias = 1; # see below optVariableWeights = std (x); else ## unbiased variance estimation: the bias when using reliability weights ## is 1 - var(weights) / std(weigths)^2 sqrtBias = sqrt (1 - (sumsq (optWeights) / sum (optWeights) ^ 2)); optVariableWeights = mystd (x, optWeights) / sqrtBias; endif endif Xc = Xc ./ optVariableWeights; endif ## Compute the observation weight matrix if (isempty (optWeights)) Wd = eye (nobs - nmissing); else Wd = diag (optWeights) ./ sum (optWeights); endif ## Compute the coefficients switch (optAlgorithmS) case "svd" ## Check if there are more variables than observations if (nvars <= nobs) [U, S, coeff] = svd (sqrt (Wd) * Xc(ridcs,:), "econ"); else ## Calculate the svd on the transpose matrix, much faster if (optEconomyB) [coeff, S, V] = svd (Xc(ridcs,:)' * sqrt (Wd), "econ"); else [coeff, S, V] = svd (Xc(ridcs,:)' * sqrt (Wd)); endif endif case "eig" ## this method requires the computation of the sample covariance matrix if (optRowsB) ## pairwise: ## in this case the degrees of freedom for each element of the matrix ## are equal to the number of valid rows for the couple of columns ## used to compute the element Xpairwise = Xc; Xpairwise(find (isnan (Xc))) = 0; Ndegrees = (nobs - 1) * ones (nvars, nvars); for i_iter = 1 : nvars for j_iter = i_iter : nvars Ndegrees(i_iter, j_iter) = Ndegrees(i_iter, j_iter) - ... sum (any (TF(:,[i_iter j_iter]), 2)); Ndegrees(j_iter, i_iter) = Ndegrees(i_iter, j_iter); endfor endfor Mcov = Xpairwise' * Wd * Xpairwise ./ Ndegrees; else ## the degrees of freedom are not really important here ndegrees = nobs - nmissing - 1; Mcov = Xc(ridcs, :)' * Wd * Xc(ridcs, :) / ndegrees; endif [coeff, S] = eigs (Mcov, nvars); endswitch ## Change the coefficients according to the variable weights if (! isempty (optVariableWeights)) coeff = coeff .* transpose (optVariableWeights); endif ## MATLAB compatibility: the sign convention is that the ## greatest absolute value for each column is positive switchSignV = find (max (coeff) < abs (min (coeff))); if (! isempty (switchSignV)) coeff(:, switchSignV) = -1 * coeff(:, switchSignV); endif ## Compute the scores if (nargout > 1) ## This is for the score when using variable weights, it is not really ## a new definition of Xc if (! isempty (optVariableWeights)) Xc = Xc ./ optVariableWeights; endif ## Get the Scores score = Xc(ridcs,:) * coeff; ## Get the rank of the score matrix r = rank (score); ## If there is missing data, put it back ## FIXME: this needs tests if (nmissing) scoretmp = zeros (nobs, nvars); scoretmp(find (missingRows == 0), :) = score; scoretmp(find (missingRows), :) = NaN; score = scoretmp; endif ## Only use the first r columns, pad rest with zeros if economy != true score = score(:, 1:r) ; if (! optEconomyB) score = [score, (zeros (nobs , nvars-r))]; else coeff = coeff(: , 1:r); endif endif ## Compute the variances if (nargout > 2) ## degrees of freedom: n - 1 for centered data if (optCenteredB) dof = size (Xc(ridcs,:), 1) - 1; else dof = size (Xc(ridcs,:), 1); endif ## This is the same as the eigenvalues of the covariance matrix of x if (strcmp (optAlgorithmS, "eig")) latent = diag (S, 0); else latent = (diag (S'*S) / dof)(1:r); endif ## If observation weights were used, we need to scale back these values if (! isempty (optWeights)) latent = latent .* sum (optWeights(ridcs)); endif if (! optEconomyB) latent= [latent; (zeros (nvars - r, 1))]; endif endif ## Compute the Hotelling T-square statistics ## MATLAB compatibility: when using weighted observations the T-square ## statistics differ by some rounding error if (nargout > 3) ## Calculate the Hotelling T-Square statistic for the observations ## formally: tsquared = sumsq (zscore (score(:, 1:r)),2); if (! isempty (optWeights)) ## probably splitting the weights, using the square roots, is not the ## best solution, numerically weightedScore = score .* sqrt (optWeights); tsquared = mahal (weightedScore(ridcs, 1:r), weightedScore(ridcs, 1:r))... ./ optWeights; else tsquared = mahal (score(ridcs, 1:r), score(ridcs, 1:r)); endif endif ## Compute the variance explained by each principal component if (nargout > 4) explained = 100 * latent / sum (latent); endif ## When a number of components is chosen, the coefficients and score matrix ## only show that number of columns if (optNumComponentsI != nvars) coeff = coeff(:, 1:optNumComponentsI); endif if (optNumComponentsI != nvars && nargout > 1) score = score(:, 1:optNumComponentsI); endif endfunction #return the weighted standard deviation function retval = mystd (x, w) (dim = find (size(x) != 1, 1)) || (dim = 1); den = sum (w); mu = sum (w .* x, dim) ./ sum (w); retval = sum (w .* ((x - mu) .^ 2), dim) / den; retval = sqrt (retval); endfunction %!shared COEFF,SCORE,latent,tsquare,m,x,R,V,lambda,i,S,F #NIST Engineering Statistics Handbook example (6.5.5.2) %!test %! x=[7 4 3 %! 4 1 8 %! 6 3 5 %! 8 6 1 %! 8 5 7 %! 7 2 9 %! 5 3 3 %! 9 5 8 %! 7 4 5 %! 8 2 2]; %! R = corrcoef (x); %! [V, lambda] = eig (R); %! [~, i] = sort(diag(lambda), "descend"); #arrange largest PC first %! S = V(:, i) * diag(sqrt(diag(lambda)(i))); %!assert(diag(S(:, 1:2)*S(:, 1:2)'), [0.8662; 0.8420; 0.9876], 1E-4); #contribution of first 2 PCs to each original variable %! B = V(:, i) * diag( 1./ sqrt(diag(lambda)(i))); %! F = zscore(x)*B; %! [COEFF,SCORE,latent,tsquare] = pca(zscore(x, 1)); %!assert(tsquare,sumsq(F, 2),1E4*eps); %!test %! x=[1,2,3;2,1,3]'; %! [COEFF,SCORE,latent,tsquare] = pca(x, "Economy", false); %! m=[sqrt(2),sqrt(2);sqrt(2),-sqrt(2);-2*sqrt(2),0]/2; %! m(:,1) = m(:,1)*sign(COEFF(1,1)); %! m(:,2) = m(:,2)*sign(COEFF(1,2)); %!assert(COEFF,m(1:2,:),10*eps); %!assert(SCORE,-m,10*eps); %!assert(latent,[1.5;.5],10*eps); %!assert(tsquare,[4;4;4]/3,10*eps); #test with observation weights (using Matlab's results for this case as a reference) %! [COEFF,SCORE,latent,tsquare] = pca(x, "Economy", false, "weights", [1 2 1], "variableweights", "variance"); %!assert(COEFF, [0.632455532033676 -0.632455532033676; 0.741619848709566 0.741619848709566], 10*eps); %!assert(SCORE, [-0.622019449426284 0.959119380657905; -0.505649896847432 -0.505649896847431; 1.633319243121148 0.052180413036957], 10*eps); %!assert(latent, [1.783001790889027; 0.716998209110974], 10*eps); %!xtest assert(tsquare, [1.5; 0.5; 1.5], 10*eps); #currently, [4; 2; 4]/3 is actually returned; see comments above %!test %! x=x'; %! [COEFF,SCORE,latent,tsquare] = pca(x, "Economy", false); %! m=[sqrt(2),sqrt(2),0;-sqrt(2),sqrt(2),0;0,0,2]/2; %! m(:,1) = m(:,1)*sign(COEFF(1,1)); %! m(:,2) = m(:,2)*sign(COEFF(1,2)); %! m(:,3) = m(:,3)*sign(COEFF(3,3)); %!assert(COEFF,m,10*eps); %!assert(SCORE(:,1),-m(1:2,1),10*eps); %!assert(SCORE(:,2:3),zeros(2),10*eps); %!assert(latent,[1;0;0],10*eps); %!assert(tsquare,[0.5;0.5],10*eps) %!test %! [COEFF,SCORE,latent,tsquare] = pca(x); %!assert(COEFF,m(:, 1),10*eps); %!assert(SCORE,-m(1:2,1),10*eps); %!assert(latent,[1],10*eps); %!assert(tsquare,[0.5;0.5],10*eps) %!error pca([1 2; 3 4], "Algorithm", "xxx") %!error <'centered' requires a boolean value> pca([1 2; 3 4], "Centered", "xxx") %!error pca([1 2; 3 4], "NumComponents", -4) %!error pca([1 2; 3 4], "Rows", 1) %!error pca([1 2; 3 4], "Weights", [1 2 3]) %!error pca([1 2; 3 4], "Weights", [-1 2]) %!error pca([1 2; 3 4], "VariableWeights", [-1 2]) %!error pca([1 2; 3 4], "VariableWeights", "xxx") %!error pca([1 2; 3 4], "XXX", 1) statistics-release-1.7.3/inst/pcacov.m000066400000000000000000000104201475240274700177510ustar00rootroot00000000000000## Copyright (C) 2013-2019 Fernando Damian Nieuwveldt ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 3 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{coeff} =} pcacov (@var{K}) ## @deftypefnx {statistics} {[@var{coeff}, @var{latent}] =} pcacov (@var{K}) ## @deftypefnx {statistics} {[@var{coeff}, @var{latent}, @var{explained}] =} pcacov (@var{K}) ## ## Perform principal component analysis on covariance matrix ## ## @code{@var{coeff} = pcacov (@var{K})} performs principal component analysis ## on the square covariance matrix @var{K} and returns the principal component ## coefficients, also known as loadings. The columns are in order of decreasing ## component variance. ## ## @code{[@var{coeff}, @var{latent}] = pcacov (@var{K})} also returns a vector ## with the principal component variances, i.e. the eigenvalues of @var{K}. ## @var{latent} has a length of @qcode{size (@var{coeff}, 1)}. ## ## @code{[@var{coeff}, @var{latent}, @var{explained}] = pcacov (@var{K})} also ## returns a vector with the percentage of the total variance explained by each ## principal component. @var{explained} has the same size as @var{latent}. ## The entries in @var{explained} range from 0 (none of the variance is ## explained) to 100 (all of the variance is explained). ## ## @code{pcacov} does not standardize @var{K} to have unit variances. In order ## to perform principal component analysis on standardized variables, use the ## correlation matrix @qcode{@var{R} = @var{K} ./ (@var{SD} * @var{SD}')}, where ## @qcode{@var{SD} = sqrt (diag (@var{K}))}, in place of @var{K}. To perform ## principal component analysis directly on the data matrix, use @code{pca}. ## ## @subheading References ## @enumerate ## @item ## Jolliffe, I. T., Principal Component Analysis, 2nd Edition, Springer, 2002 ## @end enumerate ## ## @seealso{bartlett, factoran, pcares, pca} ## @end deftypefn function [coeff, latent, explained] = pcacov (K) ## Check X being a square matrix if (ndims (K) != 2 || size (K, 1) != size (K, 2)) error ("pcacov: K must be a square matrix."); endif [U, S, V] = svd (K); ## Force a sign convention on the coefficients so that ## the largest element in each column has a positive sign [row, col] = size (U); [~, m_ind] = max (abs (U), [], 1); csign = sign (U (m_ind + (0:row:(col - 1) * row))); coeff = bsxfun (@times, U, csign); ## Compute extra output arguments if (nargout > 1) latent = diag (S); endif if (nargout > 2) explained = 100 * latent ./ sum (latent); endif endfunction %!demo %! x = [ 7 26 6 60; %! 1 29 15 52; %! 11 56 8 20; %! 11 31 8 47; %! 7 52 6 33; %! 11 55 9 22; %! 3 71 17 6; %! 1 31 22 44; %! 2 54 18 22; %! 21 47 4 26; %! 1 40 23 34; %! 11 66 9 12; %! 10 68 8 12 %! ]; %! Kxx = cov (x); %! [coeff, latent, explained] = pcacov (Kxx) ## Test output %!test %! load hald %! Kxx = cov (ingredients); %! [coeff,latent,explained] = pcacov(Kxx); %! c_out = [-0.0678, -0.6460, 0.5673, 0.5062; ... %! -0.6785, -0.0200, -0.5440, 0.4933; ... %! 0.0290, 0.7553, 0.4036, 0.5156; ... %! 0.7309, -0.1085, -0.4684, 0.4844]; %! l_out = [517.7969; 67.4964; 12.4054; 0.2372]; %! e_out = [ 86.5974; 11.2882; 2.0747; 0.0397]; %! assert (coeff, c_out, 1e-4); %! assert (latent, l_out, 1e-4); %! assert (explained, e_out, 1e-4); ## Test input validation %!error pcacov (ones (2,3)) %!error pcacov (ones (3,3,3)) statistics-release-1.7.3/inst/pcares.m000066400000000000000000000102411475240274700177540ustar00rootroot00000000000000## Copyright (C) 2013-2019 Fernando Damian Nieuwveldt ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 3 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{residuals} =} pcares (@var{x}, @var{ndim}) ## @deftypefnx {statistics} {[@var{residuals}, @var{reconstructed}] =} pcares (@var{x}, @var{ndim}) ## ## Calculate residuals from principal component analysis. ## ## @code{@var{residuals} = pcares (@var{x}, @var{ndim})} returns the residuals ## obtained by retaining @var{ndim} principal components of the @math{NxD} ## matrix @var{x}. Rows of @var{x} correspond to observations, columns of ## @var{x} correspond to variables. @var{ndim} is a scalar and must be less ## than or equal to @math{D}. @var{residuals} is a matrix of the same size as ## @var{x}. Use the data matrix, not the covariance matrix, with this function. ## ## @code{[@var{residuals}, @var{reconstructed}] = pcares (@var{x}, @var{ndim})} ## returns the reconstructed observations, i.e. the approximation to @var{x} ## obtained by retaining its first @var{ndim} principal components. ## ## @code{pcares} does not normalize the columns of @var{x}. Use ## @qcode{pcares (zscore (@var{x}), @var{ndim})} in order to perform the ## principal components analysis based on standardized variables, i.e. based on ## correlations. Use @code{pcacov} in order to perform principal components ## analysis directly on a covariance or correlation matrix without constructing ## residuals. ## ## @subheading References ## @enumerate ## @item ## Jolliffe, I. T., Principal Component Analysis, 2nd Edition, Springer, 2002 ## @end enumerate ## ## @seealso{factoran, pcacov, pca} ## @end deftypefn function [residuals, reconstructed] = pcares (x, ndim) ## Check input arguments if (nargin < 2) error ("pcares: too few input arguments."); endif if (ndim > size (x, 2)) error ("pcares: NDIM must be less than or equal to the column of X."); endif ## Mean center data Xcentered = bsxfun (@minus, x, mean (x)); ## Apply svd to get the principal component coefficients [U, S, V] = svd (Xcentered); ## Use only the first ndim PCA components v = V(:,1:ndim); ## Calculate the residuals residuals = Xcentered - Xcentered * (v * v'); ## Compute extra output arguments if (nargout > 1) ## Reconstructed data using ndim PCA components reconstructed = x - residuals; endif endfunction %!demo %! x = [ 7 26 6 60; %! 1 29 15 52; %! 11 56 8 20; %! 11 31 8 47; %! 7 52 6 33; %! 11 55 9 22; %! 3 71 17 6; %! 1 31 22 44; %! 2 54 18 22; %! 21 47 4 26; %! 1 40 23 34; %! 11 66 9 12; %! 10 68 8 12]; %! %! ## As we increase the number of principal components, the norm %! ## of the residuals matrix will decrease %! r1 = pcares (x,1); %! n1 = norm (r1) %! r2 = pcares (x,2); %! n2 = norm (r2) %! r3 = pcares (x,3); %! n3 = norm (r3) %! r4 = pcares (x,4); %! n4 = norm (r4) ## Test output %!test %! load hald %! r1 = pcares (ingredients,1); %! r2 = pcares (ingredients,2); %! r3 = pcares (ingredients,3); %! assert (r1(1,:), [2.0350, 2.8304, -6.8378, 3.0879], 1e-4); %! assert (r2(1,:), [-2.4037, 2.6930, -1.6482, 2.3425], 1e-4); %! assert (r3(1,:), [ 0.2008, 0.1957, 0.2045, 0.1921], 1e-4); ## Test input validation %!error pcares (ones (20, 3)) %!error ... %! pcares (ones (30, 2), 3) statistics-release-1.7.3/inst/pdist.m000066400000000000000000000320571475240274700176330ustar00rootroot00000000000000## Copyright (C) 2008 Francesco Potortì ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{D} =} pdist (@var{X}) ## @deftypefnx {statistics} {@var{D} =} pdist (@var{X}, @var{Distance}) ## @deftypefnx {statistics} {@var{D} =} pdist (@var{X}, @var{Distance}, @var{DistParameter}) ## ## Return the distance between any two rows in @var{X}. ## ## @code{@var{D} = pdist (@var{X}} calculates the euclidean distance between ## pairs of observations in @var{X}. @var{X} must be an @math{MxP} numeric ## matrix representing @math{M} points in @math{P}-dimensional space. This ## function computes the pairwise distances returned in @var{D} as an ## @math{Mx(M-1)/P} row vector. Use @code{@var{Z} = squareform (@var{D})} to ## convert the row vector @var{D} into a an @math{MxM} symmetric matrix @var{Z}, ## where @qcode{@var{Z}(i,j)} corresponds to the pairwise distance between ## points @qcode{i} and @qcode{j}. ## ## @code{@var{D} = pdist (@var{X}, @var{Y}, @var{Distance})} returns the ## distance between pairs of observations in @var{X} using the metric specified ## by @var{Distance}, which can be any of the following options. ## ## @multitable @columnfractions 0.23 0.02 0.65 ## @item @qcode{"euclidean"} @tab @tab Euclidean distance. ## @item @qcode{"squaredeuclidean"} @tab @tab Squared Euclidean distance. ## @item @qcode{"seuclidean"} @tab @tab standardized Euclidean distance. Each ## coordinate difference between the rows in @var{X} and the query matrix ## @var{Y} is scaled by dividing by the corresponding element of the standard ## deviation computed from @var{X}. A different scaling vector can be specified ## with the subsequent @var{DistParameter} input argument. ## @item @qcode{"mahalanobis"} @tab @tab Mahalanobis distance, computed using a ## positive definite covariance matrix. A different covariance matrix can be ## specified with the subsequent @var{DistParameter} input argument. ## @item @qcode{"cityblock"} @tab @tab City block distance. ## @item @qcode{"minkowski"} @tab @tab Minkowski distance. The default exponent ## is 2. A different exponent can be specified with the subsequent ## @var{DistParameter} input argument. ## @item @qcode{"chebychev"} @tab @tab Chebychev distance (maximum coordinate ## difference). ## @item @qcode{"cosine"} @tab @tab One minus the cosine of the included angle ## between points (treated as vectors). ## @item @qcode{"correlation"} @tab @tab One minus the sample linear correlation ## between observations (treated as sequences of values). ## @item @qcode{"hamming"} @tab @tab Hamming distance, which is the percentage ## of coordinates that differ. ## @item @qcode{"jaccard"} @tab @tab One minus the Jaccard coefficient, which is ## the percentage of nonzero coordinates that differ. ## @item @qcode{"spearman"} @tab @tab One minus the sample Spearman's rank ## correlation between observations (treated as sequences of values). ## @item @var{@@distfun} @tab @tab Custom distance function handle. A distance ## function of the form @code{function @var{D2} = distfun (@var{XI}, @var{YI})}, ## where @var{XI} is a @math{1xP} vector containing a single observation in ## @math{P}-dimensional space, @var{YI} is an @math{NxP} matrix containing an ## arbitrary number of observations in the same @math{P}-dimensional space, and ## @var{D2} is an @math{NxP} vector of distances, where @qcode{(@var{D2}k)} is ## the distance between observations @var{XI} and @qcode{(@var{YI}k,:)}. ## @end multitable ## ## @code{@var{D} = pdist (@var{X}, @var{Y}, @var{Distance}, @var{DistParameter})} ## returns the distance using the metric specified by @var{Distance} and ## @var{DistParameter}. The latter one can only be specified when the selected ## @var{Distance} is @qcode{"seuclidean"}, @qcode{"minkowski"}, and ## @qcode{"mahalanobis"}. ## ## @seealso{pdist2, squareform, linkage} ## @end deftypefn function D = pdist (X, varargin) ## Check input data if (nargin < 1) error ("pdist: too few input arguments."); endif if (! isnumeric (X) || isempty (X)) error ("pdist: X must be a nonempty numeric matrix."); endif if (ndims (X) != 2) error ("pdist: X must be a two-dimensional matrix."); endif if (rows (X) < 2) D = cast (zeros (1, 0), class (X)); return; endif ## Add default values Distance = "euclidean"; # Distance metric DistParameter = []; # Distance parameter ## Parse additional Distance metric and Distance parameter (if available) DMs = {"euclidean", "squaredeuclidean", "seuclidean", ... "mahalanobis", "cityblock", "minkowski", "chebychev", ... "cosine", "correlation", "hamming", "jaccard", "spearman"}; if (numel (varargin) > 0) if (any (strcmpi (DMs, varargin{1}))) Distance = tolower (varargin{1}); elseif (is_function_handle (varargin{1})) Distance = varargin{1}; else error ("pdist: invalid value for Distance input argument."); endif endif if (numel (varargin) > 1) if (isnumeric (varargin{2})) DistParameter = varargin{2}; else error ("pdist: invalid value for DistParameter input argument."); endif endif ## Calculate selected distance order = nchoosek (1:rows (X) ,2); ix = order (:,1); iy = order (:,2); ## Handle build-in distance metric if (ischar (Distance)) switch (Distance) case "euclidean" D = sqrt (sum ((X(ix(:),:) - X(iy(:),:)) .^ 2, 2)); case "squaredeuclidean" D = sum ((X(ix(:),:) - X(iy(:),:)) .^ 2, 2); case "seuclidean" if (isempty (DistParameter)) DistParameter = std (X, [], 1); else if (numel (DistParameter) != columns (X)) error (strcat (["pdist2: DistParameter for standardized"], ... [" euclidean must be a vector of equal length"], ... [" to the number of columns in X."])); endif if (any (DistParameter < 0)) error (strcat (["pdist2: DistParameter for standardized"], ... [" euclidean must be a nonnegative vector."])); endif endif DistParameter(DistParameter == 0) = 1; # fix constant variable D = sqrt (sum (((X(ix(:),:) - X(iy(:),:)) ./ DistParameter) .^ 2, 2)); case "mahalanobis" if (isempty (DistParameter)) DistParameter = cov (X(! any (isnan (X), 2),:)); else if (columns (DistParameter) != columns (X)) error (strcat (["pdist2: DistParameter for mahalanobis"], ... [" distance must be a covariance matrix with"], ... [" the same number of columns as X."])); endif [~, p] = chol (DistParameter); if (p != 0) error (strcat (["pdist2: covariance matrix for mahalanobis"], ... [" distance must be symmetric and positive"], ... [" definite."])); endif endif dxx = X(ix(:),:) - X(iy(:),:); ## Catch warning if matrix is close to singular or badly scaled. [DP_inv, rc] = inv (DistParameter); if (rc < eps) msg = sprintf (strcat (["pdist: matrix is close to"], ... [" singular or badly scaled.\n RCOND = "], ... [" %e. Results may be inaccurate."]), rc); warning (msg); endif D = sqrt (sum ((dxx * DP_inv) .* dxx, 2)); case "cityblock" D = sum (abs (X(ix(:),:) - X(iy(:),:)), 2); case "minkowski" if (isempty (DistParameter)) DistParameter = 2; else if (! (isnumeric (DistParameter) && isscalar (DistParameter) && DistParameter > 0)) error (strcat (["pdist2: DistParameter for minkowski distance"],... [" must be a positive scalar."])); endif endif D = sum (abs (X(ix(:),:) - X(iy(:),:)) .^ DistParameter, 2) .^ ... (1 / DistParameter); case "chebychev" D = max (abs (X(ix(:),:) - X(iy(:),:)), [], 2); case "cosine" sx = sum (X .^ 2, 2) .^ (-1 / 2); D = 1 - sum (X(ix(:),:) .* X(iy(:),:), 2) .* sx(ix(:)) .* sx(iy(:)); case "correlation" mX = mean (X(ix(:),:), 2); mY = mean (X(iy(:),:), 2); xy = sum ((X(ix(:),:) - mX) .* (X(iy(:),:) - mY), 2); xx = sqrt (sum ((X(ix(:),:) - mX) .* (X(ix(:),:) - mX), 2)); yy = sqrt (sum ((X(iy(:),:) - mY) .* (X(iy(:),:) - mY), 2)); D = 1 - (xy ./ (xx .* yy)); case "hamming" D = mean (abs (X(ix(:),:) != X(iy(:),:)), 2); case "jaccard" xx0 = (X(ix(:),:) != 0 | X(iy(:),:) != 0); D = sum ((X(ix(:),:) != X(iy(:),:)) & xx0, 2) ./ sum (xx0, 2); case "spearman" for i = 1:size (X, 1) rX(i,:) = tiedrank (X(i,:)); endfor rM = (size (X, 2) + 1) / 2; xy = sum ((rX(ix(:),:) - rM) .* (rX(iy(:),:) - rM), 2); xx = sqrt (sum ((rX(ix(:),:) - rM) .* (rX(ix(:),:) - rM), 2)); yy = sqrt (sum ((rX(iy(:),:) - rM) .* (rX(iy(:),:) - rM), 2)); D = 1 - (xy ./ (xx .* yy)); endswitch ## Force output ot row vector D = D'; endif ## Handle a function handle if (is_function_handle (Distance)) ## Check the input output sizes of the user function D2 = []; try D2 = Distance (X(1,:), X([2:end],:)); catch ME error ("pdist: invalid function handle for distance metric."); end_try_catch Xrows = rows (X) - 1; if (! isequal (size (D2), [Xrows, 1])) error ("pdist: custom distance function produces wrong output size."); endif ## Evaluate user defined distance metric function D = zeros (1, numel (ix)); id_beg = 1; for r = 1:Xrows id_end = id_beg + size (X([r+1:end],:), 1) - 1; D(id_beg:id_end) = feval (Distance, X(r,:), X([r+1:end],:)); id_beg = id_end + 1; endfor endif endfunction ## Test output %!shared xy, t, eucl, x %! xy = [0 1; 0 2; 7 6; 5 6]; %! t = 1e-3; %! eucl = @(v,m) sqrt(sumsq(repmat(v,rows(m),1)-m,2)); %! x = [1 2 3; 4 5 6; 7 8 9; 3 2 1]; %!assert (pdist (xy), [1.000 8.602 7.071 8.062 6.403 2.000], t); %!assert (pdist (xy, eucl), [1.000 8.602 7.071 8.062 6.403 2.000], t); %!assert (pdist (xy, "euclidean"), [1.000 8.602 7.071 8.062 6.403 2.000], t); %!assert (pdist (xy, "seuclidean"), [0.380 2.735 2.363 2.486 2.070 0.561], t); %!assert (pdist (xy, "mahalanobis"), [1.384 1.967 2.446 2.384 1.535 2.045], t); %!assert (pdist (xy, "cityblock"), [1.000 12.00 10.00 11.00 9.000 2.000], t); %!assert (pdist (xy, "minkowski"), [1.000 8.602 7.071 8.062 6.403 2.000], t); %!assert (pdist (xy, "minkowski", 3), [1.000 7.763 6.299 7.410 5.738 2.000], t); %!assert (pdist (xy, "cosine"), [0.000 0.349 0.231 0.349 0.231 0.013], t); %!assert (pdist (xy, "correlation"), [0.000 2.000 0.000 2.000 0.000 2.000], t); %!assert (pdist (xy, "spearman"), [0.000 2.000 0.000 2.000 0.000 2.000], t); %!assert (pdist (xy, "hamming"), [0.500 1.000 1.000 1.000 1.000 0.500], t); %!assert (pdist (xy, "jaccard"), [1.000 1.000 1.000 1.000 1.000 0.500], t); %!assert (pdist (xy, "chebychev"), [1.000 7.000 5.000 7.000 5.000 2.000], t); %!assert (pdist (x), [5.1962, 10.3923, 2.8284, 5.1962, 5.9161, 10.7703], 1e-4); %!assert (pdist (x, "euclidean"), ... %! [5.1962, 10.3923, 2.8284, 5.1962, 5.9161, 10.7703], 1e-4); %!assert (pdist (x, eucl), ... %! [5.1962, 10.3923, 2.8284, 5.1962, 5.9161, 10.7703], 1e-4); %!assert (pdist (x, "squaredeuclidean"), [27, 108, 8, 27, 35, 116]); %!assert (pdist (x, "seuclidean"), ... %! [1.8071, 3.6142, 0.9831, 1.8071, 1.8143, 3.4854], 1e-4); %!warning ... %! pdist (x, "mahalanobis"); %!assert (pdist (x, "cityblock"), [9, 18, 4, 9, 9, 18]); %!assert (pdist (x, "minkowski"), ... %! [5.1962, 10.3923, 2.8284, 5.1962, 5.9161, 10.7703], 1e-4); %!assert (pdist (x, "minkowski", 3), ... %! [4.3267, 8.6535, 2.5198, 4.3267, 5.3485, 9.2521], 1e-4); %!assert (pdist (x, "cosine"), ... %! [0.0254, 0.0406, 0.2857, 0.0018, 0.1472, 0.1173], 1e-4); %!assert (pdist (x, "correlation"), [0, 0, 2, 0, 2, 2], 1e-14); %!assert (pdist (x, "spearman"), [0, 0, 2, 0, 2, 2], 1e-14); %!assert (pdist (x, "hamming"), [1, 1, 2/3, 1, 1, 1]); %!assert (pdist (x, "jaccard"), [1, 1, 2/3, 1, 1, 1]); %!assert (pdist (x, "chebychev"), [3, 6, 2, 3, 5, 8]); statistics-release-1.7.3/inst/pdist2.m000066400000000000000000000450731475240274700177170ustar00rootroot00000000000000## Copyright (C) 2014-2019 Piotr Dollar ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{D} =} pdist2 (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{D} =} pdist2 (@var{X}, @var{Y}, @var{Distance}) ## @deftypefnx {statistics} {@var{D} =} pdist2 (@var{X}, @var{Y}, @var{Distance}, @var{DistParameter}) ## @deftypefnx {statistics} {@var{D} =} pdist2 (@dots{}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{D}, @var{I}] =} pdist2 (@dots{}, @var{Name}, @var{Value}) ## ## Compute pairwise distance between two sets of vectors. ## ## @code{@var{D} = pdist2 (@var{X}, @var{Y})} calculates the euclidean distance ## between each pair of observations in @var{X} and @var{Y}. Let @var{X} be an ## @math{MxP} matrix representing @math{M} points in @math{P}-dimensional space ## and @var{Y} be an @math{NxP} matrix representing another set of points in the ## same space. This function computes the @math{MxN} distance matrix @var{D}, ## where @qcode{@var{D}(i,j)} is the distance between @qcode{@var{X}(i,:)} and ## @qcode{@var{Y}(j,:)}. ## ## @code{@var{D} = pdist2 (@var{X}, @var{Y}, @var{Distance})} returns the ## distance between each pair of observations in @var{X} and @var{Y} using the ## metric specified by @var{Distance}, which can be any of the following options. ## ## @multitable @columnfractions 0.23 0.02 0.65 ## @item @qcode{"euclidean"} @tab @tab Euclidean distance. ## @item @qcode{"squaredeuclidean"} @tab @tab Squared Euclidean distance. ## @item @qcode{"seuclidean"} @tab @tab standardized Euclidean distance. Each ## coordinate difference between the rows in @var{X} and the query matrix ## @var{Y} is scaled by dividing by the corresponding element of the standard ## deviation computed from @var{X}. A different scaling vector can be specified ## with the subsequent @var{DistParameter} input argument. ## @item @qcode{"mahalanobis"} @tab @tab Mahalanobis distance, computed using a ## positive definite covariance matrix. A different covariance matrix can be ## specified with the subsequent @var{DistParameter} input argument. ## @item @qcode{"cityblock"} @tab @tab City block distance. ## @item @qcode{"minkowski"} @tab @tab Minkowski distance. The default exponent ## is 2. A different exponent can be specified with the subsequent ## @var{DistParameter} input argument. ## @item @qcode{"chebychev"} @tab @tab Chebychev distance (maximum coordinate ## difference). ## @item @qcode{"cosine"} @tab @tab One minus the cosine of the included angle ## between points (treated as vectors). ## @item @qcode{"correlation"} @tab @tab One minus the sample linear correlation ## between observations (treated as sequences of values). ## @item @qcode{"hamming"} @tab @tab Hamming distance, which is the percentage ## of coordinates that differ. ## @item @qcode{"jaccard"} @tab @tab One minus the Jaccard coefficient, which is ## the percentage of nonzero coordinates that differ. ## @item @qcode{"spearman"} @tab @tab One minus the sample Spearman's rank ## correlation between observations (treated as sequences of values). ## @item @var{@@distfun} @tab @tab Custom distance function handle. A distance ## function of the form @code{function @var{D2} = distfun (@var{XI}, @var{YI})}, ## where @var{XI} is a @math{1xP} vector containing a single observation in ## @math{P}-dimensional space, @var{YI} is an @math{NxP} matrix containing an ## arbitrary number of observations in the same @math{P}-dimensional space, and ## @var{D2} is an @math{NxP} vector of distances, where @qcode{(@var{D2}k)} is ## the distance between observations @var{XI} and @qcode{(@var{YI}k,:)}. ## @end multitable ## ## @code{@var{D} = pdist2 (@var{X}, @var{Y}, @var{Distance}, @var{DistParameter})} ## returns the distance using the metric specified by @var{Distance} and ## @var{DistParameter}. The latter one can only be specified when the selected ## @var{Distance} is @qcode{"seuclidean"}, @qcode{"minkowski"}, and ## @qcode{"mahalanobis"}. ## ## @code{@var{D} = pdist2 (@dots{}, @var{Name}, @var{Value})} for any previous ## arguments, modifies the computation using @var{Name}-@var{Value} parameters. ## @itemize ## @item ## @code{@var{D} = pdist2 (@var{X}, @var{Y}, @var{Distance}, @qcode{"Smallest"}, ## @var{K})} computes the distance using the metric specified by ## @var{Distance} and returns the @var{K} smallest pairwise distances to ## observations in @var{X} for each observation in @var{Y} in ascending order. ## @item ## @code{@var{D} = pdist2 (@var{X}, @var{Y}, @var{Distance}, @var{DistParameter}, ## @qcode{"Largest"}, @var{K})} computes the distance using the metric specified ## by @var{Distance} and @var{DistParameter} and returns the @var{K} largest ## pairwise distances in descending order. ## @end itemize ## ## @code{[@var{D}, @var{I}] = pdist2 (@dots{}, @var{Name}, @var{Value})} also ## returns the matrix @var{I}, which contains the indices of the observations in ## @var{X} corresponding to the distances in @var{D}. You must specify either ## @qcode{"Smallest"} or @qcode{"Largest"} as an optional @var{Name}-@var{Value} ## pair pair argument to compute the second output argument. ## ## @seealso{pdist, knnsearch, rangesearch} ## @end deftypefn function [D, I] = pdist2 (X, Y, varargin) ## Check input data if (nargin < 2) error ("pdist2: too few input arguments."); endif if (size (X, 2) != size (Y, 2)) error ("pdist2: X and Y must have equal number of columns."); endif if (ndims (X) != 2 || ndims (Y) != 2) error ("pdist2: X and Y must be 2 dimensional matrices."); endif ## Add default values Distance = "euclidean"; # Distance metric DistParameter = []; # Distance parameter SortOrder = []; # Flag for sorting distances to find ## Parse additional Distance metric and Distance parameter (if available) DMs = {"euclidean", "squaredeuclidean", "seuclidean", ... "mahalanobis", "cityblock", "minkowski", "chebychev", ... "cosine", "correlation", "hamming", "jaccard", "spearman"}; if (numel (varargin) > 0) if (any (strcmpi (DMs, varargin{1}))) Distance = tolower (varargin{1}); varargin(1) = []; if (numel (varargin) > 0) if (isnumeric (varargin{1})) DistParameter = varargin{1}; varargin(1) = []; endif endif elseif (is_function_handle (varargin{1})) Distance = varargin{1}; varargin(1) = []; if (numel (varargin) > 0) if (isnumeric (varargin{1})) DistParameter = varargin{1}; varargin(1) = []; endif endif endif endif ## Parse additional parameters in Name/Value pairs parcount = 0; while (numel (varargin) > 0) if (numel (varargin) < 2) error ("pdist2: missing value in optional name/value paired arguments."); endif switch (tolower (varargin{1})) case "smallest" SortOrder = "ascend"; K = varargin{2}; parcount += 1; case "largest" SortOrder = "descend"; K = varargin{2}; parcount += 1; otherwise error ("pdist2: invalid NAME in optional pairs of arguments."); endswitch varargin(1:2) = []; endwhile ## Check additional arguments if (parcount > 1) error ("pdist2: you can only use either Smallest or Largest."); endif if (isempty (SortOrder) && nargout > 1) error (strcat (["pdist2: Smallest or Largest must be specified"], ... [" to compute second output."])); endif ## Calculate selected distance [ix, iy] = meshgrid (1:size (X, 1), 1:size (Y, 1)); ## Handle build-in distance metric if (ischar (Distance)) switch (Distance) case "euclidean" D = sqrt (sum ((X(ix(:),:) - Y(iy(:),:)) .^ 2, 2)); case "squaredeuclidean" D = sum ((X(ix(:),:) - Y(iy(:),:)) .^ 2, 2); case "seuclidean" if (isempty (DistParameter)) DistParameter = std (X, [], 1); else if (numel (DistParameter) != columns (X)) error (strcat (["pdist2: DistParameter for standardized"], ... [" euclidean must be a vector of equal length"], ... [" to the number of columns in X."])); endif if (any (DistParameter < 0)) error (strcat (["pdist2: DistParameter for standardized"], ... [" euclidean must be a nonnegative vector."])); endif endif DistParameter(DistParameter == 0) = 1; # fix constant variable D = sqrt (sum (((X(ix(:),:) - Y(iy(:),:)) ./ DistParameter) .^ 2, 2)); case "mahalanobis" if (isempty (DistParameter)) DistParameter = cov (X(! any (isnan (X), 2),:)); else if (columns (DistParameter) != columns (X)) error (strcat (["pdist2: DistParameter for mahalanobis"], ... [" distance must be a covariance matrix with"], ... [" the same number of columns as X."])); endif [~, p] = chol (DistParameter); if (p != 0) error (strcat (["pdist2: covariance matrix for mahalanobis"], ... [" distance must be symmetric and positive"], ... [" definite."])); endif endif ## Catch warning if matrix is close to singular or badly scaled. [DP_inv, rc] = inv (DistParameter); if (rc < eps) msg = sprintf (strcat (["pdist2: matrix is close to"], ... [" singular or badly scaled.\n RCOND = "], ... [" %e. Results may be inaccurate."]), rc); warning (msg); endif dxy = X(ix(:),:) - Y(iy(:),:); D = sqrt (sum ((dxy * DP_inv) .* dxy, 2)); case "cityblock" D = sum (abs (X(ix(:),:) - Y(iy(:),:)), 2); case "minkowski" if (isempty (DistParameter)) DistParameter = 2; else if (! (isnumeric (DistParameter) && isscalar (DistParameter) && DistParameter > 0)) error (strcat (["pdist2: DistParameter for minkowski distance"],... [" must be a positive scalar."])); endif endif D = sum (abs (X(ix(:),:) - Y(iy(:),:)) .^ DistParameter, 2) .^ ... (1 / DistParameter); case "chebychev" D = max (abs (X(ix(:),:) - Y(iy(:),:)), [], 2); case "cosine" sx = sum (X .^ 2, 2) .^ (-1 / 2); sy = sum (Y .^ 2, 2) .^ (-1 / 2); D = 1 - sum (X(ix(:),:) .* Y(iy(:),:), 2) .* sx(ix(:)) .* sy(iy(:)); case "correlation" mX = mean (X(ix(:),:), 2); mY = mean (Y(iy(:),:), 2); xy = sum ((X(ix(:),:) - mX) .* (Y(iy(:),:) - mY), 2); xx = sqrt (sum ((X(ix(:),:) - mX) .* (X(ix(:),:) - mX), 2)); yy = sqrt (sum ((Y(iy(:),:) - mY) .* (Y(iy(:),:) - mY), 2)); D = 1 - (xy ./ (xx .* yy)); case "hamming" D = mean (abs (X(ix(:),:) != Y(iy(:),:)), 2); case "jaccard" xy0 = (X(ix(:),:) != 0 | Y(iy(:),:) != 0); D = sum ((X(ix(:),:) != Y(iy(:),:)) & xy0, 2) ./ sum (xy0, 2); case "spearman" for i = 1:size (X, 1) rX(i,:) = tiedrank (X(i,:)); endfor for i = 1:size (Y, 1) rY(i,:) = tiedrank (Y(i,:)); endfor rM = (size (X, 2) + 1) / 2; xy = sum ((rX(ix(:),:) - rM) .* (rY(iy(:),:) - rM), 2); xx = sqrt (sum ((rX(ix(:),:) - rM) .* (rX(ix(:),:) - rM), 2)); yy = sqrt (sum ((rY(iy(:),:) - rM) .* (rY(iy(:),:) - rM), 2)); D = 1 - (xy ./ (xx .* yy)); endswitch endif ## Handle a function handle if (is_function_handle (Distance)) ## Check the input output sizes of the user function D2 = []; try D2 = Distance (X(1,:), Y); catch ME error ("pdist2: invalid function handle for distance metric."); end_try_catch Yrows = rows (Y); if (! isequal (size (D2), [Yrows, 1])) error ("pdist2: custom distance function produces wrong output size."); endif ## Evaluate user defined distance metric function Yrows = rows (Y); D = zeros (numel (ix), 1); id_beg = 1; for r = 1:rows (X) id_end = id_beg + Yrows - 1; D(id_beg:id_end) = feval (Distance, X(r,:), Y); id_beg = id_end + 1; endfor endif ## From vector to matrix D = reshape (D, size (Y, 1), size (X, 1))'; if (nargout > 1) [D, I] = sort (D', 2, SortOrder); K = min (size (D, 2), K); # fix max K to avoid out of bound error D = D(:,1:K)'; I = I(:,1:K)'; endif endfunction ## Test output %!shared x, y, xx %! x = [1, 1, 1; 2, 2, 2; 3, 3, 3]; %! y = [0, 0, 0; 1, 2, 3; 0, 2, 4; 4, 7, 1]; %! xx = [1 2 3; 4 5 6; 7 8 9; 3 2 1]; %!test %! d = sqrt([3, 5, 11, 45; 12, 2, 8, 30; 27, 5, 11, 21]); %! assert (pdist2 (x, y), d); %!test %! d = [5.1962, 2.2361, 3.3166, 6.7082; ... %! 3.4641, 2.2361, 3.3166, 5.4772]; %! i = [3, 1, 1, 1; 2, 3, 3, 2]; %! [D, I] = pdist2 (x, y, "euclidean", "largest", 2); %! assert ({D, I}, {d, i}, 1e-4); %!test %! d = [1.7321, 1.4142, 2.8284, 4.5826; ... %! 3.4641, 2.2361, 3.3166, 5.4772]; %! i = [1, 2, 2, 3;2, 1, 1, 2]; %! [D, I] = pdist2 (x, y, "euclidean", "smallest", 2); %! assert ({D, I}, {d, i}, 1e-4); %!test %! yy = [1 2 3;5 6 7;9 5 1]; %! d = [0, 6.1644, 5.3852; 1.4142, 6.9282, 8.7750; ... %! 3.7417, 7.0711, 9.9499; 6.1644, 10.4881, 10.3441]; %! i = [2, 4, 4; 3, 2, 2; 1, 3, 3; 4, 1, 1]; %! [D, I] = pdist2 (y, yy, "euclidean", "smallest", 4); %! assert ({D, I}, {d, i}, 1e-4); %!test %! yy = [1 2 3;5 6 7;9 5 1]; %! d = [0, 38, 29; 2, 48, 77; 14, 50, 99; 38, 110, 107]; %! i = [2, 4, 4; 3, 2, 2; 1, 3, 3; 4, 1, 1]; %! [D, I] = pdist2 (y, yy, "squaredeuclidean", "smallest", 4); %! assert ({D, I}, {d, i}, 1e-4); %!test %! yy = [1 2 3;5 6 7;9 5 1]; %! d = [0, 3.3256, 2.7249; 0.7610, 3.3453, 4.4799; ... %! 1.8514, 3.3869, 5.0703; 2.5525, 5.0709, 5.1297]; %! i = [2, 2, 4; 3, 4, 2; 1, 3, 1; 4, 1, 3]; %! [D, I] = pdist2 (y, yy, "seuclidean", "smallest", 4); %! assert ({D, I}, {d, i}, 1e-4); %!test %! d = [2.1213, 4.2426, 6.3640; 1.2247, 2.4495, 4.4159; ... %! 3.2404, 4.8990, 6.8191; 2.7386, 4.2426, 6.1237]; %! assert (pdist2 (y, x, "mahalanobis"), d, 1e-4); %!test %! xx = [1, 3, 4; 3, 5, 4; 8, 7, 6]; %! d = [1.3053, 1.8257, 15.0499; 1.3053, 3.3665, 16.5680]; %! i = [2, 2, 2; 3, 4, 4]; %! [D, I] = pdist2 (y, xx, "mahalanobis", "smallest", 2); %! assert ({D, I}, {d, i}, 1e-4); %!test %! d = [2.5240, 4.1633, 17.3638; 2.0905, 3.9158, 17.0147]; %! i = [1, 1, 3; 4, 3, 1]; %! [D, I] = pdist2 (y, xx, "mahalanobis", "largest", 2); %! assert ({D, I}, {d, i}, 1e-4); %!test %! d = [3, 3, 5, 9; 6, 2, 4, 8; 9, 3, 5, 7]; %! assert (pdist2 (x, y, "cityblock"), d); %!test %! d = [1, 2, 3, 6; 2, 1, 2, 5; 3, 2, 3, 4]; %! assert (pdist2 (x, y, "chebychev"), d); %!test %! d = repmat ([NaN, 0.0742, 0.2254, 0.1472], [3, 1]); %! assert (pdist2 (x, y, "cosine"), d, 1e-4); %!test %! yy = [1 2 3;5 6 7;9 5 1]; %! d = [0, 0, 0.5; 0, 0, 2; 1.5, 1.5, 2; NaN, NaN, NaN]; %! i = [2, 2, 4; 3, 3, 2; 4, 4, 3; 1, 1, 1]; %! [D, I] = pdist2 (y, yy, "correlation", "smallest", 4); %! assert ({D, I}, {d, i}, eps); %! [D, I] = pdist2 (y, yy, "spearman", "smallest", 4); %! assert ({D, I}, {d, i}, eps); %!test %! d = [1, 2/3, 1, 1; 1, 2/3, 1, 1; 1, 2/3, 2/3, 2/3]; %! i = [1, 1, 1, 2; 2, 2, 3, 3; 3, 3, 2, 1]; %! [D, I] = pdist2 (x, y, "hamming", "largest", 4); %! assert ({D, I}, {d, i}, eps); %! [D, I] = pdist2 (x, y, "jaccard", "largest", 4); %! assert ({D, I}, {d, i}, eps); %!test %! xx = [1, 2, 3, 4; 2, 3, 4, 5; 3, 4, 5, 6]; %! yy = [1, 2, 2, 3; 2, 3, 3, 4]; %! [D, I] = pdist2 (x, y, "euclidean", "Smallest", 4); %! eucldist = @(v,m) sqrt(sumsq(repmat(v,rows(m),1)-m,2)); %! [d, i] = pdist2 (x, y, eucldist, "Smallest", 4); %! assert ({D, I}, {d, i}); %!warning ... %! pdist2 (xx, xx, "mahalanobis"); ## Test input validation %!error pdist2 (1) %!error ... %! pdist2 (ones (4, 5), ones (4)) %!error ... %! pdist2 (ones (4, 2, 3), ones (3, 2)) %!error ... %! pdist2 (ones (3), ones (3), "euclidean", "Largest") %!error ... %! pdist2 (ones (3), ones (3), "minkowski", 3, "Largest") %!error ... %! pdist2 (ones (3), ones (3), "minkowski", 3, "large", 4) %!error ... %! pdist2 (ones (3), ones (3), "minkowski", 3, "Largest", 4, "smallest", 5) %!error ... %! [d, i] = pdist2(ones (3), ones (3), "minkowski", 3) %!error ... %! pdist2 (ones (3), ones (3), "seuclidean", 3) %!error ... %! pdist2 (ones (3), ones (3), "seuclidean", [1, -1, 3]) %!error ... %! pdist2 (ones (3), eye (3), "mahalanobis", eye(2)) %!error ... %! pdist2 (ones (3), eye (3), "mahalanobis", ones(3)) %!error ... %! pdist2 (ones (3), eye (3), "minkowski", 0) %!error ... %! pdist2 (ones (3), eye (3), "minkowski", -5) %!error ... %! pdist2 (ones (3), eye (3), "minkowski", [1, 2]) %!error ... %! pdist2 (ones (3), ones (3), @(v,m) sqrt(repmat(v,rows(m),1)-m,2)) %!error ... %! pdist2 (ones (3), ones (3), @(v,m) sqrt(sum(sumsq(repmat(v,rows(m),1)-m,2)))) statistics-release-1.7.3/inst/plsregress.m000066400000000000000000000474371475240274700207110ustar00rootroot00000000000000## Copyright (C) 2012-2019 Fernando Damian Nieuwveldt ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 3 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{xload}, @var{yload}] =} plsregress (@var{X}, @var{Y}) ## @deftypefnx {statistics} {[@var{xload}, @var{yload}] =} plsregress (@var{X}, @var{Y}, @var{NCOMP}) ## @deftypefnx {statistics} {[@var{xload}, @var{yload}, @var{xscore}, @var{yscore}, @var{coef}, @var{pctVar}, @var{mse}, @var{stats}] =} plsregress (@var{X}, @var{Y}, @var{NCOMP}) ## @deftypefnx {statistics} {[@var{xload}, @var{yload}, @var{xscore}, @var{yscore}, @var{coef}, @var{pctVar}, @var{mse}, @var{stats}] =} plsregress (@dots{}, @var{Name}, @var{Value}) ## ## Calculate partial least squares regression using SIMPLS algorithm. ## ## @code{plsregress} uses the SIMPLS algorithm, and first centers @var{X} and ## @var{Y} by subtracting off column means to get centered variables. However, ## it does not rescale the columns. To perform partial least squares regression ## with standardized variables, use @code{zscore} to normalize @var{X} and ## @var{Y}. ## ## @code{[@var{xload}, @var{yload}] = plsregress (@var{X}, @var{Y})} computes a ## partial least squares regression of @var{Y} on @var{X}, using @var{NCOMP} ## PLS components, which by default are calculated as ## @qcode{min (size (@var{X}, 1) - 1, size(@var{X}, 2))}, and returns the ## the predictor and response loadings in @var{xload} and @var{yload}, ## respectively. ## @itemize ## @item @var{X} is an @math{NxP} matrix of predictor variables, with rows ## corresponding to observations, and columns corresponding to variables. ## @item @var{Y} is an @math{NxM} response matrix. ## @item @var{xload} is a @math{PxNCOMP} matrix of predictor loadings, where ## each row of @var{xload} contains coefficients that define a linear ## combination of PLS components that approximate the original predictor ## variables. ## @item @var{yload} is an @math{MxNCOMP} matrix of response loadings, where ## each row of @var{yload} contains coefficients that define a linear ## combination of PLS components that approximate the original response ## variables. ## @end itemize ## ## @code{[@var{xload}, @var{yload}] = plsregress (@var{X}, @var{Y}, ## @var{NCOMP})} defines the desired number of PLS components to use in the ## regression. @var{NCOMP}, a scalar positive integer, must not exceed the ## default calculated value. ## ## @code{[@var{xload}, @var{yload}, @var{xscore}, @var{yscore}, @var{coef}, ## @var{pctVar}, @var{mse}, @var{stats}] = plsregress (@var{X}, @var{Y}, ## @var{NCOMP})} also returns the following arguments: ## @itemize ## @item @var{xscore} is an @math{NxNCOMP} orthonormal matrix with the predictor ## scores, i.e., the PLS components that are linear combinations of the ## variables in @var{X}, with rows corresponding to observations and columns ## corresponding to components. ## @item @var{yscore} is an @math{NxNCOMP} orthonormal matrix with the response ## scores, i.e., the linear combinations of the responses with which the PLS ## components @var{xscore} have maximum covariance, with rows corresponding to ## observations and columns corresponding to components. ## @item @var{coef} is a @math{(P+1)xM} matrix with the PLS regression ## coefficients, containing the intercepts in the first row. ## @item @var{pctVar} is a @math{2xNCOMP} matrix containing the percentage of ## the variance explained by the model with the first row containing the ## percentage of exlpained varianced in @var{X} by each PLS component and the ## second row containing the percentage of explained variance in @var{Y}. ## @item @var{mse} is a @math{2x(NCOMP+1)} matrix containing the estimated mean ## squared errors for PLS models with @qcode{0:@var{NCOMP}} components with the ## first row containing the squared errors for the predictor variables in ## @var{X} and the second row containing the mean squared errors for the ## response variable(s) in @var{Y}. ## @item @var{stats} is a structure with the following fields: ## @itemize ## @item @var{stats}@qcode{.W} is a @math{PxNCOMP} matrix of PLS weights. ## @item @var{stats}@qcode{.T2} is the @math{T^2} statistics for each point in ## @var{xscore}. ## @item @var{stats}@qcode{.Xresiduals} is an @math{NxP} matrix with the ## predictor residuals. ## @item @var{stats}@qcode{.Yresiduals} is an @math{NxM} matrix with the ## response residuals. ## @end itemize ## @end itemize ## ## @code{[@dots{}] = plsregress (@dots{}, @var{Name}, @var{Value}, @dots{})} ## specifies one or more of the following @var{Name}/@var{Value} pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Name} @tab @var{Value} ## @item @tab @qcode{"CV"} @tab The method used to compute @var{mse}. When ## @var{Value} is a positive integer @math{K}, @code{plsregress} uses ## @math{K}-fold cross-validation. Set @var{Value} to a cross-validation ## partition, created using @code{cvpartition}, to use other forms of ## cross-validation. Set @var{Value} to @qcode{"resubstitution"} to use both ## @var{X} and @var{Y} to fit the model and to estimate the mean squared errors, ## without cross-validation. By default, @qcode{@var{Value} = "resubstitution"}. ## @item @tab @qcode{"MCReps"} @tab A positive integer indicating the number of ## Monte-Carlo repetitions for cross-validation. By default, ## @qcode{@var{Value} = 1}. A different @qcode{"MCReps"} value is only ## meaningful when using the @qcode{"HoldOut"} method for cross-validation, ## previously set by a @code{cvpartition} object. If no cross-validation method ## is used, then @qcode{"MCReps"} must be @qcode{1}. ## @end multitable ## ## Further information about the PLS regression can be found at ## @url{https://en.wikipedia.org/wiki/Partial_least_squares_regression} ## ## @subheading References ## @enumerate ## @item ## SIMPLS: An alternative approach to partial least squares regression. ## Chemometrics and Intelligent Laboratory Systems (1993) ## ## @end enumerate ## @end deftypefn function [xload, yload, xscore, yscore, coef, pctVar, mse, stats] = ... plsregress (X, Y, NCOMP, varargin) ## Check input arguments and add defaults if (nargin < 2) error ("plsregress: function called with too few input arguments."); endif if (! isnumeric (X) || ! isnumeric (Y)) error ("plsregress: X and Y must be real matrices."); endif ## Get size of predictor and response inputs [nobs, npred] = size (X); [Yobs, nresp] = size (Y); if (nobs != Yobs) error ("plsregress: X and Y observations mismatch."); endif ## Calculate max number of components NCOMPmax = min (nobs - 1, npred); if (nargin < 3) NCOMP = NCOMPmax; elseif (! isnumeric (NCOMP) || ! isscalar (NCOMP) || NCOMP != fix (NCOMP) || NCOMP <= 0) error ("plsregress: invalid value for NCOMP."); elseif (NCOMP > NCOMPmax) error ("plsregress: NCOMP exceeds maximum components for X."); endif ## Add default optional arguments CV = false; mcreps = 1; ## Parse additional Name-Value pairs while (numel (varargin) > 0) if (strcmpi (varargin{1}, "cv")) cvarg = varargin{2}; if (isa (cvarg, "cvpartition")) CV = true; elseif (isscalar (cvarg) && cvarg == fix (cvarg) && cvarg > 0) CV = true; elseif (! strcmpi (cvarg, "resubstitution")) error ("plsregress: invalid VALUE for 'cv' optional argument."); endif elseif (strcmpi (varargin{1}, "mcreps")) mcreps = varargin{2}; if (! (isscalar (mcreps) && mcreps == fix (mcreps) && mcreps > 0)) error ("plsregress: invalid VALUE for 'mcreps' optional argument."); endif else error ("plsregress: invalid NAME argument."); endif varargin(1:2) = []; endwhile ## Check MCREPS = 1 when "resubstitution" is set for cross validation if (! CV && mcreps != 1) error (strcat (["plsregress: 'mcreps' must be 1 when 'resubstitution'"], ... [" is specified for cross validation."])); endif ## Check number of output arguments if (nargout < 2 || nargout > 8) print_usage(); endif ## Mean centering Data matrix Xmeans = mean (X); X0 = bsxfun (@minus, X, Xmeans); ## Mean centering responses Ymeans = mean (Y); Y0 = bsxfun (@minus, Y, Ymeans); [P, Q, T, U, W] = simpls (X0, Y0, NCOMP); ## Store output arguments xload = P; yload = Q; xscore = T; yscore = U; ## Compute regression coefficients if (nargout > 4) coef = W * Q'; coef = [Ymeans - Xmeans * coef; coef]; endif ## Compute the percent of variance explained for X and Y if (nargout > 5) XVar = sum (abs (xload) .^ 2, 1) ./ sum (sum (abs (X0) .^ 2, 1)); YVar = sum (abs (yload) .^ 2, 1) ./ sum (sum (abs (Y0) .^ 2, 1)); pctVar = [XVar; YVar]; endif ## Estimate the mean squared errors if (nargout > 6) ## Compute MSE by cross-validation if (CV) mse = NaN (2, NCOMP + 1); ## Check crossval method and recalculate max number of components if isa (cvarg, "cvpartition") type = "Partition"; NCOMPmax = min(min(cvarg.TrainSize)-1,npred); ts = sum (cvarg.TestSize); else type = "Kfold"; NCOMPmax = min (floor ((nobs * (cvarg - 1) / cvarg) -1), npred); ts = nobs; endif if (NCOMP > NCOMPmax) warning (strcat (["plsregress: NCOMP exceeds maximum components"], ... [" for cross validation."])); NCOMP = NCOMPmax; endif ## Create function handle with NCOMP extra argument F = @(xtr, ytr, xte, yte) sseCV (xtr, ytr, xte, yte, NCOMP); ## Apply cross validation sse = crossval (F, X, Y, type, cvarg, "mcreps", mcreps); ## Compute MSE from the SSEs collected from each cross validation set mse(:,1:NCOMP+1) = reshape (sum (sse, 1) / (ts * mcreps), [2, NCOMP+1]); ## Computed fitted if residuals are requested if (nargout > 7) xfitted = xscore * xload'; yfitted = xscore * yload'; endif ## Compute MSE by resubstitution else mse = zeros (2, NCOMP + 1); ## Model with 0 components mse(1,1) = sum (sum (abs (X0) .^ 2, 2)); mse(2,1) = sum (sum (abs (Y0) .^ 2, 2)); ## Models with 1:NCOMP components for i = 1:NCOMP xfitted = xscore(:,1:i) * xload(:,1:i)'; yfitted = xscore(:,1:i) * yload(:,1:i)'; mse(1,i+1) = sum (sum (abs (X0 - xfitted) .^ 2, 2)); mse(2,i+1) = sum (sum (abs (Y0 - yfitted) .^ 2, 2)); endfor ## Compute the mean of the sum of squares above mse = mse / nobs; endif endif ## Compute stats if (nargout > 7) ## Save weights stats.W = W; ## Compute T-squared stats.T2 = sum (bsxfun (@rdivide, abs (xscore) .^ 2, ... var (xscore, [], 1)) , 2); ## Compute residuals for X and Y stats.Xresiduals = X0 - xfitted; stats.Yresiduals = Y0 - yfitted; endif endfunction ## SIMPLS algorithm function [P, Q, T, U, W] = simpls (X0, Y0, NCOMP) ## Get size of predictor and response inputs [nobs, npred] = size (X0); [Yobs, nresp] = size (Y0); ## Compute covariance S = X0' * Y0; ## Preallocate matrices W = P = V = zeros (npred, NCOMP); T = U = zeros (nobs, NCOMP); Q = zeros (nresp, NCOMP); ## Models with 1:NCOMP components for a = 1:NCOMP [eigvec, eigval] = eig (S' * S); # Y factor weights ## Get max eigenvector domindex = find (diag (eigval) == max (diag (eigval))); q = eigvec(:,domindex); w = S * q; # X block factor weights t = X0 * w; # X block factor scores t = t - mean (t); nt = sqrt (t' * t); # compute norm t = t / nt; w = w / nt; # normalize p = X0' * t; # X block factor loadings q = Y0' * t; # Y block factor loadings u = Y0 * q; # Y block factor scores v = p; ## Ensure orthogonality if (a > 1) v = v - V * (V' * p); u = u - T * (T' * u); endif v = v / sqrt (v' * v); # normalize orthogonal loadings S = S - v * (v' * S); # deflate S wrt loadings V(:,a) = v; ## Store data P(:,a) = p; # Xloads Q(:,a) = q; # Yloads T(:,a) = t; # xscore U(:,a) = u; # Yscores W(:,a) = w; # Weights endfor endfunction ## Helper function for SSE cross-validation function sse = sseCV (XTR, YTR, XTE, YTE, NCOMP) ## Center train data XTRmeans = mean (XTR); YTRmeans = mean (YTR); X0TR = bsxfun (@minus, XTR, XTRmeans); Y0TR = bsxfun (@minus, YTR, YTRmeans); ## Center test data X0TE = bsxfun(@minus, XTE, XTRmeans); Y0TE = bsxfun(@minus, YTE, YTRmeans); ## Fit the full model [xload, yload, ~, ~, W] = simpls (X0TR, Y0TR, NCOMP); XTEscore = X0TE * W; ## Preallocate SSE matrix sse = zeros (2, NCOMP + 1); ## Model with 0 components sse(1,1) = sum (sum (abs (X0TE) .^ 2, 2)); sse(2,1) = sum (sum (abs (Y0TE) .^ 2, 2)); ## Models with 1:NCOMP components for i = 1:NCOMP X0fitted = XTEscore(:,1:i) * xload(:,1:i)'; sse(1,i+1) = sum (sum (abs (X0TE - X0fitted) .^ 2, 2)); Y0fitted = XTEscore(:,1:i) * yload(:,1:i)'; sse(2,i+1) = sum (sum (abs (Y0TE - Y0fitted) .^ 2, 2)); endfor endfunction %!demo %! ## Perform Partial Least-Squares Regression %! %! ## Load the spectra data set and use the near infrared (NIR) spectral %! ## intensities (NIR) as the predictor and the corresponding octave %! ## ratings (octave) as the response. %! load spectra %! %! ## Perform PLS regression with 10 components %! [xload, yload, xscore, yscore, coef, ptcVar] = plsregress (NIR, octane, 10); %! %! ## Plot the percentage of explained variance in the response variable %! ## (PCTVAR) as a function of the number of components. %! plot (1:10, cumsum (100 * ptcVar(2,:)), "-ro"); %! xlim ([1, 10]); %! xlabel ("Number of PLS components"); %! ylabel ("Percentage of Explained Variance in octane"); %! title ("Explained Variance per PLS components"); %! %! ## Compute the fitted response and display the residuals. %! octane_fitted = [ones(size(NIR,1),1), NIR] * coef; %! residuals = octane - octane_fitted; %! figure %! stem (residuals, "color", "r", "markersize", 4, "markeredgecolor", "r") %! xlabel ("Observations"); %! ylabel ("Residuals"); %! title ("Residuals in octane's fitted responce"); %!demo %! ## Calculate Variable Importance in Projection (VIP) for PLS Regression %! %! ## Load the spectra data set and use the near infrared (NIR) spectral %! ## intensities (NIR) as the predictor and the corresponding octave %! ## ratings (octave) as the response. Variables with a VIP score greater than %! ## 1 are considered important for the projection of the PLS regression model. %! load spectra %! %! ## Perform PLS regression with 10 components %! [xload, yload, xscore, yscore, coef, pctVar, mse, stats] = ... %! plsregress (NIR, octane, 10); %! %! ## Calculate the normalized PLS weights %! W0 = stats.W ./ sqrt(sum(stats.W.^2,1)); %! %! ## Calculate the VIP scores for 10 components %! nobs = size (xload, 1); %! SS = sum (xscore .^ 2, 1) .* sum (yload .^ 2, 1); %! VIPscore = sqrt (nobs * sum (SS .* (W0 .^ 2), 2) ./ sum (SS, 2)); %! %! ## Find variables with a VIP score greater than or equal to 1 %! VIPidx = find (VIPscore >= 1); %! %! ## Plot the VIP scores %! scatter (1:length (VIPscore), VIPscore, "xb"); %! hold on %! scatter (VIPidx, VIPscore (VIPidx), "xr"); %! plot ([1, length(VIPscore)], [1, 1], "--k"); %! hold off %! axis ("tight"); %! xlabel ("Predictor Variables"); %! ylabel ("VIP scores"); %! title ("VIP scores for each predictror variable with 10 components"); ## Test output %!test %! load spectra %! [xload, yload, xscore, yscore, coef, pctVar] = plsregress (NIR, octane, 10); %! xload1_out = [-0.0170, 0.0039, 0.0095, 0.0258, 0.0025, ... %! -0.0075, 0.0000, 0.0018, -0.0027, 0.0020]; %! yload_out = [6.6384, 9.3106, 2.0505, 0.6471, 0.9625, ... %! 0.5905, 0.4244, 0.2437, 0.3516, 0.2548]; %! xscore1_out = [-0.0401, -0.1764, -0.0340, 0.1669, 0.1041, ... %! -0.2067, 0.0457, 0.1565, 0.0706, -0.1471]; %! yscore1_out = [-12.4635, -15.0003, 0.0638, 0.0652, -0.0070, ... %! -0.0634, 0.0062, -0.0012, -0.0151, -0.0173]; %! assert (xload(1,:), xload1_out, 1e-4); %! assert (yload, yload_out, 1e-4); %! assert (xscore(1,:), xscore1_out, 1e-4); %! assert (yscore(1,:), yscore1_out, 1e-4); %!test %! load spectra %! [xload, yload, xscore, yscore, coef, pctVar] = plsregress (NIR, octane, 5); %! xload1_out = [-0.0170, 0.0039, 0.0095, 0.0258, 0.0025]; %! yload_out = [6.6384, 9.3106, 2.0505, 0.6471, 0.9625]; %! xscore1_out = [-0.0401, -0.1764, -0.0340, 0.1669, 0.1041]; %! yscore1_out = [-12.4635, -15.0003, 0.0638, 0.0652, -0.0070]; %! assert (xload(1,:), xload1_out, 1e-4); %! assert (yload, yload_out, 1e-4); %! assert (xscore(1,:), xscore1_out, 1e-4); %! assert (yscore(1,:), yscore1_out, 1e-4); ## Test input validation %!error %! plsregress (1) %!error plsregress (1, "asd") %!error plsregress (1, {1,2,3}) %!error plsregress ("asd", 1) %!error plsregress ({1,2,3}, 1) %!error ... %! plsregress (ones (20,3), ones (15,1)) %!error ... %! plsregress (ones (20,3), ones (20,1), 0) %!error ... %! plsregress (ones (20,3), ones (20,1), -5) %!error ... %! plsregress (ones (20,3), ones (20,1), 3.2) %!error ... %! plsregress (ones (20,3), ones (20,1), [2, 3]) %!error ... %! plsregress (ones (20,3), ones (20,1), 4) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", 4.5) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", -1) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", "somestring") %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", 3, "mcreps", 2.2) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", 3, "mcreps", -2) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", 3, "mcreps", [1, 2]) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "Name", 3, "mcreps", 1) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", 3, "Name", 1) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "mcreps", 2) %!error ... %! plsregress (ones (20,3), ones (20,1), 3, "cv", "resubstitution", "mcreps", 2) %!error plsregress (1, 2) statistics-release-1.7.3/inst/ppplot.m000066400000000000000000000062701475240274700200240ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {} ppplot (@var{x}, @var{dist}) ## @deftypefnx {statistics} {} ppplot (@var{x}, @var{dist}, @var{params}) ## @deftypefnx {statistics} {[@var{p}, @var{y}] =} ppplot (@var{x}, @var{dist}, @var{params}) ## ## Perform a PP-plot (probability plot). ## ## If F is the CDF of the distribution @var{dist} with parameters ## @var{params} and @var{x} a sample vector of length @var{n}, the PP-plot ## graphs ordinate @var{y}(@var{i}) = F (@var{i}-th largest element of ## @var{x}) versus abscissa @var{p}(@var{i}) = (@var{i} - 0.5)/@var{n}. If ## the sample comes from F, the pairs will approximately follow a straight ## line. ## ## The default for @var{dist} is the standard normal distribution. ## ## The optional argument @var{params} contains a list of parameters of ## @var{dist}. ## ## For example, for a probability plot of the uniform distribution on [2,4] ## and @var{x}, use ## ## @example ## ppplot (x, "unif", 2, 4) ## @end example ## ## @noindent ## @var{dist} can be any string for which a function @var{distcdf} that ## calculates the CDF of distribution @var{dist} exists. ## ## If no output is requested then the data are plotted immediately. ## @seealso{qqplot} ## @end deftypefn function [p, y] = ppplot (x, dist, varargin) if (nargin < 1) print_usage (); endif if (! isnumeric (x) || ! isreal (x) || ! isvector (x) || isscalar (x)) error ("ppplot: X must be a numeric vector of real numbers"); endif s = sort (x); n = length (x); p = ((1 : n)' - 0.5) / n; if (nargin == 1) F = @stdnormal_cdf; elseif (! ischar (dist)) error ("ppplot: DIST must be a string"); else F = str2func ([dist "cdf"]); endif if (nargin <= 2) y = feval (F, s); else y = feval (F, s, varargin{:}); endif if (nargout == 0) plot (p, y); axis ([0, 1, 0, 1]); endif endfunction function p = stdnormal_cdf (x) p = 0.5 * erfc (x ./ sqrt (2)); endfunction ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! ppplot ([2 3 3 4 4 5 6 5 6 7 8 9 8 7 8 9 0 8 7 6 5 4 6 13 8 15 9 9]); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error ppplot () %!error ppplot (ones (2,2)) %!error ppplot (1, 2) %!error ppplot ([1 2 3 4], 2) statistics-release-1.7.3/inst/princomp.m000066400000000000000000000125721475240274700203370ustar00rootroot00000000000000## Copyright (C) 2013-2019 Fernando Damian Nieuwveldt ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 3 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{COEFF} =} princomp (@var{X}) ## @deftypefnx {statistics} {[@var{COEFF}, @var{SCORE}] =} princomp (@var{X}) ## @deftypefnx {statistics} {[@var{COEFF}, @var{SCORE}, @var{latent}] =} princomp (@var{X}) ## @deftypefnx {statistics} {[@var{COEFF}, @var{SCORE}, @var{latent}, @var{tsquare}] =} princomp (@var{X}) ## @deftypefnx {statistics} {[@dots{}] =} princomp (@var{X}, "econ") ## ## Performs a principal component analysis on a NxP data matrix X. ## ## @itemize @bullet ## @item ## @var{COEFF} : returns the principal component coefficients ## @item ## @var{SCORE} : returns the principal component scores, the representation of X ## in the principal component space ## @item ## @var{LATENT} : returns the principal component variances, i.e., the ## eigenvalues of the covariance matrix X. ## @item ## @var{TSQUARE} : returns Hotelling's T-squared Statistic for each observation ## in X ## @item ## [...] = princomp(X,'econ') returns only the elements of latent that are not ## necessarily zero, and the corresponding columns of COEFF and SCORE, that is, ## when n <= p, only the first n-1. This can be significantly faster when p is ## much larger than n. In this case the svd will be applied on the transpose of ## the data matrix X ## ## @end itemize ## ## @subheading References ## ## @enumerate ## @item ## Jolliffe, I. T., Principal Component Analysis, 2nd Edition, Springer, 2002 ## ## @end enumerate ## @end deftypefn function [COEFF, SCORE, latent, tsquare] = princomp (X, varargin) if (nargin < 1 || nargin > 2) print_usage (); endif if (nargin == 2 && ! strcmpi (varargin{:}, "econ")) error (strcat (["princomp: if a second input argument is present,"], ... [" it must be the string 'econ'."])); endif [nobs nvars] = size(X); # Center the columns to mean zero Xcentered = bsxfun(@minus,X,mean(X)); # Check if there are more variables then observations if nvars <= nobs [U,S,COEFF] = svd(Xcentered, "econ"); else # Calculate the svd on the transpose matrix, much faster if (nargin == 2 && strcmpi ( varargin{:} , "econ")) [COEFF,S,V] = svd(Xcentered' , 'econ'); else [COEFF,S,V] = svd(Xcentered'); endif endif if nargout > 1 # Get the Scores SCORE = Xcentered*COEFF; # Get the rank of the SCORE matrix r = rank(SCORE); # Only use the first r columns, pad rest with zeros if economy != 'econ' SCORE = SCORE(:,1:r) ; if !(nargin == 2 && strcmpi ( varargin{:} , "econ")) SCORE = [SCORE, zeros(nobs , nvars-r)]; else COEFF = COEFF(: , 1:r); endif endif if nargout > 2 # This is the same as the eigenvalues of the covariance matrix of X latent = (diag(S'*S)/(size(Xcentered,1)-1))(1:r); if !(nargin == 2 && strcmpi ( varargin{:} , "econ")) latent= [latent;zeros(nvars-r,1)]; endif endif if nargout > 3 # Calculate the Hotelling T-Square statistic for the observations tsquare = sumsq(zscore(SCORE(:,1:r)),2); endif endfunction %!shared COEFF,SCORE,latent,tsquare,m,x,R,V,lambda,i,S,F #NIST Engineering Statistics Handbook example (6.5.5.2) %!test %! x=[7 4 3 %! 4 1 8 %! 6 3 5 %! 8 6 1 %! 8 5 7 %! 7 2 9 %! 5 3 3 %! 9 5 8 %! 7 4 5 %! 8 2 2]; %! R = corrcoef (x); %! [V, lambda] = eig (R); %! [~, i] = sort(diag(lambda), "descend"); #arrange largest PC first %! S = V(:, i) * diag(sqrt(diag(lambda)(i))); %! ## contribution of first 2 PCs to each original variable %!assert(diag(S(:, 1:2)*S(:, 1:2)'), [0.8662; 0.8420; 0.9876], 1E-4); %! B = V(:, i) * diag( 1./ sqrt(diag(lambda)(i))); %! F = zscore(x)*B; %! [COEFF,SCORE,latent,tsquare] = princomp(zscore(x, 1)); %!assert(tsquare,sumsq(F, 2),1E4*eps); %!test %! x=[1,2,3;2,1,3]'; %! [COEFF,SCORE,latent,tsquare] = princomp(x); %! m=[sqrt(2),sqrt(2);sqrt(2),-sqrt(2);-2*sqrt(2),0]/2; %! m(:,1) = m(:,1)*sign(COEFF(1,1)); %! m(:,2) = m(:,2)*sign(COEFF(1,2)); %!assert(COEFF,m(1:2,:),10*eps); %!assert(SCORE,-m,10*eps); %!assert(latent,[1.5;.5],10*eps); %!assert(tsquare,[4;4;4]/3,10*eps); %!test %! x=x'; %! [COEFF,SCORE,latent,tsquare] = princomp(x); %! m=[sqrt(2),sqrt(2),0;-sqrt(2),sqrt(2),0;0,0,2]/2; %! m(:,1) = m(:,1)*sign(COEFF(1,1)); %! m(:,2) = m(:,2)*sign(COEFF(1,2)); %! m(:,3) = m(:,3)*sign(COEFF(3,3)); %!assert(COEFF,m,10*eps); %!assert(SCORE(:,1),-m(1:2,1),10*eps); %!assert(SCORE(:,2:3),zeros(2),10*eps); %!assert(latent,[1;0;0],10*eps); %!assert(tsquare,[0.5;0.5],10*eps) %!test %! [COEFF,SCORE,latent,tsquare] = princomp(x, "econ"); %!assert(COEFF,m(:, 1),10*eps); %!assert(SCORE,-m(1:2,1),10*eps); %!assert(latent,[1],10*eps); %!assert(tsquare,[0.5;0.5],10*eps) statistics-release-1.7.3/inst/private/000077500000000000000000000000001475240274700177755ustar00rootroot00000000000000statistics-release-1.7.3/inst/private/exact2xkCT.m000066400000000000000000000167771475240274700221550ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Private Function} [@var{p_net}, @var{p_val}] = exact2xkCT (@var{ct}, @var{weights}, @var{rsstat}) ## ## Compute the exact p-value for a 2-by-K contingency table based on the ## network algorithm. ## ## Reference: Cyrus R. Mehta & Nitin R. Patel (1980) A network algorithm for ## the exact treatment of the 2×k contingency table, Communications in ## Statistics - Simulation and Computation, 9:6, 649-664, ## DOI: 10.1080/03610918008812182 ## ## @end deftypefn function [p_net, p_val] = exact2xkCT (ct, weights, rsstat) ## Calculate nodes and arcs [nodes, arcs] = build_nodes (ct,weights); ## Apply backward induction to nodes nodes = backward_induce (nodes,arcs); ## Forward scan the network to get p-values p_val = forward_scan (nodes, arcs, rsstat); ## Calculate p-values TP = nodes{4,1}; p_val = p_val / TP; p_net = p_val(2) + min(p_val(1), p_val(3)); endfunction ## Calculate structures describing nodes and arcs function [nodes, arcs] = build_nodes (ct, weights) column = size (ct, 2); ## number of columns in contigency table rowsum = sum (ct, 2); ## sum of rows colsum = sum (ct, 1); ## sum of columns oldnodes = zeros(1,2); ## nodes added during last pass oldlo = 0; ## min possible sum so far oldhi = 0; ## max possible sum so far oldnn = 1; ## node numbers (row numbers) from last pass ctsum = rowsum(1); ## sum of entries in first row nodecount = 1; ## current node count ## Initialize cell structures for nodes and arcs nodes = cell(4, column+1); ## to hold nodes nodes{1,1} = zeros (1,2); ## n-by-2 array, n = # of nodes, row = [j,mj] nodes{2,column+1} = 0; ## n-vector of longest path to end from here nodes{3,column+1} = 0; ## n-vector of shortest path to end from here nodes{4,column+1} = 1; ## n-vector of total probability to end from here arcs = cell(3, column); ## to hold arcs ## row 1: n-by-2 array, n = # of connections, row = pair connected ## row 2: n-vector of arc lengths ## row 3: n-vector of arc probabilities for j = 1:column ## Find nodes possible at the next step nj = colsum(j); lo = max (oldlo, ctsum - sum (colsum(j+1:end))); hi = min (ctsum, oldhi + nj); newnodes = zeros (hi - lo + 1,2); newnodes(:,1) = j; newnodes(:,2) = (lo:hi)'; newnn = 1:size (newnodes,1); nodecount = nodecount + size (newnodes, 1); nodes{1,j+1} = newnodes; ## Find arcs possible to the next step [a0, a1] = meshgrid (oldnn, newnn); a0 = a0(:); a1 = a1(:); oldsum = oldnodes(a0,2); newsum = newnodes(a1,2); xj = newsum - oldsum; ok = (xj >= 0) & (xj <= nj); arcs{1,j} = [a0(ok) a1(ok)]; ## arc connections xj = xj(ok); arcs{2,j} = weights(j) * xj; pj = exp (gammaln (nj + 1) - gammaln (xj + 1) - gammaln (nj - xj + 1)); arcs{3,j} = pj; ## arc probabilities ## Update data structures oldlo = lo; oldhi = hi; oldnodes = newnodes; oldnn = newnn; endfor endfunction ## Calculate backward induction by adding information to NODES array function nodes = backward_induce (nodes, arcs) ## initialize for final node column = size (nodes,2) - 1; startSP = zeros (1); startLP = startSP; startTP = ones (1); for j = column:-1:1 ## destination nodes are previous start nodes endSP = startSP; endLP = startLP; endTP = startTP; ## get new start nodes and information about them a = arcs{1,j}; startmax = max(a(:,1)); startSP = zeros(startmax,1); startLP = startSP; startTP = startSP; arclen = arcs{2,j}; arcprob = arcs{3,j}; for nodenum = 1:startmax % for each start node, compute SP, LP, TP k1 = find(a(:,1) == nodenum); k2 = a(k1,2); startLP(nodenum) = max(arclen(k1) + endLP(k2)); startSP(nodenum) = min(arclen(k1) + endSP(k2)); startTP(nodenum) = sum(arcprob(k1) .* endTP(k2)); endfor ## store information about nodes at this level nodes{2,j} = startLP; nodes{3,j} = startSP; nodes{4,j} = startTP; endfor endfunction ## Get p-values by forward scanning the network function p_val = forward_scan (nodes, arcs, rsstat) NROWS = 50; p_val = zeros(3,1); ## [ProbT] stack = zeros(NROWS, 4); stack(:,1) = Inf; stack(1,1) = 1; ## level of current node stack(1,2) = 1; ## number at this level of current node stack(1,3) = 0; ## length so far to this node stack(1,4) = 1; ## probability so far of reaching this node N = size (stack, 1); i1 = 0; i2 = 0; i3 = 0; while (1) ## Get next lowest level node to process minlevel = min(stack((stack(1:N)>0))); if (isinf (minlevel)) break; endif sp = find (stack(1:N) == minlevel); sp = sp(1); L = stack(sp,1); J = stack(sp,2); pastL = stack(sp,3); pastP = stack(sp,4); stack(sp,1) = Inf; ## Get info for arcs at level L and their target nodes LP = nodes{2,L+1}; SP = nodes{3,L+1}; TP = nodes{4,L+1}; aj = arcs{1,L}; arclen = arcs{2,L}; arcprob = arcs{3,L}; ## Look only at arcs from node J seps = sqrt (eps); arows = find (aj(:,1) == J)'; for k = arows tonode = aj(k,2); thisL = arclen(k); thisP = pastP * arcprob(k); len = pastL + thisL; ## No paths from node J are signicant if (len + LP(tonode) < rsstat - seps) p_val(1) = p_val(1) + thisP * TP(tonode); ## All paths from node J are significant elseif (len + SP(tonode) > rsstat + seps) p_val(3) = p_val(3) + thisP * TP(tonode); ## Single match from node J elseif (SP(tonode) == LP(tonode)) p_val(2) = p_val(2) + thisP * TP(tonode); ## Match node J with another already stored node else ## Find a stored node that matches this one r = find(stack(:,1) == L+1); if (any (r)) r = r(stack(r,2) == tonode); if (any (r)) r = r(abs (stack(r,3) - len) < seps); endif endif ## If any one is found, merge node J with it if (any (r)) sp = r(1); stack(sp,4) = stack(sp,4) + thisP; i1 = i1 + 1; ## Otherwise add a new node else z = find(isinf(stack(:,1))); if (isempty (z)) i2 = i2 +1; block = zeros (NROWS, 4); block(:,1) = Inf; stack = [stack; block]; sp = N + 1; N = N + NROWS; else i3 = i3 + 1; sp = z(1); endif stack(sp,1) = L + 1; stack(sp,2) = tonode; stack(sp,3) = len; stack(sp,4) = thisP; endif endif endfor endwhile endfunction statistics-release-1.7.3/inst/private/getlinkfunctions.m000066400000000000000000000142731475240274700235500ustar00rootroot00000000000000## Copyright (C) 2025 Andreas Bertsatos ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Private Function} [@var{flink}, @var{dlink}, @var{ilink}, @var{errmsg}] = getlinkfunctions (@var{linkArg}) ## ## Return the link function and its derivative and inverse base. ## ## @end deftypefn function [flink, dlink, ilink, errmsg] = getlinkfunctions (linkArg) flink = dlink = ilink = []; errmsg = ""; ## linkArg is a scalar structure if (isstruct (linkArg)) if (! isscalar (linkArg)) errmsg = "structure with custom link functions must be a scalar."; return; endif rf = {"Link", "Derivative", "Inverse"}; if (! all (ismember (rf, fieldnames (linkArg)))) errmsg = ["structure with custom link functions requires", ... " the fields 'Link', 'Derivative', and 'Inverse'."]; return; endif if (ischar (linkArg.Link) && ! isempty (which (linkArg.Link))) flink = @(mu) feval (linkArg.Link, mu); elseif (isa (linkArg.Link, "function_handle")) flink = linkArg.Link; else errmsg = ["bad 'Link' function in custom link function structure."]; return; endif errmsg = testLinkFunction (flink, "Link"); if (! isempty (errmsg)) return; endif if (ischar (linkArg.Derivative) && ! isempty (which (linkArg.Derivative))) dlink = @(mu) feval (linkArg.Derivative, mu); elseif (isa (linkArg.Derivative, "function_handle")) dlink = linkArg.Derivative; else errmsg = ["bad 'Derivative' function in custom link function structure."]; return; endif errmsg = testLinkFunction (dlink, "Derivative"); if (! isempty (errmsg)) return; endif if (ischar (linkArg.Inverse) && ! isempty (which (linkArg.Inverse))) ilink = @(mu) feval (linkArg.Inverse, mu); elseif (isa (linkArg.Inverse, "function_handle")) ilink = linkArg.Inverse; else errmsg = ["bad 'Inverse' function in custom link function structure."]; return; endif errmsg = testLinkFunction (ilink, "Inverse"); if (! isempty (errmsg)) return; endif ## linkArg is a cell array elseif (iscell (linkArg)) if (numel (linkArg) != 3) errmsg = "cell array with custom link functions must have three elements."; return; endif if (isa (linkArg{1}, "function_handle")) flink = linkArg{1}; else errmsg = ["bad 'Link' function in custom link function cell array."]; return; endif errmsg = testLinkFunction (flink, "Link"); if (! isempty (errmsg)) return; endif if (isa (linkArg{2}, "function_handle")) dlink = linkArg{2}; else errmsg = ["bad 'Derivative' function in custom link function cell array."]; return; endif errmsg = testLinkFunction (dlink, "Derivative"); if (! isempty (errmsg)) return; endif if (isa (linkArg{3}, "function_handle")) ilink = linkArg{3}; else errmsg = ["bad 'Inverse' function in custom link function cell array."]; return; endif errmsg = testLinkFunction (ilink, "Inverse"); if (! isempty (errmsg)) return; endif ## linkArg is a scalar value elseif (isnumeric (linkArg)) if (! (isscalar (linkArg) && isfinite (linkArg) && isreal (linkArg))) errmsg = ["numeric input for custom link function", ... " must be a finite real scalar value."]; return; endif flink = @(mu) mu .^ linkArg; dlink = @(mu) linkArg .* mu .^ (linkArg - 1); ilink = @(eta) eta .^ (1 / linkArg); ## linkArg is character vector elseif (ischar (linkArg)) if (! isvector (linkArg)) errmsg = "canonical link function name must be a character vector."; return; endif supported_link_functions = {"identity", "log", "logit", "probit", ... "loglog", "comploglog", "reciprocal"}; if (! any (strcmpi (linkArg, supported_link_functions))) errmsg = sprintf ("canonical link function '%s' is not supported.", ... linkArg); return; endif ## Select a canonical link function switch (linkArg) case "identity" flink = @(mu) mu; dlink = @(mu) 1; ilink = @(eta) eta; case "log" flink = @(mu) log (mu); dlink = @(mu) 1 ./ (mu); ilink = @(eta) exp (eta); case "logit" flink = @(mu) log (mu ./ (1 - mu)); dlink = @(mu) 1 ./ (mu .* (1 - mu)); ilink = @(eta) 1 ./ (1 + exp (- eta)); case "probit" flink = @(mu) norminv (mu); dlink = @(mu) 1 ./ normpdf (norminv (mu)); ilink = @(eta) normcdf (eta); case "loglog" flink = @(mu) log (- log (mu)); dlink = @(mu) 1 ./ (mu .* log (mu)); ilink = @(eta) exp (- exp (eta)); case "comploglog" flink = @(mu) log (- log1p (- mu)); dlink = @(mu) 1 ./ - ((1 - mu) .* log1p (- mu)); ilink = @(eta) -expm1 (- exp (eta)); case "reciprocal" flink = @(mu) 1 ./ mu; dlink = @(mu) -1 ./ (mu .^ 2); ilink = @(eta) 1 ./ (eta); endswitch else errmsg = "invalid value for custom link function."; endif endfunction function errmsg = testLinkFunction (flink, linkname); errmsg = ""; testInput = [1; 2; 3; 4; 5]; try testOutput = flink (testInput); if (! isequal (size (testInput), size (testOutput))) errmsg = sprintf (["custom '%s' function must return an output", ... " of the same size as input."], linkname); endif catch errmsg = sprintf ("invalid custom '%s' function.", linkname); end_try_catch endfunction statistics-release-1.7.3/inst/probit.m000066400000000000000000000026141475240274700200030ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{x} =} probit (@var{p}) ## ## Probit transformation ## ## Return the probit (the quantile of the standard normal distribution) for ## each element of @var{p}. ## ## @seealso{logit} ## @end deftypefn function x = probit (p) if (nargin != 1) print_usage (); endif x = -sqrt (2) * erfcinv (2 * p); endfunction ## Test output %!assert (probit ([-1, 0, 0.5, 1, 2]), [NaN, -Inf, 0, Inf, NaN]) %!assert (probit ([0.2, 0.99]), norminv ([0.2, 0.99])) ## Test input validation %!error probit () %!error probit (1, 2) statistics-release-1.7.3/inst/procrustes.m000066400000000000000000000314541475240274700207210ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/OR ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, OR (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{d} =} procrustes (@var{X}, @var{Y}) ## @deftypefnx {statistics} {@var{d} =} procrustes (@var{X}, @var{Y}, @var{param1}, @var{value1}, @dots{}) ## @deftypefnx {statistics} {[@var{d}, @var{Z}] =} procrustes (@dots{}) ## @deftypefnx {statistics} {[@var{d}, @var{Z}, @var{transform}] =} procrustes (@dots{}) ## ## Procrustes Analysis. ## ## @code{@var{d} = procrustes (@var{X}, @var{Y})} computes a linear ## transformation of the points in the matrix @var{Y} to best conform them to ## the points in the matrix @var{X} by minimizing the sum of squared errors, as ## the goodness of fit criterion, which is returned in @var{d} as a ## dissimilarity measure. @var{d} is standardized by a measure of the scale of ## @var{X}, given by ## @itemize ## @item @qcode{sum (sum ((X - repmat (mean (X, 1), size (X, 1), 1)) .^ 2, 1))} ## @end itemize ## i.e., the sum of squared elements of a centered version of @var{X}. However, ## if @var{X} comprises repetitions of the same point, the sum of squared errors ## is not standardized. ## ## @var{X} and @var{Y} must have the same number of points (rows) and ## @qcode{procrustes} matches the @math{i}-th point in @var{Y} to the ## @math{i}-th point in @var{X}. Points in @var{Y} can have smaller dimensions ## (columns) than those in @var{X}, but not the opposite. Missing dimensions in ## @var{Y} are added with padding columns of zeros as necessary to match the ## the dimensions in @var{X}. ## ## @code{[@var{d}, @var{Z}] = procrustes (@var{X}, @var{Y})} also returns the ## transformed values in @var{Y}. ## ## @code{[@var{d}, @var{Z}, @var{transform}] = procrustes (@var{X}, @var{Y})} ## also returns the transformation that maps @var{Y} to @var{Z}. ## ## @var{transform} is a structure with fields: ## ## @multitable @columnfractions 0.05 0.1 0.05 0.8 ## @item @tab @qcode{c} @tab @tab the translation component ## @item @tab @qcode{T} @tab @tab the orthogonal rotation and reflection ## component ## @item @tab @qcode{b} @tab @tab the scale component ## @end multitable ## ## So that @code{@var{Z} = @var{transform}.@qcode{b} * @var{Y} * ## @var{transform}.@qcode{T} + @var{transform}.@qcode{c}} ## ## @qcode{procrustes} can take two optional parameters as Name-Value pairs. ## ## @code{[@dots{}] = procrustes (@dots{}, @qcode{"Scaling"}, @qcode{false})} ## computes a transformation that does not include scaling, that is ## @var{transform}.@qcode{b} = 1. Setting @qcode{"Scaling"} to @qcode{true} ## includes a scaling component, which is the default. ## ## @code{[@dots{}] = procrustes (@dots{}, @qcode{"Reflection"}, @qcode{false})} ## computes a transformation that does not include a reflection component, that ## is @var{transform}.@qcode{T} = 1. Setting @qcode{"Reflection"} to ## @qcode{true} forces the solution to include a reflection component in the ## computed transformation, that is @var{transform}.@qcode{T} = -1. ## ## @code{[@dots{}] = procrustes (@dots{}, @qcode{"Reflection"}, @qcode{"best"})} ## computes the best fit procrustes solution, which may or may not include a ## reflection component, which is the default. ## ## @seealso{cmdscale} ## @end deftypefn function [d, Z, transform] = procrustes (X, Y, varargin) if (nargin < 1) error ("fishertest: contingency table is missing."); endif if (nargin > 5) error ("fishertest: too many input parameters."); endif ## Check X and Y for appropriate input if (isempty (X) || ! ismatrix (X) || ndims (X) != 2 || ... isempty (Y) || ! ismatrix (Y) || ndims (Y) != 2) error ("procrustes: X and Y must be 2-dimensional matrices."); endif if (any (isnan (X(:))) || any (isinf (X(:))) || iscomplex (X) || ... any (isnan (Y(:))) || any (isinf (Y(:))) || iscomplex (Y)) error ("procrustes: values in X and Y must be real."); endif [Xp, Xd] = size (X); [Yp, Yd] = size (Y); if (Yp != Xp) error ("procrustes: X and Y must have equal number of rows."); elseif (Yd > Xd) error ("procrustes: X must have at least as many columns as Y."); endif ## Add defaults and parse optional arguments scaling = true; reflection = "best"; if (nargin > 2) params = numel (varargin); if ((params / 2) != fix (params / 2)) error ("procrustes: optional arguments must be in Name-Value pairs.") endif for idx = 1:2:params name = varargin{idx}; value = varargin{idx+1}; switch (lower (name)) case "scaling" scaling = value; if (! (isscalar (scaling) && islogical (scaling))) error ("procrustes: invalid value for scaling."); endif case "reflection" reflection = value; if (! (strcmpi (reflection, "best") || islogical (reflection))) error ("procrustes: invalid value for reflection."); endif otherwise error ("procrustes: invalid name for optional arguments."); endswitch endfor endif ## Center at the origin. Xmu = mean (X, 1); Ymu = mean (Y, 1); X_0 = X - repmat (Xmu, Xp, 1); Y_0 = Y - repmat (Ymu, Xp, 1); ## Get centroid size and check for X or Y having identical points Xsumsq = sum (X_0 .^ 2, 1); Ysumsq = sum (Y_0 .^ 2, 1); constX = all (Xsumsq <= abs (eps (class (X)) * Xp * Xmu) .^ 2); constY = all (Ysumsq <= abs (eps (class (X)) * Xp * Ymu) .^ 2); Xsumsq = sum (Xsumsq); Ysumsq = sum (Ysumsq); if (! constX && ! constY) ## Scale to "centered" Frobenius norm. normX = sqrt (Xsumsq); normY = sqrt (Ysumsq); X_0 = X_0 / normX; Y_0 = Y_0 / normY; ## Fix dimension space (if necessary) if (Yd < Xd) Y_0 = [Y_0 zeros(Xp, Xd-Yd)]; end ## Find optimal rotation matrix of Y A = X_0' * Y_0; [U, S, V] = svd (A); T = V * U'; ## Handle reflection only if 'true' or 'false' was given if (! strcmpi (reflection, "best")) is_reflection = (det(T) < 0); ## Force a reflection if data and reflection option disagree if (reflection != is_reflection) V(:,end) = -V(:,end); S(end,end) = -S(end,end); T = V * U'; endif endif ## Apply scaling (if requested) traceTA = sum (diag (S)); if (scaling) b = traceTA * normX / normY; d = 1 - traceTA .^ 2; if (nargout > 1) Z = normX * traceTA * Y_0 * T + repmat (Xmu, Xp, 1); endif else b = 1; d = 1 + Ysumsq / Xsumsq - 2 * traceTA * normY / normX; if (nargout > 1) Z = normY * Y_0 * T + repmat (Xmu, Xp, 1); endif endif ## 3rd output argument if (nargout > 2) if (Yd < Xd) T = T(1:Yd,:); endif c = Xmu - b * Ymu * T; transform = struct ("T", T, "b", b, "c", repmat (c, Xp, 1)); end ## Special cases elseif constX # Identical points in X d = 0; Z = repmat (Xmu, Xp, 1); T = eye (Yd, Xd); transform = struct ("T", T, "b", 0, "c", Z); else # Identical points in Y d = 1; Z = repmat (Xmu, Xp, 1); T = eye (Yd, Xd); transform = struct ("T", T, "b", 0, "c", Z); endif endfunction %!demo %! ## Create some random points in two dimensions %! n = 10; %! randn ("seed", 1); %! X = normrnd (0, 1, [n, 2]); %! %! ## Those same points, rotated, scaled, translated, plus some noise %! S = [0.5, -sqrt(3)/2; sqrt(3)/2, 0.5]; # rotate 60 degrees %! Y = normrnd (0.5*X*S + 2, 0.05, n, 2); %! %! ## Conform Y to X, plot original X and Y, and transformed Y %! [d, Z] = procrustes (X, Y); %! plot (X(:,1), X(:,2), "rx", Y(:,1), Y(:,2), "b.", Z(:,1), Z(:,2), "bx"); %!demo %! ## Find Procrustes distance and plot superimposed shape %! %! X = [40 88; 51 88; 35 78; 36 75; 39 72; 44 71; 48 71; 52 74; 55 77]; %! Y = [36 43; 48 42; 31 26; 33 28; 37 30; 40 31; 45 30; 48 28; 51 24]; %! plot (X(:,1),X(:,2),"x"); %! hold on %! plot (Y(:,1),Y(:,2),"o"); %! xlim ([0 100]); %! ylim ([0 100]); %! legend ("Target shape (X)", "Source shape (Y)"); %! [d, Z] = procrustes (X, Y) %! plot (Z(:,1), Z(:,2), "s"); %! legend ("Target shape (X)", "Source shape (Y)", "Transformed shape (Z)"); %! hold off %!demo %! ## Apply Procrustes transformation to larger set of points %! %! ## Create matrices with landmark points for two triangles %! X = [5, 0; 5, 5; 8, 5]; # target %! Y = [0, 0; 1, 0; 1, 1]; # source %! %! ## Create a matrix with more points on the source triangle %! Y_mp = [linspace(Y(1,1),Y(2,1),10)', linspace(Y(1,2),Y(2,2),10)'; ... %! linspace(Y(2,1),Y(3,1),10)', linspace(Y(2,2),Y(3,2),10)'; ... %! linspace(Y(3,1),Y(1,1),10)', linspace(Y(3,2),Y(1,2),10)']; %! %! ## Plot both shapes, including the larger set of points for the source shape %! plot ([X(:,1); X(1,1)], [X(:,2); X(1,2)], "bx-"); %! hold on %! plot ([Y(:,1); Y(1,1)], [Y(:,2); Y(1,2)], "ro-", "MarkerFaceColor", "r"); %! plot (Y_mp(:,1), Y_mp(:,2), "ro"); %! xlim ([-1 10]); %! ylim ([-1 6]); %! legend ("Target shape (X)", "Source shape (Y)", ... %! "More points on Y", "Location", "northwest"); %! hold off %! %! ## Obtain the Procrustes transformation %! [d, Z, transform] = procrustes (X, Y) %! %! ## Use the Procrustes transformation to superimpose the more points (Y_mp) %! ## on the source shape onto the target shape, and then visualize the results. %! Z_mp = transform.b * Y_mp * transform.T + transform.c(1,:); %! figure %! plot ([X(:,1); X(1,1)], [X(:,2); X(1,2)], "bx-"); %! hold on %! plot ([Y(:,1); Y(1,1)], [Y(:,2); Y(1,2)], "ro-", "MarkerFaceColor", "r"); %! plot (Y_mp(:,1), Y_mp(:,2), "ro"); %! xlim ([-1 10]); %! ylim ([-1 6]); %! plot ([Z(:,1); Z(1,1)],[Z(:,2); Z(1,2)],"ks-","MarkerFaceColor","k"); %! plot (Z_mp(:,1),Z_mp(:,2),"ks"); %! legend ("Target shape (X)", "Source shape (Y)", ... %! "More points on Y", "Transformed source shape (Z)", ... %! "Transformed additional points", "Location", "northwest"); %! hold off %!demo %! ## Compare shapes without reflection %! %! T = [33, 93; 33, 87; 33, 80; 31, 72; 32, 65; 32, 58; 30, 72; ... %! 28, 72; 25, 69; 22, 64; 23, 59; 26, 57; 30, 57]; %! S = [48, 83; 48, 77; 48, 70; 48, 65; 49, 59; 49, 56; 50, 66; ... %! 52, 66; 56, 65; 58, 61; 57, 57; 54, 56; 51, 55]; %! plot (T(:,1), T(:,2), "x-"); %! hold on %! plot (S(:,1), S(:,2), "o-"); %! legend ("Target shape (d)", "Source shape (b)"); %! hold off %! d_false = procrustes (T, S, "reflection", false); %! printf ("Procrustes distance without reflection: %f\n", d_false); %! d_true = procrustes (T, S, "reflection", true); %! printf ("Procrustes distance with reflection: %f\n", d_true); %! d_best = procrustes (T, S, "reflection", "best"); %! printf ("Procrustes distance with best fit: %f\n", d_true); ## Test input validation %!error procrustes (); %!error procrustes (1, 2, 3, 4, 5, 6); %!error ... %! procrustes (ones (2, 2, 2), ones (2, 2, 2)); %!error ... %! procrustes ([1, 2; -3, 4; 2, 3], [1, 2; -3, 4; 2, 3+i]); %!error ... %! procrustes ([1, 2; -3, 4; 2, 3], [1, 2; -3, 4; 2, NaN]); %!error ... %! procrustes ([1, 2; -3, 4; 2, 3], [1, 2; -3, 4; 2, Inf]); %!error ... %! procrustes (ones (10 ,3), ones (11, 3)); %!error ... %! procrustes (ones (10 ,3), ones (10, 4)); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), "reflection"); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), true); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), "scaling", 0); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), "scaling", [true true]); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), "reflection", 1); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), "reflection", "some"); %!error ... %! procrustes (ones (10 ,3), ones (10, 3), "param1", "some"); statistics-release-1.7.3/inst/qqplot.m000066400000000000000000000103131475240274700200170ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{q}, @var{s}] =} qqplot (@var{x}) ## @deftypefnx {statistics} {[@var{q}, @var{s}] =} qqplot (@var{x}, @var{y}) ## @deftypefnx {statistics} {[@var{q}, @var{s}] =} qqplot (@var{x}, @var{dist}) ## @deftypefnx {statistics} {[@var{q}, @var{s}] =} qqplot (@var{x}, @var{y}, @var{params}) ## @deftypefnx {statistics} {} qqplot (@dots{}) ## ## Perform a QQ-plot (quantile plot). ## ## If F is the CDF of the distribution @var{dist} with parameters ## @var{params} and G its inverse, and @var{x} a sample vector of length ## @var{n}, the QQ-plot graphs ordinate @var{s}(@var{i}) = @var{i}-th ## largest element of x versus abscissa @var{q}(@var{i}f) = G((@var{i} - ## 0.5)/@var{n}). ## ## If the sample comes from F, except for a transformation of location ## and scale, the pairs will approximately follow a straight line. ## ## If the second argument is a vector @var{y} the empirical CDF of @var{y} ## is used as @var{dist}. ## ## The default for @var{dist} is the standard normal distribution. The ## optional argument @var{params} contains a list of parameters of ## @var{dist}. For example, for a quantile plot of the uniform ## distribution on [2,4] and @var{x}, use ## ## @example ## qqplot (x, "unif", 2, 4) ## @end example ## ## @noindent ## @var{dist} can be any string for which a function @var{distinv} or ## @var{dist_inv} exists that calculates the inverse CDF of distribution ## @var{dist}. ## ## If no output arguments are given, the data are plotted directly. ## @seealso{ppplot} ## @end deftypefn function [qout, sout] = qqplot (x, dist, varargin) if (nargin < 1) print_usage (); endif if (! isnumeric (x) || ! isreal (x) || ! isvector (x) || isscalar (x)) error ("qqplot: X must be a numeric vector of real numbers"); endif if (nargin == 1) f = @probit; else if (isnumeric (dist)) f = @(y) empirical_inv (y, dist); elseif (ischar (dist) && (exist (invname = [dist "inv"]) || exist (invname = [dist "_inv"]))) f = str2func (invname); else error ("qqplot: no inverse CDF found for distribution DIST"); endif endif; s = sort (x); n = length (x); t = ((1 : n)' - .5) / n; if (nargin <= 2) q = f (t); q_label = func2str (f); else q = f (t, varargin{:}); if (nargin == 3) q_label = sprintf ("%s with parameter %g", func2str (f), varargin{1}); else q_label = sprintf ("%s with parameters %g", func2str (f), varargin{1}); param_str = sprintf (", %g", varargin{2:end}); q_label = [q_label param_str]; endif endif if (nargout == 0) plot (q, s, "-x"); q_label = strrep (q_label, '_inv', '\_inv'); if (q_label(1) == '@') q_label = q_label(6:end); # Strip "@(y) " from anon. function endif xlabel (q_label); ylabel ("sample points"); else qout = q; sout = s; endif endfunction ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! qqplot ([2 3 3 4 4 5 6 5 6 7 8 9 8 7 8 9 0 8 7 6 5 4 6 13 8 15 9 9]); %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect ## Test input validation %!error qqplot () %!error qqplot ({1}) %!error qqplot (ones (2,2)) %!error qqplot (1, "foobar") %!error qqplot ([1 2 3], "foobar") statistics-release-1.7.3/inst/qrandn.m000066400000000000000000000062371475240274700177740ustar00rootroot00000000000000## Copyright (C) 2014 - Juan Pablo Carbajal ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{z} =} qrandn (@var{q}, @var{r}, @var{c}) ## @deftypefnx {statistics} {@var{z} =} qrandn (@var{q}, [@var{r}, @var{c}]) ## ## Returns random deviates drawn from a q-Gaussian distribution. ## ## Parameter @var{q} charcterizes the q-Gaussian distribution. ## The result has the size indicated by @var{s}. ## ## Reference: ## W. Thistleton, J. A. Marsh, K. Nelson, C. Tsallis (2006) ## "Generalized Box-Muller method for generating q-Gaussian random deviates" ## arXiv:cond-mat/0605570 http://arxiv.org/abs/cond-mat/0605570 ## ## @seealso{rand, randn} ## @end deftypefn function z = qrandn (q, R, C=[]) if (nargin < 2) print_usage; endif if (! isscalar (q)) error ("qrandn: the parameter q must be a scalar.")' endif ## Check that q < 3 if (q >= 3) error ("qrandn: the parameter q must be lower than 3."); endif if (numel (R) > 1) S = R; elseif (numel (R) == 1 && isempty (C)) S = [R, 1]; elseif (numel (R) == 1 && ! isempty (C)) S = [R, C]; endif ## Calaulate the q to be used on the q-log qGen = (1 + q) / (3 - q); ## Initialize the output vector z = sqrt (-2 * log_q (rand (S), qGen)) .* sin (2 * pi * rand (S)); endfunction ## Returns the q-log of x, using q function a = log_q (x, q) dq = 1 - q; ## Check to see if q = 1 (to double precision) if (abs (dq) < 10 * eps) ## If q is 1, use the usual natural logarithm a = log (x); else ## If q differs from 1, use the definition of the q-log a = (x .^ dq - 1) ./ dq; endif endfunction %!demo %! z = qrandn (-5, 5e6); %! [c x] = hist (z,linspace(-1.5,1.5,200),1); %! figure(1) %! plot(x,c,"r."); axis tight; axis([-1.5,1.5]); %! %! z = qrandn (-0.14286, 5e6); %! [c x] = hist (z,linspace(-2,2,200),1); %! figure(2) %! plot(x,c,"r."); axis tight; axis([-2,2]); %! %! z = qrandn (2.75, 5e6); %! [c x] = hist (z,linspace(-1e3,1e3,1e3),1); %! figure(3) %! semilogy(x,c,"r."); axis tight; axis([-100,100]); %! %! # --------- %! # Figures from the reference paper. ## Tests for input validation %!error qrandn ([1 2], 1) %!error qrandn (4, 1) %!error qrandn (3, 1) %!error qrandn (2.5, 1, 2, 3) %!error qrandn (2.5) ## Tests for output validation %!test %! q = 1.5; %! s = [2, 3]; %! z = qrandn (q, s); %! assert (isnumeric (z) && isequal (size (z), s)); statistics-release-1.7.3/inst/randsample.m000066400000000000000000000110161475240274700206260ustar00rootroot00000000000000## Copyright (C) 2014 - Nir Krakauer ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{y} =} randsample (@var{v}, @var{k}) ## @deftypefnx {statistics} {@var{y} =} randsample (@var{v}, @var{k}, @var{replacement}=false) ## @deftypefnx {statistics} {@var{y} =} randsample (@var{v}, @var{k}, @var{replacement}=false, [@var{w}=[]]) ## ## Sample elements from a vector. ## ## Returns @var{k} random elements from a vector @var{v} with @var{n} elements, ## sampled without or with @var{replacement}. ## ## If @var{v} is a scalar, samples from 1:@var{v}. ## ## If a weight vector @var{w} of the same size as @var{v} is specified, the ## probablility of each element being sampled is proportional to @var{w}. ## Unlike Matlab's function of the same name, this can be done for sampling with ## or without replacement. ## ## Randomization is performed using rand(). ## ## @seealso{datasample, randperm} ## @end deftypefn function y = randsample (v, k, replacement=false ,w=[]) if (isscalar (v) && isreal (v)) n = v; vector_v = false; elseif (isvector (v)) n = numel (v); vector_v = true; else error ("randsample: The input v must be a vector or positive integer."); endif if k < 0 || ( k > n && !replacement ) error (strcat (["randsample: The input k must be a non-negative "], ... ["integer. Sampling without replacement needs k <= n."])); endif if (all (length (w) != [0, n])) error ("randsample: the size w (%d) must match the first argument (%d)", ... length(w), n); endif if (replacement) # sample with replacement if (isempty (w)) # all elements are equally likely to be sampled y = round (n * rand (1, k) + 0.5); else y = weighted_replacement (k, w); endif else # sample without replacement if (isempty (w)) # all elements are equally likely to be sampled y = randperm (n, k); else # use "accept-reject"-like sampling y = weighted_replacement (k, w); while (1) [yy, idx] = sort (y); # Note: sort keeps order of equal elements. Idup = [false, (diff (yy)==0)]; if (! any (Idup)) break else Idup(idx) = Idup; # find duplicates in original vector w(y) = 0; # don't permit resampling ## remove duplicates, then sample again y = [y(! Idup), (weighted_replacement (sum (Idup), w))]; endif endwhile endif endif if vector_v y = v(y); endif endfunction function y = weighted_replacement (k, w) w = w / sum (w); w = [0, cumsum(w(:))']; ## distribute k uniform random deviates based on the given weighting y = arrayfun (@(x) find (w <= x, 1, "last"), rand (1, k)); endfunction %!test %! n = 20; %! k = 5; %! x = randsample(n, k); %! assert (size(x), [1 k]); %! x = randsample(n, k, true); %! assert (size(x), [1 k]); %! x = randsample(n, k, false); %! assert (size(x), [1 k]); %! x = randsample(n, k, true, ones(n, 1)); %! assert (size(x), [1 k]); %! x = randsample(1:n, k); %! assert (size(x), [1 k]); %! x = randsample(1:n, k, true); %! assert (size(x), [1 k]); %! x = randsample(1:n, k, false); %! assert (size(x), [1 k]); %! x = randsample(1:n, k, true, ones(n, 1)); %! assert (size(x), [1 k]); %! x = randsample((1:n)', k); %! assert (size(x), [k 1]); %! x = randsample((1:n)', k, true); %! assert (size(x), [k 1]); %! x = randsample((1:n)', k, false); %! assert (size(x), [k 1]); %! x = randsample((1:n)', k, true, ones(n, 1)); %! assert (size(x), [k 1]); %! n = 10; %! k = 100; %! x = randsample(n, k, true, 1:n); %! assert (size(x), [1 k]); %! x = randsample((1:n)', k, true); %! assert (size(x), [k 1]); %! x = randsample(k, k, false, 1:k); %! assert (size(x), [1 k]); statistics-release-1.7.3/inst/rangesearch.m000066400000000000000000000553001475240274700207660ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{idx} =} rangesearch (@var{X}, @var{Y}, @var{r}) ## @deftypefnx {statistics} {[@var{idx}, @var{D}] =} rangesearch (@var{X}, @var{Y}, @var{r}) ## @deftypefnx {statistics} {[@dots{}] =} rangesearch (@dots{}, @var{name}, @var{value}) ## ## Find all neighbors within specified distance from input data. ## ## @code{@var{idx} = rangesearch (@var{X}, @var{Y}, @var{r})} returns all the ## points in @var{X} that are within distance @var{r} from the points in @var{Y}. ## @var{X} must be an @math{NxP} numeric matrix of input data, where rows ## correspond to observations and columns correspond to features or variables. ## @var{Y} is an @math{MxP} numeric matrix with query points, which must have ## the same numbers of column as @var{X}. @var{r} must be a nonnegative scalar ## value. @var{idx} is an @math{Mx1} cell array, where @math{M} is the number ## of observations in @var{Y}. The vector @qcode{@var{Idx}@{j@}} contains the ## indices of observations (rows) in @var{X} whose distances to ## @qcode{@var{Y}(j,:)} are not greater than @var{r}. ## ## @code{[@var{idx}, @var{D}] = rangesearch (@var{X}, @var{Y}, @var{r})} also ## returns the distances, @var{D}, which correspond to the points in @var{X} ## that are within distance @var{r} from the points in @var{Y}. @var{D} is an ## @math{Mx1} cell array, where @math{M} is the number of observations in ## @var{Y}. The vector @qcode{@var{D}@{j@}} contains the indices of ## observations (rows) in @var{X} whose distances to @qcode{@var{Y}(j,:)} are ## not greater than @var{r}. ## ## Additional parameters can be specified by @qcode{Name-Value} pair arguments. ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"P"} @tab @tab is the Minkowski distance exponent and it must be ## a positive scalar. This argument is only valid when the selected distance ## metric is @qcode{"minkowski"}. By default it is 2. ## ## @item @qcode{"Scale"} @tab @tab is the scale parameter for the standardized ## Euclidean distance and it must be a nonnegative numeric vector of equal ## length to the number of columns in @var{X}. This argument is only valid when ## the selected distance metric is @qcode{"seuclidean"}, in which case each ## coordinate of @var{X} is scaled by the corresponding element of ## @qcode{"scale"}, as is each query point in @var{Y}. By default, the scale ## parameter is the standard deviation of each coordinate in @var{X}. ## ## @item @qcode{"Cov"} @tab @tab is the covariance matrix for computing the ## mahalanobis distance and it must be a positive definite matrix matching the ## the number of columns in @var{X}. This argument is only valid when the ## selected distance metric is @qcode{"mahalanobis"}. ## ## @item @qcode{"BucketSize"} @tab @tab is the maximum number of data points in ## the leaf node of the Kd-tree and it must be a positive integer. This ## argument is only valid when the selected search method is @qcode{"kdtree"}. ## ## @item @qcode{"SortIndices"} @tab @tab is a boolean flag to sort the returned ## indices in ascending order by distance and it is @qcode{true} by default. ## When the selected search method is @qcode{"exhaustive"} or the ## @qcode{"IncludeTies"} flag is true, @code{rangesearch} always sorts the ## returned indices. ## ## @item @qcode{"Distance"} @tab @tab is the distance metric used by ## @code{rangesearch} as specified below: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{"euclidean"} @tab Euclidean distance. ## @item @tab @qcode{"seuclidean"} @tab standardized Euclidean distance. Each ## coordinate difference between the rows in @var{X} and the query matrix ## @var{Y} is scaled by dividing by the corresponding element of the standard ## deviation computed from @var{X}. To specify a different scaling, use the ## @qcode{"Scale"} name-value argument. ## @item @tab @qcode{"cityblock"} @tab City block distance. ## @item @tab @qcode{"chebychev"} @tab Chebychev distance (maximum coordinate ## difference). ## @item @tab @qcode{"minkowski"} @tab Minkowski distance. The default exponent ## is 2. To specify a different exponent, use the @qcode{"P"} name-value ## argument. ## @item @tab @qcode{"mahalanobis"} @tab Mahalanobis distance, computed using a ## positive definite covariance matrix. To change the value of the covariance ## matrix, use the @qcode{"Cov"} name-value argument. ## @item @tab @qcode{"cosine"} @tab Cosine distance. ## @item @tab @qcode{"correlation"} @tab One minus the sample linear correlation ## between observations (treated as sequences of values). ## @item @tab @qcode{"spearman"} @tab One minus the sample Spearman's rank ## correlation between observations (treated as sequences of values). ## @item @tab @qcode{"hamming"} @tab Hamming distance, which is the percentage ## of coordinates that differ. ## @item @tab @qcode{"jaccard"} @tab One minus the Jaccard coefficient, which is ## the percentage of nonzero coordinates that differ. ## @item @tab @var{@@distfun} @tab Custom distance function handle. A distance ## function of the form @code{function @var{D2} = distfun (@var{XI}, @var{YI})}, ## where @var{XI} is a @math{1xP} vector containing a single observation in ## @math{P}-dimensional space, @var{YI} is an @math{NxP} matrix containing an ## arbitrary number of observations in the same @math{P}-dimensional space, and ## @var{D2} is an @math{NxP} vector of distances, where @qcode{(@var{D2}k)} is ## the distance between observations @var{XI} and @qcode{(@var{YI}k,:)}. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @item @qcode{"NSMethod"} @tab @tab is the nearest neighbor search method used ## by @code{rangesearch} as specified below. ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{"kdtree"} @tab Creates and uses a Kd-tree to find nearest ## neighbors. @qcode{"kdtree"} is the default value when the number of columns ## in @var{X} is less than or equal to 10, @var{X} is not sparse, and the ## distance metric is @qcode{"euclidean"}, @qcode{"cityblock"}, ## @qcode{"manhattan"}, @qcode{"chebychev"}, or @qcode{"minkowski"}. Otherwise, ## the default value is @qcode{"exhaustive"}. This argument is only valid when ## the distance metric is one of the four aforementioned metrics. ## @item @tab @qcode{"exhaustive"} @tab Uses the exhaustive search algorithm by ## computing the distance values from all the points in @var{X} to each point in ## @var{Y}. ## @end multitable ## ## @seealso{knnsearch, pdist2} ## @end deftypefn function [idx, dist] = rangesearch (X, Y, r, varargin) ## Check input data if (nargin < 2) error ("rangesearch: too few input arguments."); endif if (size (X, 2) != size (Y, 2)) error ("rangesearch: number of columns in X and Y must match."); endif ## Add default values P = 2; # Exponent for Minkowski distance S = []; # Scale for the standardized Euclidean distance C = []; # Covariance matrix for Mahalanobis distance BS = 50; # Maximum number of points per leaf node for Kd-tree SI = true; # Sort returned indices according to distance Distance = "euclidean"; # Distance metric to be used NSMethod = []; # Nearest neighbor search method DistParameter = []; # Distance parameter for pdist2 ## Parse additional parameters in Name/Value pairs PSC = 0; while (numel (varargin) > 0) switch (tolower (varargin{1})) case "p" P = varargin{2}; PSC += 1; case "scale" S = varargin{2}; PSC += 1; case "cov" C = varargin{2}; PSC += 1; case "bucketsize" BS = varargin{2}; case "sortindices" SI = varargin{2}; case "distance" Distance = varargin{2}; case "nsmethod" NSMethod = varargin{2}; otherwise error ("rangesearch: invalid NAME in optional pairs of arguments."); endswitch varargin(1:2) = []; endwhile ## Check input parameters if (PSC > 1) error ("rangesearch: only a single distance parameter can be defined."); endif if (! isscalar (P) || ! isnumeric (P) || P <= 0) error ("rangesearch: invalid value of Minkowski Exponent."); endif if (! isempty (S)) if (any (S) < 0 || numel (S) != columns (X) || ! strcmpi (Distance, "seuclidean")) error ("rangesearch: invalid value in Scale or the size of Scale."); endif endif if (! isempty (C)) if (! strcmp (Distance, "mahalanobis") || ! ismatrix (C) || ! isnumeric (C)) error (strcat (["rangesearch: invalid value in Cov, Cov can only"], ... [" be given for mahalanobis distance."])); endif endif if (! isscalar (BS) || BS < 0) error ("rangesearch: invalid value of bucketsize."); endif ## Select the appropriate distance parameter if (strcmpi (Distance, "minkowski")) DistParameter = P; elseif (strcmpi (Distance, "seuclidean")) DistParameter = S; elseif (strcmpi (Distance, "mahalanobis")) DistParameter = C; endif ## Check NSMethod and set kdtree as default if the conditions match if (isempty (NSMethod)) ## Set default method 'kdtree' if condintions are satistfied; if (! issparse (X) && (columns (X) <= 10) && ... (strcmpi (Distance, "euclidean") || strcmpi (Distance, "cityblock") || strcmpi (Distance, "minkowski") || strcmpi (Distance, "chebychev"))) NSMethod = "kdtree"; else NSMethod = "exhaustive"; endif else ## Not empty then check if is exhaustive or kdtree if (strcmpi (NSMethod,"kdtree") && ! ( strcmpi (Distance, "euclidean") || strcmpi (Distance, "cityblock") || strcmpi (Distance, "minkowski") || strcmpi (Distance, "chebychev"))) error (strcat (["rangesearch: 'kdtree' cannot be used with"], ... [" the given distance metric."])); endif endif ## Check for NSMethod if (strcmpi (NSMethod, "kdtree")) ## Build kdtree and search the query point ret = buildkdtree (X, BS); ## Return all neighbors as cell dist = cell (rows (Y), 1); idx = cell (rows (Y), 1); k = rows (X); for i = 1:rows (Y) ## Need to fix the kd-tree search to compare with r distance (not k-NN) NN = findkdtree (ret, Y(i, :), k, Distance, DistParameter); D = - ones (k, 1); D(NN) = pdist2 (X(NN,:), Y(i,:), Distance, DistParameter); Didx_row = find (D <= r & D >= 0)'; Dist_row = D(Didx_row)'; if (SI) [S, I] = sort (Dist_row); Dist_row = Dist_row(I); Didx_row = Didx_row(I); endif dist{i} = Dist_row; idx{i} = Didx_row; endfor else ## Calculate all distances dist = cell (rows (Y), 1); idx = cell (rows (Y), 1); for i = 1:rows (Y) D = pdist2 (X, Y(i,:), Distance, DistParameter); Didx_row = find (D <= r)'; Dist_row = D(Didx_row)'; if (SI) [S, I] = sort (Dist_row); Dist_row = Dist_row(I); Didx_row = Didx_row(I); endif dist{i} = Dist_row; idx{i} = Didx_row; endfor endif endfunction ## buildkdtree function ret = buildkdtree_recur (X, r, d, BS) count = length (r); dimen = size (X, 2); if (count == 1) ret = struct ("point", r(1), "dimen", d); else mid = ceil (count / 2); ret = struct ("point", r(mid), "dimen", d); d = mod (d, dimen) + 1; ## Build left sub tree if (mid > 1) left = r(1:mid-1); left_points = X(left,d); [val, left_idx] = sort (left_points); leftr = left(left_idx); ret.left = buildkdtree_recur (X, leftr, d); endif ## Build right sub tree if (count > mid) right = r(mid+1:count); right_points = X(right,d); [val, right_idx] = sort (right_points); rightr = right(right_idx); ret.right = buildkdtree_recur (X, rightr, d); endif endif endfunction ## Need to fix the kd-tree search to compare with r distance (not k-NN) ## wrapper function for buildkdtree_recur function ret = buildkdtree (X, BS) [val, r] = sort (X(:,1)); ret = struct ("data", X, "root", buildkdtree_recur (X, r, 1, BS)); endfunction function farthest = kdtree_cand_farthest (X, p, cand, dist, distparam) D = pdist2 (X, p, dist, distparam); [val, index] = max (D'(cand)); farthest = cand (index); endfunction ## function to insert into NN list function inserted = kdtree_cand_insert (X, p, cand, k, point, dist, distparam) if (length (cand) < k) inserted = [cand; point]; else farthest = kdtree_cand_farthest (X, p, cand, dist, distparam); if (pdist2 (cand(find(cand == farthest),:), point, dist, distparam)) inserted = [cand; point]; else farthest = kdtree_cand_farthest (X, p, cand, dist, distparam); cand (find (cand == farthest)) = point; inserted = cand; endif endif endfunction ## function to search in a kd tree function nn = findkdtree_recur (X, node, p, nn, ... k, dist, distparam) point = node.point; d = node.dimen; if (X(point,d) > p(d)) ## Search in left sub tree if (isfield (node, "left")) nn = findkdtree_recur (X, node.left, p, nn, k, dist, distparam); endif ## Add current point if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); if (length(nn) < k || pdist2 (X(point,:), p, dist, distparam) <= pdist2 (X(farthest,:), p, dist, distparam)) nn = kdtree_cand_insert (X, p, nn, k, point, dist, distparam); endif ## Search in right sub tree if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); radius = pdist2 (X(farthest,:), p, dist, distparam); if (isfield (node, "right") && (length(nn) < k || p(d) + radius > X(point,d))) nn = findkdtree_recur (X, node.right, p, nn, ... k, dist, distparam); endif else ## Search in right sub tree if (isfield (node, "right")) nn = findkdtree_recur (X, node.right, p, nn, k, dist, distparam); endif ## Add current point if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); if (length (nn) < k || pdist2 (X(point,:), p, dist, distparam) <= pdist2 (X(farthest,:), p, dist, distparam)) nn = kdtree_cand_insert (X, p, nn, k, point, dist, distparam); endif ## Search in left sub tree if neccessary farthest = kdtree_cand_farthest (X, p, nn, dist, distparam); radius = pdist2 (X(farthest,:), p, dist, distparam); if (isfield (node, "left") && (length (nn) < k || p(d) - radius <= X(point,d))) nn = findkdtree_recur (X, node.left, p, nn, k, dist, distparam); endif endif endfunction ## wrapper function for findkdtree_recur function nn = findkdtree (tree, p, k, dist, distparam) X = tree.data; root = tree.root; nn = findkdtree_recur (X, root, p, [], k, dist, distparam); endfunction %!demo %! ## Generate 1000 random 2D points from each of five distinct multivariate %! ## normal distributions that form five separate classes %! N = 1000; %! d = 10; %! randn ("seed", 5); %! X1 = mvnrnd (d * [0, 0], eye (2), 1000); %! randn ("seed", 6); %! X2 = mvnrnd (d * [1, 1], eye (2), 1000); %! randn ("seed", 7); %! X3 = mvnrnd (d * [-1, -1], eye (2), 1000); %! randn ("seed", 8); %! X4 = mvnrnd (d * [1, -1], eye (2), 1000); %! randn ("seed", 8); %! X5 = mvnrnd (d * [-1, 1], eye (2), 1000); %! X = [X1; X2; X3; X4; X5]; %! %! ## For each point in X, find the points in X that are within a radius d %! ## away from the points in X. %! Idx = rangesearch (X, X, d, "NSMethod", "exhaustive"); %! %! ## Select the first point in X (corresponding to the first class) and find %! ## its nearest neighbors within the radius d. Display these points in %! ## one color and the remaining points in a different color. %! x = X(1,:); %! nearestPoints = X (Idx{1},:); %! nonNearestIdx = true (size (X, 1), 1); %! nonNearestIdx(Idx{1}) = false; %! %! scatter (X(nonNearestIdx,1), X(nonNearestIdx,2)) %! hold on %! scatter (nearestPoints(:,1),nearestPoints(:,2)) %! scatter (x(1), x(2), "black", "filled") %! hold off %! %! ## Select the last point in X (corresponding to the fifth class) and find %! ## its nearest neighbors within the radius d. Display these points in %! ## one color and the remaining points in a different color. %! x = X(end,:); %! nearestPoints = X (Idx{1},:); %! nonNearestIdx = true (size (X, 1), 1); %! nonNearestIdx(Idx{1}) = false; %! %! figure %! scatter (X(nonNearestIdx,1), X(nonNearestIdx,2)) %! hold on %! scatter (nearestPoints(:,1),nearestPoints(:,2)) %! scatter (x(1), x(2), "black", "filled") %! hold off ## Test output %!shared x, y, X, Y %! x = [1, 2, 3; 4, 5, 6; 7, 8, 9; 3, 2, 1]; %! y = [2, 3, 4; 1, 4, 3]; %! X = [1, 2, 3, 4; 2, 3, 4, 5; 3, 4, 5, 6]; %! Y = [1, 2, 2, 3; 2, 3, 3, 4]; %!test %! [idx, D] = rangesearch (x, y, 4); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[1.7321, 3.3166, 3.4641]; [2, 3.4641]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 4, "NSMethod", "exhaustive"); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[1.7321, 3.3166, 3.4641]; [2, 3.4641]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 4, "NSMethod", "kdtree"); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[1.7321, 3.3166, 3.4641]; [2, 3.4641]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 4, "SortIndices", true); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[1.7321, 3.3166, 3.4641]; [2, 3.4641]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 4, "SortIndices", false); %! assert (idx, {[1, 2, 4]; [1, 4]}); %! assert (D, {[1.7321, 3.4641, 3.3166]; [2, 3.4641]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 4, "NSMethod", "exhaustive", ... %! "SortIndices", false); %! assert (idx, {[1, 2, 4]; [1, 4]}); %! assert (D, {[1.7321, 3.4641, 3.3166]; [2, 3.4641]}, 1e-4); %!test %! eucldist = @(v,m) sqrt(sumsq(repmat(v,rows(m),1)-m,2)); %! [idx, D] = rangesearch (x, y, 4, "Distance", eucldist); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[1.7321, 3.3166, 3.4641]; [2, 3.4641]}, 1e-4); %!test %! eucldist = @(v,m) sqrt(sumsq(repmat(v,rows(m),1)-m,2)); %! [idx, D] = rangesearch (x, y, 4, "Distance", eucldist, ... %! "NSMethod", "exhaustive"); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[1.7321, 3.3166, 3.4641]; [2, 3.4641]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 1.5, "Distance", "seuclidean", ... %! "NSMethod", "exhaustive"); %! assert (idx, {[1, 4, 2]; [1, 4]}); %! assert (D, {[0.6024, 1.0079, 1.2047]; [0.6963, 1.2047]}, 1e-4); %!test %! [idx, D] = rangesearch (x, y, 1.5, "Distance", "seuclidean", ... %! "NSMethod", "exhaustive", "SortIndices", false); %! assert (idx, {[1, 2, 4]; [1, 4]}); %! assert (D, {[0.6024, 1.2047, 1.0079]; [0.6963, 1.2047]}, 1e-4); %!test %! [idx, D] = rangesearch (X, Y, 4); %! assert (idx, {[1, 2]; [1, 2, 3]}); %! assert (D, {[1.4142, 3.1623]; [1.4142, 1.4142, 3.1623]}, 1e-4); %!test %! [idx, D] = rangesearch (X, Y, 2); %! assert (idx, {[1]; [1, 2]}); %! assert (D, {[1.4142]; [1.4142, 1.4142]}, 1e-4); %!test %! eucldist = @(v,m) sqrt(sumsq(repmat(v,rows(m),1)-m,2)); %! [idx, D] = rangesearch (X, Y, 4, "Distance", eucldist); %! assert (idx, {[1, 2]; [1, 2, 3]}); %! assert (D, {[1.4142, 3.1623]; [1.4142, 1.4142, 3.1623]}, 1e-4); %!test %! [idx, D] = rangesearch (X, Y, 4, "SortIndices", false); %! assert (idx, {[1, 2]; [1, 2, 3]}); %! assert (D, {[1.4142, 3.1623]; [1.4142, 1.4142, 3.1623]}, 1e-4); %!test %! [idx, D] = rangesearch (X, Y, 4, "Distance", "seuclidean", ... %! "NSMethod", "exhaustive"); %! assert (idx, {[1, 2]; [1, 2, 3]}); %! assert (D, {[1.4142, 3.1623]; [1.4142, 1.4142, 3.1623]}, 1e-4); ## Test input validation %!error rangesearch (1) %!error ... %! rangesearch (ones (4, 5), ones (4)) %!error ... %! rangesearch (ones (4, 2), ones (3, 2), 1, "Distance", "euclidean", "some", "some") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "scale", ones (1, 5), "P", 3) %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "P",-2) %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "scale", ones(4,5), "distance", "euclidean") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "cov", ["some" "some"]) %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "cov", ones(4,5), "distance", "euclidean") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "bucketsize", -1) %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "cosine") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "mahalanobis") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "correlation") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "seuclidean") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "spearman") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "hamming") %!error ... %! rangesearch (ones (4, 5), ones (1, 5), 1, "NSmethod", "kdtree", "distance", "jaccard") statistics-release-1.7.3/inst/ranksum.m000066400000000000000000000255501475240274700201700ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{p} =} ranksum (@var{x}, @var{y}) ## @deftypefnx {statistics} {@var{p} =} ranksum (@var{x}, @var{y}, @var{alpha}) ## @deftypefnx {statistics} {@var{p} =} ranksum (@var{x}, @var{y}, @var{alpha}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {@var{p} =} ranksum (@var{x}, @var{y}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{p}, @var{h}] =} ranksum (@var{x}, @var{y}, @dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{h}, @var{stats}] =} ranksum (@var{x}, @var{y}, @dots{}) ## ## Wilcoxon rank sum test for equal medians. This test is equivalent to a ## Mann-Whitney U-test. ## ## @code{@var{p} = ranksum (@var{x}, @var{y})} returns the p-value of a ## two-sided Wilcoxon rank sum test. It tests the null hypothesis that two ## independent samples, in the vectors X and Y, come from continuous ## distributions with equal medians, against the alternative hypothesis that ## they are not. @var{x} and @var{y} can have different lengths and the test ## assumes that they are independent. ## ## @code{ranksum} treats NaN in @var{x}, @var{y} as missing values. ## The two-sided p-value is computed by doubling the most significant one-sided ## value. ## ## @code{[@var{p}, @var{h}] = ranksum (@var{x}, @var{y})} also returns the ## result of the hypothesis test with @code{@var{h} = 1} indicating a rejection ## of the null hypothesis at the default alpha = 0.05 significance level, and ## @code{@var{h} = 0} indicating a failure to reject the null hypothesis at the ## same significance level. ## ## @code{[@var{p}, @var{h}, @var{stats}] = ranksum (@var{x}, @var{y})} also ## returns the structure @var{stats} with information about the test statistic. ## It contains the field @code{ranksum} with the value of the rank sum test ## statistic and if computed with the "approximate" method it also contains the ## value of the z-statistic in the field @code{zval}. ## ## @code{[@dots{}] = ranksum (@var{x}, @var{y}, @var{alpha})} or alternatively ## @code{[@dots{}] = ranksum (@var{x}, @var{y}, "alpha", @var{alpha})} returns ## the result of the hypothesis test performed at the significance level ALPHA. ## ## @code{[@dots{}] = ranksum (@var{x}, @var{y}, "method", @var{M})} defines the ## computation method of the p-value specified in @var{M}, which can be "exact", ## "approximate", or "oldexact". @var{M} must be a single string. When "method" ## is unspecified, the default is: "exact" when ## @code{min (length (@var{x}), length (@var{y})) < 10} and ## @code{length (@var{x}) + length (@var{y}) < 10}, otherwise the "approximate" ## method is used. ## ## @itemize ## @item ## "exact" method uses full enumeration for small total sample size (< 10), ## otherwise the network algorithm is used for larger samples. ## @item ## "approximate" uses normal approximation method for computing the p-value. ## @item ## "oldexact" uses full enumeration for any sample size. Note, that this option ## can lead to out of memory error for large samples. Use with caution! ## @end itemize ## ## @code{[@dots{}] = ranksum (@var{x}, @var{y}, "tail", @var{tail})} defines the ## type of test, which can be "both", "right", or "left". @var{tail} must be a ## single string. ## ## @itemize ## @item ## "both" -- "medians are not equal" (two-tailed test, default) ## @item ## "right" -- "median of X is greater than median of Y" (right-tailed test) ## @item ## "left" -- "median of X is less than median of Y" (left-tailed test) ## @end itemize ## ## Note: the rank sum statistic is based on the smaller sample of vectors ## @var{x} and @var{y}. ## ## @end deftypefn function [p, h, stats] = ranksum(x, y, varargin) ## Check that x and y are vectors if ! isvector (x) || ! isvector (y) error ("X and Y must be vectors"); endif ## Remove missing data and make column vectors x = x(! isnan (x))(:); y = y(! isnan (y))(:); if isempty (x) error ("Not enough data in X"); endif if isempty (y) error ("Not enough data in Y"); endif ## Check for extra input arguments alpha = 0.05; method = []; tail = "both"; ## Old syntax: ranksum (x, y, alpha) if nargin > 2 && isnumeric (varargin{1}) && isscalar (varargin{1}) alpha = varargin{1}; varargin(1) = []; if isnan (alpha) || alpha <= 0 || alpha >= 1 error ("Alpha does not have a valid value"); endif end ## Check for Name:Value pairs arg_pairs = length (varargin); if ! (int16 (arg_pairs / 2) == arg_pairs / 2) error ("Extra arguments are not in Name:Value pairs"); endif num_pair = 1; while (arg_pairs) name = varargin{num_pair}; value = varargin{num_pair + 1}; switch (lower (name)) case "alpha" alpha = value; if (isnan (alpha) || alpha <= 0 || alpha >= 1 || ! isnumeric (alpha) ... || ! isscalar (alpha)) error ("Alpha does not have a valid value"); endif case "method" method = value; if ! any (strcmpi (method, {"exact", "approximate", "oldexact"})) error ("Wrong value for method option"); endif case "tail" tail = value; if ! any (strcmpi (tail, {"both", "right", "left"})) error ("Wrong value for tail option"); endif endswitch arg_pairs -= 2; num_pair += 2; endwhile ## Determine method nx = length (x); ny = length (y); ns = min (nx, ny); if isempty (method) if (ns < 10) && ((nx + ny) < 20) method = "exact"; else method = "approximate"; endif endif % Determine computational technique switch method case "approximate" technique = "approximation"; case "oldexact" technique = "exact"; case "exact" if (nx + ny) < 10 technique = "exact"; else technique = "network_algorithm"; endif endswitch % Compute the rank sum statistic based on the smaller sample if nx <= ny [ranks, tieadj] = tiedrank ([x; y]); x_y = true; else [ranks, tieadj] = tiedrank ([y; x]); x_y = false; endif srank = ranks(1:ns); ranksumstat = sum (srank); ## Calculate p-value according to selected technique switch technique case "exact" allpos = nchoosek (ranks, ns); sumranks = sum (allpos, 2); np = size (sumranks, 1); switch tail case "both" p_low = sum (sumranks <= ranksumstat) / np; p_high = sum (sumranks >= ranksumstat) / np; p = 2 * min (p_low, p_high); if p > 1 p = 1; endif case "right" if x_y p = sum (sumranks >= ranksumstat) / np; else p = sum (sumranks <= ranksumstat) / np; endif case "left" if x_y p = sum (sumranks <= ranksumstat) / np; else p = sum (sumranks >= ranksumstat) / np; endif endswitch case "network_algorithm" ## Calculate contingency table u = unique ([x; y]); ct = zeros (2, length (u)); if x_y ct(1,:) = histc (x,u)'; ct(2,:) = histc (y,u)'; else ct(1,:) = histc (y,u)'; ct(2,:) = histc (x,u)'; endif ## Calculate weights for wmw test colsum = sum (ct,1); tmp = cumsum (colsum); weights = [0 tmp(1:end - 1)] + .5 * (1 + diff ([0 tmp])); ## Compute p-value using network algorithm for contingency tables [p_net, p_val] = exact2xkCT (ct, weights, ranksumstat); ## Check if p = NaN if any (isnan (p_net)) || any (isnan (p_val)) p = NaN; else switch tail case "both" p = 2 * p_net; if p > 1 p = 1; endif case "right" if x_y p = p_val(2) + p_val(3); else p = p_val(2) + p_val(1); endif case "left" if x_y p = p_val(2) + p_val(1); else p = p_val(2) + p_val(3); endif endswitch endif case "approximation" wmean = ns * (nx + ny + 1) / 2; tiescores = 2 * tieadj / ((nx + ny) * (nx + ny - 1)); wvar = nx * ny * ((nx + ny + 1) - tiescores) / 12; wc = ranksumstat - wmean; ## compute z-value, including continuity correction switch tail case "both" z = (wc - 0.5 * sign (wc)) / sqrt (wvar); if ! x_y z = -z; endif p = 2 * normcdf (-abs(z)); case "right" if x_y z = (wc - 0.5) / sqrt (wvar); else z = -(wc + 0.5) / sqrt (wvar); endif p = normcdf (-z); case "left" if x_y z = (wc + 0.5) / sqrt (wvar); else z = -(wc - 0.5) / sqrt (wvar); endif p = normcdf (z); endswitch ## For additional output argument if (nargout > 2) stats.zval = z; endif endswitch ## For additional output arguments if nargout > 1, h = (p <= alpha); if (nargout > 2) if x_y stats.ranksum = ranksumstat; else stats.ranksum = sum (ranks(ns+1:end)); endif endif endif endfunction ## testing against mileage data and results from Matlab %!test %! mileage = [33.3, 34.5, 37.4; 33.4, 34.8, 36.8; ... %! 32.9, 33.8, 37.6; 32.6, 33.4, 36.6; ... %! 32.5, 33.7, 37.0; 33.0, 33.9, 36.7]; %! [p,h,stats] = ranksum(mileage(:,1),mileage(:,2)); %! assert (p, 0.004329004329004329, 1e-14); %! assert (h, true); %! assert (stats.ranksum, 21.5); %!test %! year1 = [51 52 62 62 52 52 51 53 59 63 59 56 63 74 68 86 82 70 69 75 73 ... %! 49 47 50 60 59 60 62 61 71]'; %! year2 = [54 53 64 66 57 53 54 54 62 66 59 59 67 76 75 86 82 67 74 80 75 ... %! 54 50 53 62 62 62 72 60 67]'; %! [p,h,stats] = ranksum(year1, year2, "alpha", 0.01, "tail", "left"); %! assert (p, 0.1270832752950605, 1e-14); %! assert (h, false); %! assert (stats.ranksum, 837.5); %! assert (stats.zval, -1.140287483634606, 1e-14); %! [p,h,stats] = ranksum(year1, year2, "alpha", 0.01, "tail", "left", ... %! "method", "exact"); %! assert (p, 0.127343916432862, 1e-14); %! assert (h, false); %! assert (stats.ranksum, 837.5); statistics-release-1.7.3/inst/regress.m000066400000000000000000000147761475240274700201720ustar00rootroot00000000000000## Copyright (C) 2005, 2006 William Poetra Yoga Hadisoeseno ## Copyright (C) 2011 Nir Krakauer ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{b}, @var{bint}, @var{r}, @var{rint}, @var{stats}] =} regress (@var{y}, @var{X}, [@var{alpha}]) ## ## Multiple Linear Regression using Least Squares Fit of @var{y} on @var{X} ## with the model @code{y = X * beta + e}. ## ## Here, ## ## @itemize ## @item ## @code{y} is a column vector of observed values ## @item ## @code{X} is a matrix of regressors, with the first column filled with ## the constant value 1 ## @item ## @code{beta} is a column vector of regression parameters ## @item ## @code{e} is a column vector of random errors ## @end itemize ## ## Arguments are ## ## @itemize ## @item ## @var{y} is the @code{y} in the model ## @item ## @var{X} is the @code{X} in the model ## @item ## @var{alpha} is the significance level used to calculate the confidence ## intervals @var{bint} and @var{rint} (see `Return values' below). If not ## specified, ALPHA defaults to 0.05 ## @end itemize ## ## Return values are ## ## @itemize ## @item ## @var{b} is the @code{beta} in the model ## @item ## @var{bint} is the confidence interval for @var{b} ## @item ## @var{r} is a column vector of residuals ## @item ## @var{rint} is the confidence interval for @var{r} ## @item ## @var{stats} is a row vector containing: ## ## @itemize ## @item The R^2 statistic ## @item The F statistic ## @item The p value for the full model ## @item The estimated error variance ## @end itemize ## @end itemize ## ## @var{r} and @var{rint} can be passed to @code{rcoplot} to visualize ## the residual intervals and identify outliers. ## ## NaN values in @var{y} and @var{X} are removed before calculation begins. ## ## @seealso{regress_gp, regression_ftest, regression_ttest} ## @end deftypefn function [b, bint, r, rint, stats] = regress (y, X, alpha) if (nargin < 2 || nargin > 3) print_usage; endif if (! ismatrix (y)) error ("regress: y must be a numeric matrix"); endif if (! ismatrix (X)) error ("regress: X must be a numeric matrix"); endif if (columns (y) != 1) error ("regress: y must be a column vector"); endif if (rows (y) != rows (X)) error ("regress: y and X must contain the same number of rows"); endif if (nargin < 3) alpha = 0.05; elseif (! isscalar (alpha)) error ("regress: alpha must be a scalar value") endif notnans = ! logical (sum (isnan ([y X]), 2)); y = y(notnans); X = X(notnans,:); [Xq Xr] = qr (X, 0); pinv_X = Xr \ Xq'; b = pinv_X * y; if (nargout > 1) n = rows (X); p = columns (X); dof = n - p; t_alpha_2 = tinv (alpha / 2, dof); r = y - X * b; # added -- Nir SSE = sum (r .^ 2); v = SSE / dof; # c = diag(inv (X' * X)) using (economy) QR decomposition # which means that we only have to use Xr c = diag (inv (Xr' * Xr)); db = t_alpha_2 * sqrt (v * c); bint = [b + db, b - db]; endif if (nargout > 3) dof1 = n - p - 1; h = sum(X.*pinv_X', 2); #added -- Nir (same as diag(X*pinv_X), without doing the matrix multiply) # From Matlab's documentation on Multiple Linear Regression, # sigmaihat2 = norm (r) ^ 2 / dof1 - r .^ 2 / (dof1 * (1 - h)); # dr = -tinv (1 - alpha / 2, dof) * sqrt (sigmaihat2 .* (1 - h)); # Substitute # norm (r) ^ 2 == sum (r .^ 2) == SSE # -tinv (1 - alpha / 2, dof) == tinv (alpha / 2, dof) == t_alpha_2 # We get # sigmaihat2 = (SSE - r .^ 2 / (1 - h)) / dof1; # dr = t_alpha_2 * sqrt (sigmaihat2 .* (1 - h)); # Combine, we get # dr = t_alpha_2 * sqrt ((SSE * (1 - h) - (r .^ 2)) / dof1); dr = t_alpha_2 * sqrt ((SSE * (1 - h) - (r .^ 2)) / dof1); rint = [r + dr, r - dr]; endif if (nargout > 4) R2 = 1 - SSE / sum ((y - mean (y)) .^ 2); # F = (R2 / (p - 1)) / ((1 - R2) / dof); F = dof / (p - 1) / (1 / R2 - 1); pval = 1 - fcdf (F, p - 1, dof); stats = [R2 F pval v]; endif endfunction %!test %! % Longley data from the NIST Statistical Reference Dataset %! Z = [ 60323 83.0 234289 2356 1590 107608 1947 %! 61122 88.5 259426 2325 1456 108632 1948 %! 60171 88.2 258054 3682 1616 109773 1949 %! 61187 89.5 284599 3351 1650 110929 1950 %! 63221 96.2 328975 2099 3099 112075 1951 %! 63639 98.1 346999 1932 3594 113270 1952 %! 64989 99.0 365385 1870 3547 115094 1953 %! 63761 100.0 363112 3578 3350 116219 1954 %! 66019 101.2 397469 2904 3048 117388 1955 %! 67857 104.6 419180 2822 2857 118734 1956 %! 68169 108.4 442769 2936 2798 120445 1957 %! 66513 110.8 444546 4681 2637 121950 1958 %! 68655 112.6 482704 3813 2552 123366 1959 %! 69564 114.2 502601 3931 2514 125368 1960 %! 69331 115.7 518173 4806 2572 127852 1961 %! 70551 116.9 554894 4007 2827 130081 1962 ]; %! % Results certified by NIST using 500 digit arithmetic %! % b and standard error in b %! V = [ -3482258.63459582 890420.383607373 %! 15.0618722713733 84.9149257747669 %! -0.358191792925910E-01 0.334910077722432E-01 %! -2.02022980381683 0.488399681651699 %! -1.03322686717359 0.214274163161675 %! -0.511041056535807E-01 0.226073200069370 %! 1829.15146461355 455.478499142212 ]; %! Rsq = 0.995479004577296; %! F = 330.285339234588; %! y = Z(:,1); X = [ones(rows(Z),1), Z(:,2:end)]; %! alpha = 0.05; %! [b, bint, r, rint, stats] = regress (y, X, alpha); %! assert(b,V(:,1),4e-6); %! assert(stats(1),Rsq,1e-12); %! assert(stats(2),F,3e-8); %! assert(((bint(:,1)-bint(:,2))/2)/tinv(alpha/2,9),V(:,2),-1.e-5); statistics-release-1.7.3/inst/regress_gp.m000066400000000000000000000474421475240274700206540ustar00rootroot00000000000000## Copyright (c) 2012 Juan Pablo Carbajal ## Copyright (C) 2023-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{Yfit}, @var{Yint}, @var{m}, @var{K}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}) ## @deftypefnx {statistics} {[@var{Yfit}, @var{Yint}, @var{m}, @var{K}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @qcode{"linear"}) ## @deftypefnx {statistics} {[@var{Yfit}, @var{Yint}, @var{Ysd}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @qcode{"rbf"}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @qcode{"linear"}, @var{Sp}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @var{Sp}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @qcode{"rbf"}, @var{theta}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @qcode{"rbf"}, @var{theta}, @var{g}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @qcode{"rbf"}, @var{theta}, @var{g}, @var{alpha}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @var{theta}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @var{theta}, @var{g}) ## @deftypefnx {statistics} {[@dots{}] =} regress_gp (@var{X}, @var{Y}, @var{Xfit}, @var{theta}, @var{g}, @var{alpha}) ## ## Regression using Gaussian Processes. ## ## @code{[@var{Yfit}, @var{Yint}, @var{m}, @var{K}] = regress_gp (@var{X}, ## @var{Y}, @var{Xfit})} will estimate a linear Gaussian Process model @var{m} ## in the form @qcode{@var{Y} = @var{X}' * @var{m}}, where @var{X} is an ## @math{NxP} matrix with @math{N} observations in @math{P} dimensional space ## and @var{Y} is an @math{Nx1} column vector as the dependent variable. The ## information about errors of the predictions (interpolation/extrapolation) is ## given by the covarianve matrix @var{K}. ## By default, the linear model defines the prior covariance of @var{m} as ## @code{@var{Sp} = 100 * eye (size (@var{X}, 2) + 1)}. A custom prior ## covariance matrix can be passed as @var{Sp}, which must be a @math{P+1xP+1} ## positive definite matrix. The model is evaluated for input @var{Xfit}, which ## must have the same columns as @var{X}, and the estimates are returned in ## @var{Yfit} along with the estimated variation in @var{Yint}. ## @qcode{@var{Yint}(:,1)} contains the upper boundary estimate and ## @qcode{@var{Yint}(:,1)} contains the upper boundary estimate with respect to ## @var{Yfit}. ## ## @code{[@var{Yfit}, @var{Yint}, @var{Ysd}, @var{K}] = regress_gp (@var{X}, ## @var{Y}, @var{Xfit}, @qcode{"rbf"})} will estimate a Gaussian Process model ## with a Radial Basis Function (RBF) kernel with default parameters ## @qcode{@var{theta} = 5}, which corresponds to the characteristic lengthscale, ## and @qcode{@var{g} = 0.01}, which corresponds to the nugget effect, and ## @qcode{@var{alpha} = 0.05} which defines the confidence level for the ## estimated intervals returned in @var{Yint}. The function also returns the ## predictive covariance matrix in @var{Ysd}. For multidimensional predictors ## @var{X} the function will automatically normalize each column to a zero mean ## and a standard deviation to one. ## ## Run @code{demo regress_gp} to see examples. ## ## @seealso{regress, regression_ftest, regression_ttest} ## @end deftypefn function [Yfit, Yint, varargout] = regress_gp (X, Y, Xfit, varargin) ## Check input arguments if (nargin < 3) print_usage; endif if (ndims (X) != 2) error ("regress_gp: X must be a 2-D matrix."); endif if (! isvector (Y) || size (Y, 2) != 1) error ("regress_gp: Y must be a column vector."); endif if (size (X, 1) != length (Y)) error ("regress_gp: rows in X must equal the length of Y."); endif if (size (X, 2) != size (Xfit, 2)) error ("regress_gp: X and XI must have the same number of columns."); endif ## Add defauts kernel = "linear"; Sp = 100 * eye (size (X, 2) + 1); theta = 5; g = 0.01; alpha = 0.05; ## Parse extra arguments if (nargin > 3) tmp = varargin{1}; if (ischar (tmp) && strcmpi (tmp, "linear")) kernel = "linear"; sinput = true; elseif (ischar (tmp) && strcmpi (tmp, "rbf")) kernel = "rbf"; sinput = true; elseif (isnumeric (tmp) && ! isscalar (tmp)) kernel = "linear"; sinput = false; Sp = tmp; elseif (isnumeric (tmp) && isscalar (tmp)) kernel = "rbf"; sinput = false; theta = tmp; else error ("regress_gp: invalid 4th argument."); endif endif if (nargin > 4) tmp = varargin{2}; if (sinput) if (isnumeric (tmp) && ! isscalar (tmp)) if (strcmpi (kernel, "rbf")) error ("regress_gp: theta must be a scalar when using RBF kernel."); endif Sp = tmp; if (! isequal (size (Sp), (size (X, 2) + 1) * [1, 1])) error ("regress_gp: wrong size for prior covariance matrix Sp."); endif elseif (isnumeric (tmp) && isscalar (tmp)) if (strcmpi (kernel, "linear")) error ("regress_gp: wrong size for prior covariance matrix Sp."); endif theta = tmp; else error ("regress_gp: invalid 5th argument."); endif else if (strcmpi (kernel, "linear")) error ("regress_gp: invalid 5th argument."); endif g = tmp; endif endif if (nargin > 5) tmp = varargin{3}; if (isnumeric (tmp) && isscalar (tmp) && sinput) g = tmp; elseif (isnumeric (tmp) && isscalar (tmp) && ! sinput) alpha = tmp; else error ("regress_gp: invalid 6th argument."); endif endif if (nargin > 6) tmp = varargin{4}; if (isnumeric (tmp) && isscalar (tmp) && sinput) alpha = tmp; else error ("regress_gp: invalid 7th argument."); endif endif ## User linear kernel if (strcmpi (kernel, "linear")) ## Add constant vector X = [ones(1,size(X,1)); X']; ## Juan Pablo Carbajal ## Note that in the book the equation (below 2.11) for the A reads ## A = (1/sy^2)*X*X' + inv (Vp); ## where sy is the scalar variance of the of the residuals (i.e Y = X' * w + epsilon) ## and epsilon is drawn from N(0,sy^2). Vp is the variance of the parameters w. ## Note that ## (sy^2 * A)^{-1} = (1/sy^2)*A^{-1} = (X*X' + sy^2 * inv(Vp))^{-1}; ## and that the formula for the w mean is ## (1/sy^2)*A^{-1}*X*Y ## Then one obtains ## inv(X*X' + sy^2 * inv(Vp))*X*Y ## Looking at the formula bloew we see that Sp = (1/sy^2)*Vp ## making the regression depend on only one parameter, Sp, and not two. ## Xsq = sum (X' .^ 2); ## [n, d] = size (X); ## sigma = 1/sqrt(2); ## Ks = exp (-(Xsq' * ones (1, n) -ones (n, 1) * Xsq + 2 * X * X') / (2 * sigma ^ 2)); A = X * X' + inv (Sp); K = inv (A); wm = K * X * Y; ## Add constant vector Xfit = [ones(size(Xfit,1),1), Xfit]; ## Compute predictions Yfit = Xfit*wm; Ysd = Xfit * K * Xfit'; dy = diag (Ysd); Yint = [Yfit+dy, Yfit-dy]; if (nargout > 2) varargout{1} = wm; endif if (nargout > 3) varargout{2} = K; endif endif ## User RBF kernel if (strcmpi (kernel, "rbf")) ## Normalize predictors if (size (X, 2) > 1) [X, MU, SIGMA] = zscore (X); Xfit = (Xfit - MU) ./SIGMA; endif ## Get number of training samples n = size (X, 1); ## Calculate squared distance matrix of training input D = squareform (pdist (X) .^2); ## Compute kernel covariance for training quantities S = exp (-D / theta) + g * eye (n); ## Compute kernel covariance for testing quantities Dxi = squareform (pdist (Xfit) .^ 2); Sxi = exp (-Dxi / theta) + g * eye (size (Dxi, 1)); ## Compute kernel covariance for prediction Dx = pdist2 (Xfit, X) .^ 2; Sx = exp (-Dx / theta); ## Caculate predictive covariance K = inv (S); ## Calculate response output Yfit = Sx * K * Y; ## Estimate scale parameter for predictive variance scale = (Y' * K * Y) / size (Y, 1); ## Calculate standard deviation of the response output Ysd = scale * (Sxi - Sx * K * Sx'); ysd1 = sqrt (diag (Ysd)); ## Calculate prediction intervals Yint = norminv (alpha, 0, ysd1); Yint = [Yfit+Yint, Yfit-Yint]; if (nargout > 2) varargout{1} = Ysd; endif endif endfunction %!demo %! ## Linear fitting of 1D Data %! rand ("seed", 125); %! X = 2 * rand (5, 1) - 1; %! randn ("seed", 25); %! Y = 2 * X - 1 + 0.3 * randn (5, 1); %! %! ## Points for interpolation/extrapolation %! Xfit = linspace (-2, 2, 10)'; %! %! ## Fit regression model %! [Yfit, Yint, m] = regress_gp (X, Y, Xfit); %! %! ## Plot fitted data %! plot (X, Y, "xk", Xfit, Yfit, "r-", Xfit, Yint, "b-"); %! title ("Gaussian process regression with linear kernel"); %!demo %! ## Linear fitting of 2D Data %! rand ("seed", 135); %! X = 2 * rand (4, 2) - 1; %! randn ("seed", 35); %! Y = 2 * X(:,1) - 3 * X(:,2) - 1 + 1 * randn (4, 1); %! %! ## Mesh for interpolation/extrapolation %! [x1, x2] = meshgrid (linspace (-1, 1, 10)); %! Xfit = [x1(:), x2(:)]; %! %! ## Fit regression model %! [Ypred, Yint, Ysd] = regress_gp (X, Y, Xfit); %! Ypred = reshape (Ypred, 10, 10); %! YintU = reshape (Yint(:,1), 10, 10); %! YintL = reshape (Yint(:,2), 10, 10); %! %! ## Plot fitted data %! plot3 (X(:,1), X(:,2), Y, ".k", "markersize", 16); %! hold on; %! h = mesh (x1, x2, Ypred, zeros (10, 10)); %! set (h, "facecolor", "none", "edgecolor", "yellow"); %! h = mesh (x1, x2, YintU, ones (10, 10)); %! set (h, "facecolor", "none", "edgecolor", "cyan"); %! h = mesh (x1, x2, YintL, ones (10, 10)); %! set (h, "facecolor", "none", "edgecolor", "cyan"); %! hold off %! axis tight %! view (75, 25) %! title ("Gaussian process regression with linear kernel"); %!demo %! ## Projection over basis function with linear kernel %! pp = [2, 2, 0.3, 1]; %! n = 10; %! rand ("seed", 145); %! X = 2 * rand (n, 1) - 1; %! randn ("seed", 45); %! Y = polyval (pp, X) + 0.3 * randn (n, 1); %! %! ## Powers %! px = [sqrt(abs(X)), X, X.^2, X.^3]; %! %! ## Points for interpolation/extrapolation %! Xfit = linspace (-1, 1, 100)'; %! pxi = [sqrt(abs(Xfit)), Xfit, Xfit.^2, Xfit.^3]; %! %! ## Define a prior covariance assuming that the sqrt component is not present %! Sp = 100 * eye (size (px, 2) + 1); %! Sp(2,2) = 1; # We don't believe the sqrt(abs(X)) is present %! %! ## Fit regression model %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi, Sp); %! %! ## Plot fitted data %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("Linear kernel over basis function with prior covariance"); %!demo %! ## Projection over basis function with linear kernel %! pp = [2, 2, 0.3, 1]; %! n = 10; %! rand ("seed", 145); %! X = 2 * rand (n, 1) - 1; %! randn ("seed", 45); %! Y = polyval (pp, X) + 0.3 * randn (n, 1); %! %! ## Powers %! px = [sqrt(abs(X)), X, X.^2, X.^3]; %! %! ## Points for interpolation/extrapolation %! Xfit = linspace (-1, 1, 100)'; %! pxi = [sqrt(abs(Xfit)), Xfit, Xfit.^2, Xfit.^3]; %! %! ## Fit regression model without any assumption on prior covariance %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi); %! %! ## Plot fitted data %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("Linear kernel over basis function without prior covariance"); %!demo %! ## Projection over basis function with rbf kernel %! pp = [2, 2, 0.3, 1]; %! n = 10; %! rand ("seed", 145); %! X = 2 * rand (n, 1) - 1; %! randn ("seed", 45); %! Y = polyval (pp, X) + 0.3 * randn (n, 1); %! %! ## Powers %! px = [sqrt(abs(X)), X, X.^2, X.^3]; %! %! ## Points for interpolation/extrapolation %! Xfit = linspace (-1, 1, 100)'; %! pxi = [sqrt(abs(Xfit)), Xfit, Xfit.^2, Xfit.^3]; %! %! ## Fit regression model with RBF kernel (standard parameters) %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi, "rbf"); %! %! ## Plot fitted data %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("RBF kernel over basis function with standard parameters"); %! text (-0.5, 4, "theta = 5\n g = 0.01"); %!demo %! ## Projection over basis function with rbf kernel %! pp = [2, 2, 0.3, 1]; %! n = 10; %! rand ("seed", 145); %! X = 2 * rand (n, 1) - 1; %! randn ("seed", 45); %! Y = polyval (pp, X) + 0.3 * randn (n, 1); %! %! ## Powers %! px = [sqrt(abs(X)), X, X.^2, X.^3]; %! %! ## Points for interpolation/extrapolation %! Xfit = linspace (-1, 1, 100)'; %! pxi = [sqrt(abs(Xfit)), Xfit, Xfit.^2, Xfit.^3]; %! %! ## Fit regression model with RBF kernel with different parameters %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi, "rbf", 10, 0.01); %! %! ## Plot fitted data %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("GP regression with RBF kernel and non default parameters"); %! text (-0.5, 4, "theta = 10\n g = 0.01"); %! %! ## Fit regression model with RBF kernel with different parameters %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi, "rbf", 50, 0.01); %! %! ## Plot fitted data %! figure %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("GP regression with RBF kernel and non default parameters"); %! text (-0.5, 4, "theta = 50\n g = 0.01"); %! %! ## Fit regression model with RBF kernel with different parameters %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi, "rbf", 50, 0.001); %! %! ## Plot fitted data %! figure %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("GP regression with RBF kernel and non default parameters"); %! text (-0.5, 4, "theta = 50\n g = 0.001"); %! %! ## Fit regression model with RBF kernel with different parameters %! [Yfit, Yint, Ysd] = regress_gp (px, Y, pxi, "rbf", 50, 0.05); %! %! ## Plot fitted data %! figure %! plot (X, Y, "xk;Data;", Xfit, Yfit, "r-;Estimation;", ... %! Xfit, polyval (pp, Xfit), "g-;True;"); %! axis tight %! axis manual %! hold on %! plot (Xfit, Yint(:,1), "m-;Upper bound;", Xfit, Yint(:,2), "b-;Lower bound;"); %! hold off %! title ("GP regression with RBF kernel and non default parameters"); %! text (-0.5, 4, "theta = 50\n g = 0.05"); %!demo %! ## RBF fitting on noiseless 1D Data %! x = [0:2*pi/7:2*pi]'; %! y = 5 * sin (x); %! %! ## Predictive grid of 500 equally spaced locations %! xi = [-0.5:(2*pi+1)/499:2*pi+0.5]'; %! %! ## Fit regression model with RBF kernel %! [Yfit, Yint, Ysd] = regress_gp (x, y, xi, "rbf"); %! %! ## Plot fitted data %! r = mvnrnd (Yfit, diag (Ysd)', 50); %! plot (xi, r', "c-"); %! hold on %! plot (xi, Yfit, "r-;Estimation;", xi, Yint, "b-;Confidence interval;"); %! plot (x, y, ".k;Predictor points;", "markersize", 20) %! plot (xi, 5 * sin (xi), "-y;True Function;"); %! xlim ([-0.5,2*pi+0.5]); %! ylim ([-10,10]); %! hold off %! title ("GP regression with RBF kernel on noiseless 1D data"); %! text (0, -7, "theta = 5\n g = 0.01"); %!demo %! ## RBF fitting on noisy 1D Data %! x = [0:2*pi/7:2*pi]'; %! x = [x; x]; %! y = 5 * sin (x) + randn (size (x)); %! %! ## Predictive grid of 500 equally spaced locations %! xi = [-0.5:(2*pi+1)/499:2*pi+0.5]'; %! %! ## Fit regression model with RBF kernel %! [Yfit, Yint, Ysd] = regress_gp (x, y, xi, "rbf"); %! %! ## Plot fitted data %! r = mvnrnd (Yfit, diag (Ysd)', 50); %! plot (xi, r', "c-"); %! hold on %! plot (xi, Yfit, "r-;Estimation;", xi, Yint, "b-;Confidence interval;"); %! plot (x, y, ".k;Predictor points;", "markersize", 20) %! plot (xi, 5 * sin (xi), "-y;True Function;"); %! xlim ([-0.5,2*pi+0.5]); %! ylim ([-10,10]); %! hold off %! title ("GP regression with RBF kernel on noisy 1D data"); %! text (0, -7, "theta = 5\n g = 0.01"); ## Test input validation %!error regress_gp (ones (20, 2)) %!error regress_gp (ones (20, 2), ones (20, 1)) %!error ... %! regress_gp (ones (20, 2, 3), ones (20, 1), ones (20, 2)) %!error ... %! regress_gp (ones (20, 2), ones (20, 2), ones (20, 2)) %!error ... %! regress_gp (ones (20, 2), ones (15, 1), ones (20, 2)) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (20, 3)) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), {[3]}) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "kernel") %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", ones (4)) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "linear", 1) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", "value") %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", {5}) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), ones (3), 5) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "linear", 5) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", 5, {5}) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", 5, ones (2)) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), 5, 0.01, [1, 1]) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), 5, 0.01, "f") %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), 5, 0.01, "f") %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", 5, 0.01, "f") %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "rbf", 5, 0.01, [1, 1]) %!error ... %! regress_gp (ones (20, 2), ones (20, 1), ones (10, 2), "linear", 1) statistics-release-1.7.3/inst/regression_ftest.m000066400000000000000000000214331475240274700220710ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{stats}] =} regression_ftest (@var{y}, @var{x}, @var{fm}) ## @deftypefnx {statistics} {[@dots{}] =} regression_ftest (@var{y}, @var{x}, @var{fm}, @var{rm}) ## @deftypefnx {statistics} {[@dots{}] =} regression_ftest (@var{y}, @var{x}, @var{fm}, @var{rm}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@dots{}] =} regression_ftest (@var{y}, @var{x}, @var{fm}, [], @var{Name}, @var{Value}) ## ## F-test for General Linear Regression Analysis ## ## Perform a general linear regression F test for the null hypothesis that the ## full model of the form @qcode{y = b_0 + b_1 * x_1 + b_2 * x_2 + @dots{} + ## b_n * x_n + e}, where n is the number of variables in @var{x}, does not ## perform better than a reduced model, such as @qcode{y = b'_0 + b'_1 * x_1 + ## b'_2 * x_2 + @dots{} + b'_k * x_k + e}, where k < n and it corresponds to the ## first k variables in @var{x}. Explanatory (dependent) variable @var{y} and ## response (independent) variables @var{x} must not contain any missing values ## (NaNs). ## ## The full model, @var{fm}, must be a vector of length equal to the columns of ## @var{x}, in which case the constant term b_0 is assumed 0, or equal to ## the columns of @var{x} plus one, in which case the first element is the ## constant b_0. ## ## The reduced model, @var{rm}, must include the constant term and a subset of ## the variables (columns) in @var{x}. If @var{rm} is not given, then a constant ## term b'_0 is assumed equal to the constant term, b_0, of the full model or 0, ## if the full model, @var{fm}, does not have a constant term. @var{rm} must be ## a vector or a scalar if only a constant term is passed into the function. ## ## Name-Value pair arguments can be used to set statistical significance. ## @qcode{"alpha"} can be used to specify the significance level of the test ## (the default value is 0.05). If you want pass optional Name-Value pair ## without a reduced model, make sure that the latter is passed as an empty ## variable. ## ## If @var{h} is 1 the null hypothesis is rejected, meaning that the full model ## explains the variance better than the restricted model. If @var{h} is 0, it ## can be assumed that the full model does NOT explain the variance any better ## than the restricted model. ## ## The p-value (1 minus the CDF of this distribution at @var{f}) is returned ## in @var{pval}. ## ## Under the null, the test statistic @var{f} follows an F distribution with ## 'df1' and 'df2' degrees of freedom, which are returned as fields in the ## @var{stats} structure along with the test's F-statistic, 'fstat' ## ## @seealso{regression_ttest, regress, regress_gp} ## @end deftypefn function [h, pval, stats] = regression_ftest (y, x, fm, rm, varargin) ## Check for valid input if (nargin < 3) print_usage (); endif ## Check for finite real numbers in Y, X if (! all (isfinite (y)) || ! isreal (y)) error ("regression_ftest: Y must contain finite real numbers."); endif if (! all (isfinite (x(:))) || ! isreal (x)) error ("regression_ftest: X must contain finite real numbers."); endif ## Set default arguments alpha = 0.05; ## Check additional options i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; ## Check for valid alpha if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("regression_ftest: invalid value for alpha."); endif otherwise error ("regression_ftest: invalid Name argument."); endswitch i = i + 1; endwhile ## Get size of response (independent) variables [s, v] = size (x); ## Add a constant term of 1s in X x = [ones(s, 1), x]; ## Check the size of explanatory (dependent) variable if (! (isvector (y) && (length (y) == s))) error ("regression_ftest: Y must be a vector of length 'rows (X)'."); endif y = reshape (y, s, 1); ## Check the full model if (! (isvector (fm) && (length (fm) == v || length (fm) == v + 1))) error (strcat (["regression_ftest: full model, FM, must be a vector"], ... [" of length equal to 'rows (X)' or 'rows (X) + 1'."])); endif ## Make it row vector and add a constant = 0 if necessary fm_len = length (fm); fm = reshape (fm, 1, fm_len); if (fm_len == v) fm = [0, fm]; fm_len += 1; endif ## Check the reduced model if (nargin - length (varargin) == 4) if (isempty (rm)) rm = [fm(1), zeros(1, fm_len - 1)]; rm_len = 1; else if (! isvector (rm) || ! isnumeric (rm)) error (strcat (["regression_ftest: reduced model, RM, must be a"], ... [" numeric vector or a scalar."])); endif rm_len = length (rm); if (rm_len >= fm_len - 1) error (strcat (["regression_ftest: reduced model, RM, must have"], ... [" smaller length than the full model, FM."])); endif rm = reshape (rm, 1, rm_len); rm = [rm, zeros(1, fm_len - rm_len)]; endif else rm = [fm(1), zeros(1, fm_len - 1)]; rm_len = 1; endif ## Calculate the fitted response for full and reduced models y_fm = sum (x .* fm, 2); y_rm = sum (x .* rm, 2); ## Calculate Sum of Squares Error for full and reduced models SSE_fm = sumsq (y - y_fm); SSE_rm = sumsq (y - y_rm); ## Calculate the necessary statistics stats.df1 = fm_len - rm_len; stats.df2 = s - v; stats.fstat = ((SSE_rm - SSE_fm) / stats.df1) / (SSE_fm / stats.df2); pval = 1 - fcdf (stats.fstat, stats.df1, stats.df2); ## Determine the test outcome ## MATLAB returns this a double instead of a logical array h = double (pval < alpha); endfunction ## Test input validation %!error regression_ftest (); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]'); %!error ... %! regression_ftest ([1 2 NaN]', [2 3 4; 3 4 5]', [1 0.5]); %!error ... %! regression_ftest ([1 2 Inf]', [2 3 4; 3 4 5]', [1 0.5]); %!error ... %! regression_ftest ([1 2 3+i]', [2 3 4; 3 4 5]', [1 0.5]); %!error ... %! regression_ftest ([1 2 3]', [2 3 NaN; 3 4 5]', [1 0.5]); %!error ... %! regression_ftest ([1 2 3]', [2 3 Inf; 3 4 5]', [1 0.5]); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 3+i]', [1 0.5]); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], [], "alpha", 0); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], [], "alpha", 1.2); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], [], "alpha", [.02 .1]); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], [], "alpha", "a"); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], [], "some", 0.05); %!error ... %! regression_ftest ([1 2 3]', [2 3; 3 4]', [1 0.5]); %!error ... %! regression_ftest ([1 2; 3 4]', [2 3; 3 4]', [1 0.5]); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], ones (2)); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], "alpha"); %!error ... %! regression_ftest ([1 2 3]', [2 3 4; 3 4 5]', [1 0.5], [1 2]); ## Test results statistics-release-1.7.3/inst/regression_ttest.m000066400000000000000000000163031475240274700221070ustar00rootroot00000000000000## Copyright (C) 1995-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} regression_ttest (@var{y}, @var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} regression_ttest (@var{y}, @var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}] =} regression_ttest (@var{y}, @var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} regression_ttest (@var{y}, @var{x}) ## @deftypefnx {statistics} {[@dots{}] =} regression_ttest (@var{y}, @var{x}, @var{Name}, @var{Value}) ## ## Perform a linear regression t-test. ## ## @code{@var{h} = regression_ttest (@var{y}, @var{x})} tests the null ## hypothesis that the slope @math{beta1} of a simple linear regression equals ## 0. The result is @var{h} = 0 if the null hypothesis cannot be rejected at ## the 5% significance level, or @var{h} = 1 if the null hypothesis can be ## rejected at the 5% level. @var{y} and @var{x} must be vectors of equal ## length with finite real numbers. ## ## The p-value of the test is returned in @var{pval}. A @math{100(1-alpha)%} ## confidence interval for @math{beta1} is returned in @var{ci}. @var{stats} is ## a structure containing the value of the test statistic (@qcode{tstat}), ## the degrees of freedom (@qcode{df}), the slope coefficient (@qcode{beta1}), ## and the intercept (@qcode{beta0}). Under the null, the test statistic ## @var{stats}.@qcode{tstat} follows a @math{T}-distribution with ## @var{stats}.@qcode{df} degrees of freedom. ## ## @code{[@dots{}] = regression_ttest (@dots{}, @var{name}, @var{value})} ## specifies one or more of the following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"alpha"} @tab the significance level. Default is 0.05. ## ## @item @tab @qcode{"tail"} @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.1 0.25 0.65 ## @item @tab @qcode{"both"} @tab @math{beta1} is not 0 (two-tailed, default) ## @item @tab @qcode{"left"} @tab @math{beta1} is less than 0 (left-tailed) ## @item @tab @qcode{"right"} @tab @math{beta1} is greater than 0 (right-tailed) ## @end multitable ## ## @seealso{regression_ftest, regress, regress_gp} ## @end deftypefn function [h, pval, ci, stats] = regression_ttest (y, x, varargin) ## Check for valid input if (nargin < 2) print_usage (); endif ## Check for finite real numbers in Y, X if (! all (isfinite (y)) || ! isreal (y)) error ("regression_ttest: Y must contain finite real numbers."); endif if (! all (isfinite (x(:))) || ! isreal (x)) error ("regression_ttest: X must contain finite real numbers."); endif # Get number of observations n = length (y); ## Check Y and X have the same number of observations if (! isvector (y) || ! isvector (x) || length (x) != n) error ("regression_ttest: Y and X must be vectors of equal length."); endif ## Set default arguments alpha = 0.05; tail = "both"; ## Check additional options i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; ## Check for valid alpha if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("regression_ttest: invalid value for alpha."); endif case "tail" i = i + 1; tail = varargin{i}; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("regression_ttest: invalid value for tail."); endif otherwise error ("regression_ttest: invalid Name argument."); endswitch i = i + 1; endwhile y_bar = mean (y); x_bar = mean (x); stats.beta1 = cov (x, y) / var (x); stats.beta0 = y_bar - stats.beta1 * x_bar; y_hat = stats.beta0 + stats.beta1 * x_bar; SSE = sum ((y - y_hat) .^ 2); stats.df = n - 2; SE = sqrt (SSE / stats.df); term = SE / sqrt (sum ((x - x_bar) .^ 2)); stats.tstat = stats.beta1 / term; ## Based on the "tail" argument determine the P-value, the critical values, ## and the confidence interval. switch lower (tail) case "both" pval = 2 * (1 - tcdf (abs (stats.tstat), stats.df)); tcrit = - tinv (alpha / 2, stats.df); ci = [stats.beta1 - tcrit * term; stats.beta1 + tcrit * term]; case "left" pval = tcdf (stats.tstat, stats.df); tcrit = - tinv (alpha, stats.df); ci = [-inf; stats.beta1 + tcrit * term]; case "right" pval = 1 - tcdf (stats.tstat, stats.df); tcrit = - tinv (alpha, stats.df); ci = [stats.beta1 - tcrit * term; inf]; endswitch ## Determine the test outcome h = double (pval < alpha); h(isnan (pval)) = NaN; endfunction ## Test input validation %!error regression_ttest (); %!error regression_ttest (1); %!error ... %! regression_ttest ([1 2 NaN]', [2 3 4]'); %!error ... %! regression_ttest ([1 2 Inf]', [2 3 4]'); %!error ... %! regression_ttest ([1 2 3+i]', [2 3 4]'); %!error ... %! regression_ttest ([1 2 3]', [2 3 NaN]'); %!error ... %! regression_ttest ([1 2 3]', [2 3 Inf]'); %!error ... %! regression_ttest ([1 2 3]', [3 4 3+i]'); %!error ... %! regression_ttest ([1 2 3]', [3 4 4 5]'); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "alpha", 0); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "alpha", 1.2); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "alpha", [.02 .1]); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "alpha", "a"); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "some", 0.05); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "tail", "val"); %!error ... %! regression_ttest ([1 2 3]', [2 3 4]', "alpha", 0.01, "tail", "val"); statistics-release-1.7.3/inst/ridge.m000066400000000000000000000156701475240274700176040ustar00rootroot00000000000000## Copyright (C) 2023 Mohammed Azmat Khan ## ## This file is part of the statistics package for GNU Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, ## see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{b} =} ridge (@var{y}, @var{X}, @var{k}) ## @deftypefnx {statistics} {@var{b} =} ridge (@var{y}, @var{X}, @var{k}, @var{scaled}) ## ## Ridge regression. ## ## @code{@var{b} = ridge (@var{y}, @var{X}, @var{k})} returns the vector of ## coefficient estimates by applying ridge regression from the predictor matrix ## @var{X} to the response vector @var{y}. Each value of @var{b} is the ## coefficient for the respective ridge parameter given @var{k}. By default, ## @var{b} is calculated after centering and scaling the predictors to have a ## zero mean and standard deviation 1. ## ## @code{@var{b} = ridge (@var{y}, @var{X}, @var{k}, @var{scaled})} performs the ## regression with the specified scaling of the coefficient estimates @var{b}. ## When @qcode{@var{scaled} = 0}, the function restores the coefficients to the ## scale of the original data thus is more useful for making predictions. When ## @qcode{@var{scaled} = 1}, the coefficient estimates correspond to the scaled ## centered data. ## ## @itemize ## @item ## @code{y} must be an @math{Nx1} numeric vector with the response data. ## @item ## @code{X} must be an @math{Nxp} numeric matrix with the predictor data. ## @item ## @code{k} must be a numeric vectir with the ridge parameters. ## @item ## @code{scaled} must be a numeric scalar indicating whether the coefficient ## estimates in @var{b} are restored to the scale of the original data. By ## default, @qcode{@var{scaled} = 1}. ## @end itemize ## ## Further information about Ridge regression can be found at ## @url{https://en.wikipedia.org/wiki/Ridge_regression} ## ## @seealso{lasso, stepwisefit, regress} ## @end deftypefn function b = ridge (y, X, k, scaled) ## Check input arguments if (nargin < 3) error ("ridge: function called with too few input arguments."); endif if (! isvector (y) || columns (y) != 1 || isempty (y)) error ("ridge: Y must be a numeric column vector."); endif if (! ismatrix (X) || isempty (X)) error ("ridge: X must be a numeric matrix."); endif if (rows (y) != rows (X)) error ("ridge: Y and X must contain the same number of rows."); endif ## Parse 4th input argument if (nargin < 4 || isempty (scaled)) unscale = false; elseif (scaled == 1) unscale = false; elseif (scaled == 0) unscale = true; else error ("ridge: wrong value for SCALED argument."); endif ## Force y to a column vector y = y(:); ## Rremove any missing values notnans = ! logical (sum (isnan ([y, X]), 2)); y = y(notnans); X = X(notnans,:); ## Scale and center X to zero mean and StD = 1 m = mean (X); stdx = std (X, 0, 1); z = (X - m) ./ stdx; ## Add pseudo observations Z_pseudo = [z; (sqrt(k(1)) .* eye (columns(X)))]; Y_pseudo = [y; zeros(columns(X), 1)]; ## Compute coefficients b = Z_pseudo \ Y_pseudo; nk = numel (k); ## Compute the coefficient estimates for additional ridge parameters. if (nk >= 2) ## Adding a multiple of the identity matrix to the last p rows. ## b is set to 0 for the current ridge parameter value b(end,nk) = 0; for i=2:nk Z_pseudo(end-columns(X)+1:end, :) = sqrt (k(i)) .* eye (columns (X)); b(:,i) = Z_pseudo \ Y_pseudo; endfor endif ## Changing back to the scale if (unscale) b = b ./ repmat (stdx', 1, nk); b = [mean(y)-m*b; b]; endif endfunction %!demo %! ## Perform ridge regression for a range of ridge parameters and observe %! ## how the coefficient estimates change based on the acetylene dataset. %! %! load acetylene %! %! X = [x1, x2, x3]; %! %! x1x2 = x1 .* x2; %! x1x3 = x1 .* x3; %! x2x3 = x2 .* x3; %! %! D = [x1, x2, x3, x1x2, x1x3, x2x3]; %! %! k = 0:1e-5:5e-3; %! %! b = ridge (y, D, k); %! %! figure %! plot (k, b, "LineWidth", 2) %! ylim ([-100, 100]) %! grid on %! xlabel ("Ridge Parameter") %! ylabel ("Standardized Coefficient") %! title ("Ridge Trace") %! legend ("x1", "x2", "x3", "x1x2", "x1x3", "x2x3") %! %!demo %! %! load carbig %! X = [Acceleration Weight Displacement Horsepower]; %! y = MPG; %! %! n = length(y); %! %! rand("seed",1); % For reproducibility %! %! c = cvpartition(n,'HoldOut',0.3); %! idxTrain = training(c,1); %! idxTest = ~idxTrain; %! %! idxTrain = training(c,1); %! idxTest = ~idxTrain; %! %! k = 5; %! b = ridge(y(idxTrain),X(idxTrain,:),k,0); %! %! % Predict MPG values for the test data using the model. %! yhat = b(1) + X(idxTest,:)*b(2:end); %! scatter(y(idxTest),yhat) %! %! hold on %! plot(y(idxTest),y(idxTest),"r") %! xlabel('Actual MPG') %! ylabel('Predicted MPG') %! hold off %! ## Test output %!test %! b = ridge ([1 2 3 4]', [1 2 3 4; 2 3 4 5]', 1); %! assert (b, [0.5533; 0.5533], 1e-4); %!test %! b = ridge ([1 2 3 4]', [1 2 3 4; 2 3 4 5]', 2); %! assert (b, [0.4841; 0.4841], 1e-4); %!test %! load acetylene %! x = [x1, x2, x3]; %! b = ridge (y, x, 0); %! assert (b,[10.2273;1.97128;-0.601818],1e-4); %!test %! load acetylene %! x = [x1, x2, x3]; %! b = ridge (y, x, 0.0005); %! assert (b,[10.2233;1.9712;-0.6056],1e-4); %!test %! load acetylene %! x = [x1, x2, x3]; %! b = ridge (y, x, 0.001); %! assert (b,[10.2194;1.9711;-0.6094],1e-4); %!test %! load acetylene %! x = [x1, x2, x3]; %! b = ridge (y, x, 0.002); %! assert (b,[10.2116;1.9709;-0.6169],1e-4); %!test %! load acetylene %! x = [x1, x2, x3]; %! b = ridge (y, x, 0.005); %! assert (b,[10.1882;1.9704;-0.6393],1e-4); %!test %! load acetylene %! x = [x1, x2, x3]; %! b = ridge (y, x, 0.01); %! assert (b,[10.1497;1.9695;-0.6761],1e-4); ## Test input validation %!error ridge (1) %!error ridge (1, 2) %!error ridge (ones (3), ones (3), 2) %!error ridge ([1, 2], ones (2), 2) %!error ridge ([], ones (3), 2) %!error ridge (ones (5,1), [], 2) %!error ... %! ridge ([1; 2; 3; 4; 5], ones (3), 3) %!error ... %! ridge ([1; 2; 3], ones (3), 3, 2) %!error ... %! ridge ([1; 2; 3], ones (3), 3, "some") statistics-release-1.7.3/inst/rmmissing.m000066400000000000000000000162721475240274700205210ustar00rootroot00000000000000## Copyright (C) 1995-2023 The Octave Project Developers ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{R} =} rmmissing (@var{A}) ## @deftypefnx {statistics} {@var{R} =} rmmissing (@var{A}, @var{dim}) ## @deftypefnx {statistics} {@var{R} =} rmmissing (@dots{}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{R} @var{TF}] =} rmmissing (@dots{}) ## ## Remove missing or incomplete data from an array. ## ## Given an input vector or matrix (2-D array) @var{A}, remove missing data ## from a vector or missing rows or columns from a matrix. @var{A} ## can be a numeric array, char array, or an array of cell strings. ## @var{R} returns the array after removal of missing data. ## ## The values which represent missing data depend on the data type of @var{A}: ## ## @itemize ## @item ## @qcode{NaN}: @code{single}, @code{double}. ## ## @item ## @qcode{' '} (white space): @code{char}. ## ## @item ## @qcode{@{''@}}: string cells. ## @end itemize ## ## Choose to remove rows (default) or columns by setting optional input ## @var{dim}: ## ## @itemize ## @item ## @qcode{1}: rows. ## ## @item ## @qcode{2}: columns. ## @end itemize ## ## Note: data types with no default 'missing' value will always result in ## @code{R == A} and a TF output of @code{false(size(@var{A}))}. ## ## Additional optional parameters are set by @var{Name}-@var{Value} pairs. ## These are: ## ## @itemize ## @item ## @qcode{MinNumMissing}: minimum number of missing values to remove an entry, ## row or column, defined as a positive integer number. E.g.: if ## @qcode{MinNumMissing} is set to @code{2}, remove the row of a numeric matrix ## only if it includes 2 or more NaN. ## @end itemize ## ## Optional return value @var{TF} is a logical array where @code{true} values ## represent removed entries, rows or columns from the original data @var{A}. ## ## @end deftypefn ## ## @seealso{fillmissing, ismissing, standardizeMissing} function [R, TF] = rmmissing (A, varargin) if ((nargin < 1) || (nargin > 4)) print_usage (); endif if ndims(A) > 2 error ("rmmissing: input dimension cannot exceed 2"); endif optDimensionI = 2; # default dimension: rows optMinNumMissingI = 1; ## parse options if (nargin > 1) if (isnumeric (varargin{1})) ## option "dim" switch (varargin{1}) case 1 optDimensionI = 2; case 2 optDimensionI = 1; otherwise error ("rmmissing: 'dim' must be either 1 or 2"); endswitch pair_index = 2; else [r, c] = size (A); ## first non singleton dimension, but only two dimensions considered if (r == 1 && c != 1) optDimensionI = 1; endif pair_index = 1; endif ## parse name-value parameters while (pair_index <= (nargin - 1)) switch (lower (varargin{pair_index})) ## minimum number of missing values to remove entries; ## it must be a positive integer number case "minnummissing" if (! isnumeric (varargin{pair_index + 1}) || ! isscalar (varargin{pair_index + 1}) || floor (varargin{pair_index + 1}) != varargin{pair_index + 1} || varargin{pair_index + 1} < 1) error (["rmmissing: 'MinNumMissing' requires a positive integer"... " number as value"]); endif optMinNumMissingI = varargin{pair_index + 1}; otherwise error ("rmmissing: unknown parameter name '%s'", ... varargin{pair_index}); endswitch pair_index += 2; endwhile endif ## main logic TF = ismissing (A); if (isvector (A)) R = A(TF == 0); elseif (iscellstr(A) || ismatrix (A)) ## matrix: ismissing returns an array, so it must be converted to a row or ## column vector according to the "dim" of choice if (optMinNumMissingI > 1) TF = sum (TF, optDimensionI); TF(TF < optMinNumMissingI) = 0; TF = logical (TF); else TF = any (TF, optDimensionI); endif if (optDimensionI == 2) ## remove the rows R = A((TF == 0), :); else ## remove the columns R = A(:, (TF == 0)); endif else error ("rmmissing: unsupported data"); endif endfunction %!assert (rmmissing ([1,NaN,3]), [1,3]) %!assert (rmmissing ('abcd f'), 'abcdf') %!assert (rmmissing ({'xxx','','xyz'}), {'xxx','xyz'}) %!assert (rmmissing ({'xxx','';'xyz','yyy'}), {'xyz','yyy'}) %!assert (rmmissing ({'xxx','';'xyz','yyy'}, 2), {'xxx';'xyz'}) %!assert (rmmissing ([1,2;NaN,2]), [1,2]) %!assert (rmmissing ([1,2;NaN,2], 2), [2,2]') %!assert (rmmissing ([1,2;NaN,4;NaN,NaN],"MinNumMissing", 2), [1,2;NaN,4]) ## Test second output %!test %! x = [1:6]; %! x([2,4]) = NaN; %! [~, idx] = rmmissing (x); %! assert (idx, logical ([0, 1, 0, 1, 0, 0])); %! assert (class(idx), 'logical'); %! x = reshape (x, [2, 3]); %! [~, idx] = rmmissing (x); %! assert (idx, logical ([0; 1])); %! assert (class(idx), 'logical'); %! [~, idx] = rmmissing (x, 2); %! assert (idx, logical ([1, 1, 0])); %! assert (class(idx), 'logical'); %! [~, idx] = rmmissing (x, 1, "MinNumMissing", 2); %! assert (idx, logical ([0; 1])); %! assert (class(idx), 'logical'); %! [~, idx] = rmmissing (x, 2, "MinNumMissing", 2); %! assert (idx, logical ([0, 0, 0])); %! assert (class(idx), 'logical'); ## Test data type handling %!assert (rmmissing (single ([1 2 NaN; 3 4 5])), single ([3 4 5])) %!assert (rmmissing (logical (ones (3))), logical (ones (3))) %!assert (rmmissing (int32 (ones (3))), int32 (ones (3))) %!assert (rmmissing (uint32 (ones (3))), uint32 (ones (3))) %!assert (rmmissing ({1, 2, 3}), {1, 2, 3}) %!assert (rmmissing ([struct, struct, struct]), [struct, struct, struct]) ## Test empty input handling %!assert (rmmissing ([]), []) %!assert (rmmissing (ones (1,0)), ones (1,0)) %!assert (rmmissing (ones (1,0), 1), ones (1,0)) %!assert (rmmissing (ones (1,0), 2), ones (1,0)) %!assert (rmmissing (ones (0,1)), ones (0,1)) %!assert (rmmissing (ones (0,1), 1), ones (0,1)) %!assert (rmmissing (ones (0,1), 2), ones (0,1)) %!error rmmissing (ones (0,1,2)) ## Test input validation %!error rmmissing () %!error rmmissing (ones(2,2,2)) %!error rmmissing ([1 2; 3 4], 5) %!error rmmissing ([1 2; 3 4], "XXX", 1) %!error <'MinNumMissing'> rmmissing ([1 2; 3 4], 2, "MinNumMissing", -2) %!error <'MinNumMissing'> rmmissing ([1 2; 3 4], "MinNumMissing", 3.8) %!error <'MinNumMissing'> rmmissing ([1 2; 3 4], "MinNumMissing", [1 2 3]) %!error <'MinNumMissing'> rmmissing ([1 2; 3 4], "MinNumMissing", 'xxx') statistics-release-1.7.3/inst/runstest.m000066400000000000000000000300551475240274700203730ustar00rootroot00000000000000## Copyright (C) 2013 Nir Krakauer ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} runstest (@var{x}) ## @deftypefnx {statistics} {@var{h} =} runstest (@var{x}, @var{v}) ## @deftypefnx {statistics} {@var{h} =} runstest (@var{x}, @qcode{"ud"}) ## @deftypefnx {statistics} {@var{h} =} runstest (@dots{}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{stats}] =} runstest (@dots{}) ## ## Run test for randomness in the vector @var{x}. ## ## @code{@var{h} = runstest (@var{x})} calculates the number of runs of ## consecutive values above or below the mean of @var{x} and tests the null ## hypothesis that the values in the data vector @var{x} come in random order. ## @var{h} is 1 if the test rejects the null hypothesis at the 5% significance ## level, or 0 otherwise. ## ## @code{@var{h} = runstest (@var{x}, @var{v})} tests the null hypothesis based ## on the number of runs of consecutive values above or below the specified ## reference value @var{v}. Values exactly equal to @var{v} are omitted. ## ## @code{@var{h} = runstest (@var{x}, @qcode{"ud"})} calculates the number of ## runs up or down and tests the null hypothesis that the values in the data ## vector @var{x} follow a trend. Too few runs indicate a trend, while too ## many runs indicate an oscillation. Values exactly equal to the preceding ## value are omitted. ## ## @code{@var{h} = runstest (@dots{}, @var{Name}, @var{Value})} specifies ## additional options to the above tests by one or more @var{Name}-@var{Value} ## pair arguments. ## ## @multitable @columnfractions 0.15 0.05 0.8 ## @headitem Name @tab @tab Value ## @item @qcode{"alpha"} @tab @tab the significance level. Default is 0.05. ## ## @item @qcode{"method"} @tab @tab a string specifying the method used to ## compute the p-value of the test. It can be either @qcode{"exact"} to use an ## exact algorithm, or @qcode{"approximate"} to use a normal approximation. The ## default is @qcode{"exact"} for runs above/below, and for runs up/down when ## the length of x is less than or equal to 50. When testing for runs up/down ## and the length of @var{x} is greater than 50, then the default is ## @qcode{"approximate"}, and the @qcode{"exact"} method is not available. ## ## @item @qcode{"tail"} @tab @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.2 0.15 0.05 0.5 ## @item @tab @qcode{"both"} @tab @tab two-tailed (default) ## @item @tab @qcode{"left"} @tab @tab left-tailed ## @item @tab @qcode{"right"} @tab @tab right-tailed ## @end multitable ## ## @seealso{signrank, signtest} ## @end deftypefn function [h, pval, stats] = runstest (x, v, varargin) ## Check arguments if (nargin < 1) print_usage; endif ## Check X being a vector of scalar values if (! isvector (x) || ! isnumeric (x)) error ("runstest: X must be a vector a scalar values."); else ## Remove missing values (NaNs) x(isnan (x)) = []; endif ## Check second argument being either a scalar reference number or "ud" string if (nargin > 1) if (isempty (v)) v = mean (x); endif if (isnumeric (v) && isscalar (v)) x = sign (x - v); rm = x == 0; if (sum (rm) > 0) warning ("runstest: %d elements equal to V were omitted.", sum (rm)); endif x(rm) = []; N = numel(x); UD = false; elseif (strcmpi (v, "ud")) x = diff (x); rm = x == 0; if (sum (rm) > 0) warning ("runstest: %d repeated elements were omitted.", sum (rm)); endif x(rm) = []; N = numel(x) + 1; UD = true else error ("runstest: V must be either a scalar number or 'ud' char string."); endif v = v; else v = mean (x); x = sign (x - v); rm = x == 0; if (sum (rm) > 0) warning ("runstest: %d elements equal to 'mean(X)' were omitted.", ... sum (rm)); endif x(rm) = []; N = numel(x); UD = false; endif ## Get number of runs n_up = sum (x==1); n_dn = numel (x) - n_up; ## Add defaults alpha = 0.05; if (N < 50 || ! UD) method = "exact"; else method = "approximate"; endif tail = "both"; ## Parse optional arguments and validate parameters while (numel (varargin) > 1) switch (lower (varargin{1})) case "alpha" alpha = varargin{2}; if (! isscalar (alpha) || ! isnumeric (alpha) || alpha <= 0 || alpha >= 1) error ("runstest: invalid value for alpha."); endif case "method" method = varargin{2}; if (! any (strcmpi (method, {"exact", "approximate"}))) error ("runstest: invalid value for method."); endif if (strcmpi (method, "exact") && N > 50) warning ("runstest: exact method is not available for N > 50."); method = "approximate"; endif case "tail" tail = varargin{2}; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("runstest: invalid value for tail."); endif otherwise error ("runstest: invalid optional argument."); endswitch varargin([1:2]) = []; endwhile ## Do the calculations here if (N > 0) R_num = sum (x([1:end-1]) != x([2:end])) + 1; ##R_num = sum ((x(1:(end-1)) .* x(2:end)) < 0) + 1; #number of runs ## Special case if (N == 1) z = NaN; ## Compute with z statistic else ## Handle up/down or above/below if (UD) R_bar = (2 * N - 1) / 3; R_std = sqrt ((16 * N - 29) / 90); else R_bar = 1 + 2 * n_up * n_dn / N; R_std = sqrt (2 * n_up * n_dn * (2 * n_up * n_dn - N) / ... (N ^ 2 * (N - 1))); end ## Handle tail if (strcmpi (tail, "both")) tc = -0.5 * sign (R_num - R_bar); elseif (strcmpi (tail, "left")) tc = 0.5; else tc = -0.5; endif ## Compute z value if (R_std > 0) z = (R_num + tc - R_bar) / R_std; else z = Inf * sign (R_num + tc - R_bar); endif endif ## Exact method if (strcmpi (method, "exact")) if (UD) R_max = N - 1; ## Get precalculated results from rundist.mat file temp = load ("rundist.mat"); runD = temp.rundist; M = runD{N}; p = M / sum (M); p = p([1:R_max]); else R_max = 2 * min ([n_up, n_dn]) + 1; if (n_up == 0 || n_dn == 0) p = 1; else R_vec = [1:R_max]; p = zeros (size (R_vec)); t = mod (R_vec, 2) == 0; ## Compute even if (any (t)) k = R_vec(t) / 2; p(t) = 2 * exp (logBinoCoeff (n_up - 1, k - 1) + ... logBinoCoeff (n_dn - 1, k - 1) - ... logBinoCoeff (N, n_dn)); endif ## Compute odd if (any (! t)) k = floor (R_vec(! t) / 2); logdenom = logBinoCoeff (N, n_dn); p(! t) = exp (logBinoCoeff (n_up - 1, k - 1) + ... logBinoCoeff (n_dn - 1, k) - logdenom) + ... exp (logBinoCoeff (n_up - 1, k) + ... logBinoCoeff (n_dn - 1, k - 1) - logdenom); endif endif endif if (isempty (p)) p_ex = 1; else p_ex = p(R_num); end p_lo = sum (p([1:R_num-1])); p_hi = sum (p([R_num+1:end])); else ## Compute with z statistic p_ex = 0; p_lo = normcdf (z); p_hi = normcdf (-z); end ## Assume a constant vector in data else R_num = NaN; p_ex = 1; p_lo = 0; p_hi = 0; z = NaN; endif ## Compute tail probability if (strcmpi (tail, "both")) pval = min([1, 2*(p_ex + min ([p_lo, p_hi]))]); elseif (strcmpi (tail, "left")) pval = p_ex + p_lo; else pval = p_ex + p_hi; endif ## Retyrn decision of test h = double (pval <= alpha); if (nargout > 2) stats.nruns = R_num; stats.n1 = n_up; stats.n0 = n_dn; stats.z = z; endif endfunction ## Compute the log of the binomial coefficient function logBC = logBinoCoeff(N,n) logBC = gammaln (N + 1) - gammaln (n + 1) - gammaln (N - n + 1); endfunction %!test %! ## NIST beam deflection data %! ## http://www.itl.nist.gov/div898/handbook/eda/section4/eda425.htm %! data = [-213, -564, -35, -15, 141, 115, -420, -360, 203, -338, -431, ... %! 194, -220, -513, 154, -125, -559, 92, -21, -579, -52, 99, -543, ... %! -175, 162, -457, -346, 204, -300, -474, 164, -107, -572, -8, 83, ... %! -541, -224, 180, -420, -374, 201, -236, -531, 83, 27, -564, -112, ... %! 131, -507, -254, 199, -311, -495, 143, -46, -579, -90, 136, ... %! -472, -338, 202, -287, -477, 169, -124, -568, 17, 48, -568, -135, ... %! 162, -430, -422, 172, -74, -577, -13, 92, -534, -243, 194, -355, ... %! -465, 156, -81, -578, -64, 139, -449, -384, 193, -198, -538, 110, ... %! -44, -577, -6, 66, -552, -164, 161, -460, -344, 205, -281, -504, ... %! 134, -28, -576, -118, 156, -437, -381, 200, -220, -540, 83, 11, ... %! -568, -160, 172, -414, -408, 188, -125, -572, -32, 139, -492, ... %! -321, 205, -262, -504, 142, -83, -574, 0, 48, -571, -106, 137, ... %! -501, -266, 190, -391, -406, 194, -186, -553, 83, -13, -577, -49, ... %! 103, -515, -280, 201, 300, -506, 131, -45, -578, -80, 138, -462, ... %! -361, 201, -211, -554, 32, 74, -533, -235, 187, -372, -442, 182, ... %! -147, -566, 25, 68, -535, -244, 194, -351, -463, 174, -125, -570, ... %! 15, 72, -550, -190, 172, -424, -385, 198, -218, -536, 96]; %! [h, p, stats] = runstest (data, median (data)); %! expected_h = 1; %! expected_p = 0.008562; %! expected_z = 2.6229; %! assert (h, expected_h); %! assert (p, expected_p, 1E-6); %! assert (stats.z, expected_z, 1E-4); %!shared x %! x = [45, -60, 1.225, 55.4, -9 27]; %!test %! [h, p, stats] = runstest (x); %! assert (h, 0); %! assert (p, 0.6, 1e-14); %! assert (stats.nruns, 5); %! assert (stats.n1, 3); %! assert (stats.n0, 3); %! assert (stats.z, 0.456435464587638, 1e-14); %!test %! [h, p, stats] = runstest (x, [], "method", "approximate"); %! assert (h, 0); %! assert (p, 0.6481, 1e-4); %! assert (stats.z, 0.456435464587638, 1e-14); %!test %! [h, p, stats] = runstest (x, [], "tail", "left"); %! assert (h, 0); %! assert (p, 0.9, 1e-14); %! assert (stats.z, 1.369306393762915, 1e-14); %!error runstest (ones (2,20)) %!error runstest (["asdasda"]) %!error ... %! runstest ([2 3 4 3 2 3 4], "updown") %!error ... %! runstest ([2 3 4 3 2 3 4], [], "alpha", 0) %!error ... %! runstest ([2 3 4 3 2 3 4], [], "alpha", [0.02 0.2]) %!error ... %! runstest ([2 3 4 3 2 3 4], [], "alpha", 1.2) %!error ... %! runstest ([2 3 4 3 2 3 4], [], "alpha", -0.05) %!error ... %! runstest ([2 3 4 3 2 3 4], [], "method", "some") %!error ... %! runstest ([2 3 4 3 2 3 4], [], "tail", "some") %!error ... %! runstest ([2 3 4 3 2 3 4], [], "option", "some") statistics-release-1.7.3/inst/sampsizepwr.m000066400000000000000000001225231475240274700210720ustar00rootroot00000000000000## Copyright (C) 2022 Andrew Penn ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{n} =} sampsizepwr (@var{testtype}, @var{params}, @var{p1}) ## @deftypefnx {statistics} {@var{n} =} sampsizepwr (@var{testtype}, @var{params}, @var{p1}, @var{power}) ## @deftypefnx {statistics} {@var{power} =} sampsizepwr (@var{testtype}, @var{params}, @var{p1}, [], @var{n}) ## @deftypefnx {statistics} {@var{p1} =} sampsizepwr (@var{testtype}, @var{params}, [], @var{power}, @var{n}) ## @deftypefnx {statistics} {[@var{n1}, @var{n2}] =} sampsizepwr (@qcode{"t2"}, @var{params}, @var{p1}, @var{power}) ## @deftypefnx {statistics} {[@dots{}] =} sampsizepwr (@var{testtype}, @var{params}, @var{p1}, @var{power}, @var{n}, @var{name}, @var{value}) ## ## Sample size and power calculation for hypothesis test. ## ## @code{sampsizepwr} computes the sample size, power, or alternative parameter ## value for a hypothesis test, given the other two values. For example, you can ## compute the sample size required to obtain a particular power for a ## hypothesis test, given the parameter value of the alternative hypothesis. ## ## @code{@var{n} = sampsizepwr (@var{testtype}, @var{params}, @var{p1})} returns ## the sample size N required for a two-sided test of the specified type to have ## a power (probability of rejecting the null hypothesis when the alternative is ## true) of 0.90 when the significance level (probability of rejecting the null ## hypothesis when the null hypothesis is true) is 0.05. @var{params} specifies ## the parameter values under the null hypothesis. P1 specifies the value of ## the single parameter being tested under the alternative hypothesis. For the ## two-sample t-test, N is the value of the equal sample size for both samples, ## @var{params} specifies the parameter values of the first sample under the ## null and alternative hypotheses, and P1 specifies the value of the single ## parameter from the other sample under the alternative hypothesis. ## ## The following TESTTYPE values are available: ## ## @multitable @columnfractions 0.05 0.1 0.85 ## @item @tab "z" @tab one-sample z-test for normally distributed data with ## known standard deviation. @var{params} is a two-element vector [MU0 SIGMA0] ## of the mean and standard deviation, respectively, under the null hypothesis. ## P1 is the value of the mean under the alternative hypothesis. ## @item @tab "t" @tab one-sample t-test or paired t-test for normally ## distributed data with unknown standard deviation. @var{params} is a ## two-element vector [MU0 SIGMA0] of the mean and standard deviation, ## respectively, under the null hypothesis. P1 is the value of the mean under ## the alternative hypothesis. ## @item @tab "t2" @tab two-sample pooled t-test (test for equal means) for ## normally distributed data with equal unknown standard deviations. ## @var{params} is a two-element vector [MU0 SIGMA0] of the mean and standard ## deviation of the first sample under the null and alternative hypotheses. P1 ## is the the mean of the second sample under the alternative hypothesis. ## @item @tab "var" @tab chi-square test of variance for normally distributed ## data. @var{params} is the variance under the null hypothesis. P1 is the ## variance under the alternative hypothesis. ## @item @tab "p" @tab test of the P parameter (success probability) for a ## binomial distribution. @var{params} is the value of P under the null ## hypothesis. P1 is the value of P under the alternative hypothesis. ## @item @tab "r" @tab test of the correlation coefficient parameter for ## significance. @var{params} is the value of r under the null hypothesis. ## P1 is the value of r under the alternative hypothesis. ## @end multitable ## ## The "p" test for the binomial distribution is a discrete test for which ## increasing the sample size does not always increase the power. For N values ## larger than 200, there may be values smaller than the returned N value that ## also produce the desired power. ## ## @code{@var{n} = sampsizepwr (@var{testtype}, @var{params}, @var{p1}, ## @var{power})} returns the sample size N such that the power is @var{power} ## for the parameter value P1. For the two-sample t-test, N is the equal sample ## size of both samples. ## ## @code{[@var{n1}, @var{n2}] = sampsizepwr ("t2", @var{params}, @var{p1}, ## @var{power})} returns the sample sizes @var{n1} and @var{n2} for the two ## samples. These values are the same unless the "ratio" parameter, ## @code{@var{ratio} = @var{n2} / @var{n2}}, is set to a value other than ## the default (See the name/value pair definition of ratio below). ## ## @code{@var{power} = sampsizepwr (@var{testtype}, @var{params}, @var{p1}, [], ## @var{n})} returns the power achieved for a sample size of @var{n} when the ## true parameter value is @var{p1}. For the two-sample t-test, @var{n} is the ## smaller one of the two sample sizes. ## ## @code{@var{p1} = sampsizepwr (@var{testtype}, @var{params}, [], @var{power}, ## @var{n})} returns the parameter value detectable with the specified sample ## size @var{n} and power @var{power}. For the two-sample t-test, @var{n} is ## the smaller one of the two sample sizes. When computing @var{p1} for the "p" ## test, if no alternative can be rejected for a given @var{params}, @var{n} and ## @var{power} value, the function displays a warning message and returns NaN. ## ## @code{[@dots{}] = sampsizepwr (@dots{}, @var{n}, @var{name}, @var{value})} ## specifies one or more of the following @var{name} / @var{value} pairs: ## ## @multitable @columnfractions 0.05 0.15 0.8 ## @item @tab "alpha" @tab significance level of the test (default is 0.05) ## @item @tab "tail" @tab the type of test which can be: ## @end multitable ## ## @multitable @columnfractions 0.1 0.20 0.7 ## @item @tab "both" @tab two-sided test for an alternative @var{p1} not equal ## to @var{params} ## ## @item @tab "right" @tab one-sided test for an alternative @var{p1} larger ## than @var{params} ## ## @item @tab "left" @tab one-sided test for an alternative @var{p1} smaller ## than @var{params} ## @end multitable ## ## @multitable @columnfractions 0.05 0.15 0.8 ## @item @tab "ratio" @tab desired ratio @var{n2} / @var{n2} of the larger ## sample size @var{n2} to the smaller sample size @var{n1}. Used only for the ## two-sample t-test. The value of @code{@var{ratio}} is greater than or equal ## to 1 (default is 1). ## @end multitable ## ## @code{sampsizepwr} computes the sample size, power, or alternative hypothesis ## value given values for the other two. Specify one of these as [] to compute ## it. The remaining parameters (and ALPHA, RATIO) can be scalars or arrays of ## the same size. ## ## @seealso{vartest, ttest, ttest2, ztest, binocdf} ## @end deftypefn function [out, N2] = sampsizepwr (TestType, params, p1, power, n, varargin) ## Check for valid number of input arguments narginchk (3, Inf); ## Add defaults for 3 or 4 input arguments if (nargin == 3) power = 0.90; n = []; elseif (nargin == 4) n = []; endif ## Add defaults for extra arguments alpha = 0.05; tail = "both"; ratio = 1; ## Check for valid test type and corresponding size of parameters t_types = {"z", "t", "t2", "var", "p", "r"}; nparams = [2, 2, 2, 1, 1, 1]; if (isempty (TestType) || ! ischar (TestType) || size (TestType, 1) != 1) error ("sampsizepwr: test type must be a non-empty string."); endif if (sum (strcmpi (TestType, t_types)) != 1) error ("sampsizepwr: invalid test type."); endif if (! isnumeric (params)) error ("sampsizepwr: parameters must be numeric."); endif if (length (params) != nparams(strcmpi (TestType, t_types))) error ("sampsizepwr: invalid size of parameters for this test type."); endif ## Check for correct number of output arguments if ((nargout > 1) && ! strcmpi (TestType, "t2")) error ("sampsizepwr: wrong number of output arguments for this test type."); endif Lbound = [-Inf, -Inf, -Inf, 0, 0, -1]; Ubound = [ Inf, Inf, Inf, Inf, 1, 1]; Lbound = Lbound(strcmpi (TestType, t_types)); Ubound = Ubound(strcmpi (TestType, t_types)); ## Check for invalid parameters specific to each test type switch (lower (TestType)) case "z" if (params(2) <= 0) error ("sampsizepwr: negative or zero variance."); endif PowerFunction = @PowerFunction_N; case "t" if (params(2) <= 0) error ("sampsizepwr: negative or zero variance."); endif PowerFunction = @PowerFunction_T; case "t2" if (params(2) <= 0) error ("sampsizepwr: negative or zero variance."); endif PowerFunction = @PowerFunction_T2; case "var" if (params(1) <= 0) error ("sampsizepwr: negative or zero variance."); endif PowerFunction = @PowerFunction_V; case "p" if (params(1) <= 0 || params(1) >= 1) error ("sampsizepwr: out of range probability."); endif PowerFunction = @PowerFunction_P; case "r" if (params(1) <= -1 || params(1) >= 1) error ("sampsizepwr: out of range regression coefficient."); elseif (params(1) == 0) error ("sampsizepwr: regression coefficient must not be 0."); endif PowerFunction = @PowerFunction_R; endswitch ## Get and validate optional parameters numarg = numel (varargin); argpos = 1; while (numarg) argname = varargin{argpos}; switch (lower (argname)) case "alpha" alpha = varargin{argpos + 1}; if (! isnumeric (alpha) || any (alpha(:) <= 0) || any( alpha(:) >= 1)) error ("sampsizepwr: invalid value for 'alpha' parameter."); endif case "tail" tail = varargin{argpos + 1}; if (! ischar (tail) || (size (tail, 1) != 1)) error ("sampsizepwr: 'tail' parameter must be a non-empty string."); endif if (sum (strcmpi (tail, {"left","both","right"})) != 1) error ("sampsizepwr: invalid value for 'tail' parameter."); endif case "ratio" ratio = varargin{argpos + 1}; if (! isnumeric (ratio) || any (ratio(:) < 1)) error ("sampsizepwr: invalid value for 'ratio' parameter."); endif endswitch numarg -= 2; argpos += 2; endwhile ## Check that only one of either p1, power, or n are missing if (isempty(p1) + isempty(power) + isempty(n) != 1) error ("sampsizepwr: only one of either p1, power, or n must be missing."); endif ## Check for valid P1 if (! isempty (p1)) if (! isnumeric (p1)) error ("sampsizepwr: alternative hypothesis parameter must be numeric."); elseif ((! strcmpi (tail, "right") && any (p1(:) <= Lbound)) || ... (! strcmpi (tail, "left") && any (p1(:) >= Ubound))) error ("sampsizepwr: alternative hypothesis parameter out of range."); endif endif ## Check for valid POWER if (! isempty (power) && ... (! isnumeric (power) || any (power(:) <= 0) || any (power(:) >= 1))) error ("sampsizepwr: invalid value for POWER."); endif if (! isempty (power) && any (power(:) <= alpha(:))) error ("sampsizepwr: Cannot compute N or P1 unless POWER > 'alpha'."); endif ## Expand non-empty P1/POWER/N so they are all the same size if (isempty (p1)) [err, power, n, alpha, ratio] = common_size (power, n, alpha, ratio); outclass = getclass (power, n, alpha, ratio); elseif (isempty (power)) [err, p1, n, alpha, ratio] = common_size (p1, n, alpha, ratio); outclass = getclass (p1, n, alpha, ratio); else # n is empty [err, p1, power, alpha, ratio] = common_size (p1, power, alpha, ratio); outclass = getclass (power, p1, alpha, ratio); endif if (err > 0) error ("sampsizepwr: input arguments size mismatch."); end ## Check for valid options when computing N if (isempty (n)) if (any (p1(:) == params(1))) error ("sampsizepwr: Same value for null and alternative hypothesis."); elseif (strcmpi (tail, "left") && any (p1(:) >= params(1))) error ("sampsizepwr: Invalid P1 for testing left tail."); elseif (strcmpi (tail, "right") && any(p1(:) <= params(1))) error ("sampsizepwr: Invalid P1 for testing right tail."); endif endif ## Allocate output of proper size and class out = zeros (size (alpha), outclass); ## Compute whichever one of P1/POWER/N that is now empty if (isempty (p1)) ## Compute effect size given power and sample size switch (lower (TestType)) case "z" ## z (normal) test out(:) = findP1z (params(1), params(2), power, n, alpha, tail); case "t" ## t-test out(:) = findP1t (params(1), params(2), power, n, alpha, tail); case "t2" ## two-sample t-test out(:) = findP1t2 (params(1), params(2), power, n, alpha, tail, ratio); case "var" ## chi-square (variance) test out(:) = findP1v (params(1), power, n, alpha, tail); case "p" ## binomial (p) test out(:) = findP1p (params(1), power, n, alpha, tail); case "r" ## regression coefficient (r) test out(:) = findP1r (params(1), power, n, alpha, tail); endswitch elseif (isempty (power)) ## Compute power given effect size and sample size switch (lower (TestType)) case {"z", "t"} out(:) = PowerFunction (params(1), p1, params(2), alpha, tail, n); case "t2" out(:) = PowerFunction (params(1), p1, params(2), alpha, tail, n, ratio); case {"var", "p", "r"} out(:) = PowerFunction (params(1), p1, alpha, tail, n); endswitch else ## Compute sample size given power and effect size switch (lower (TestType)) case {"z", "t"} ## Calculate one-sided Z value directly out(:) = z1testN (params(1), p1, params(2), power, alpha, tail); ## Iterate upward from there for the other cases if (strcmpi (TestType, "t") || strcmp (tail, "both")) if (strcmpi (TestType, "t")) out = max (out, 2); endif ## Count upward until we get the value we need elem = 1:numel (alpha); while (! isempty (elem)) actualpower = PowerFunction_T (params(1), p1(elem), params(2), ... alpha(elem), tail, out(elem)); elem = elem(actualpower < power(elem)); out(elem) = out(elem) + 1; endwhile endif case "t2" ## Initialize second output argument N2 = zeros (size (alpha), outclass); ## Caculate one-sided two-sample t-test iteratively [out(:), N2(:)] = t1testN (params(1), p1, params(2), power, ... alpha, tail, ratio); case "var" ## Use a binary search method out(:) = searchbinaryN (PowerFunction, [1, 100], params(1), ... p1, power, alpha, tail); case "p" ## Use a binary search method out(:) = searchbinaryN (PowerFunction, [0, 100], params(1), ... p1, power, alpha, tail); ## Adjust for discrete distribution t = out <= 200; if (any (t(:))) ## Try values from 1 up to N (out) and pick the smallest value out(t) = adjdiscreteN (out(t), PowerFunction, params(1), ... p1(t), alpha(t), tail, power(t)); endif if (any (! t(:))) warning ("sampsizepwr: approximate N."); endif case "r" ## Calculate sample size using Student's t distribution out(:) = r1testN (params(1), p1, power, alpha, tail); endswitch endif endfunction ## Define class for output function out = getclass (varargin) if (class (varargin{1}) == "single" || class (varargin{2}) == "single" || ... class (varargin{3}) == "single" || class (varargin{4}) == "single") out = "single"; else out = "double"; endif endfunction ## Sample size calculation for the one-sided Z test function N = z1testN (mu0, mu1, sig, desiredpower, alpha, tail) ## Compute the one-sided normal value directly if (strcmp (tail, "both")) alpha = alpha ./ 2; endif z1 = -norminv (alpha); z2 = norminv (1 - desiredpower); mudiff = abs (mu0 - mu1) / sig; N = ceil (((z1 - z2) ./ mudiff) .^ 2); endfunction ## Sample size calculation for R test function N = rtestN (r0, r1, desiredpower, alpha, tail) ## Compute only for 2-tailed test if (strcmp (tail, "both")) alpha = alpha ./ 2; else error ("sampsizepwr: only 2-tailed testing for regression coefficient."); endif ## Get quantiles of the standard normal deviates for alpha and power Za = norminv (alpha); Zb = norminv (1 - desiredpower); ## Compute difference in regression coefficients rdiff = abs (r0 - r1) C = 0.5 * log ((1 + rdiff) / (1 - rdiff)); ## Compute sample size N = ((Za + Zb) / C) .^ 2 + 3; endfunction ## Find alternative hypothesis parameter value P1 for Z test function mu1 = findP1z (mu0, sig, desiredpower, N, alpha, tail) if (strcmp (tail, "both")) alpha = alpha ./ 2; end sig = sig ./ sqrt (N); ## Get quantiles of the normal or t distribution if (strcmp (tail, "left")) z1 = norminv (alpha); z2 = norminv (desiredpower); else # upper or two-tailed test z1 = norminv (1 - alpha); z2 = norminv (1 - desiredpower); endif mu1 = mu0 + sig .* (z1 - z2); ## For 2-sided test, refine by taking the other tail into account if (strcmp (tail, "both")) elem = 1:numel (alpha); desiredbeta = 1 - desiredpower; betahi = desiredbeta; betalo = zeros (size (desiredbeta)); while (true) ## Compute probability of being below the lower critical value under H1 betalo(elem) = normcdf (-z1(elem) + (mu0 - mu1(elem)) ./ sig(elem)); ## See if the upper and lower probabilities are close enough elem = elem(abs ((betahi(elem) - betalo(elem)) - desiredbeta(elem)) > ... 1e-6 * desiredbeta(elem)); if (isempty (elem)) break endif ## Find a new mu1 by adjusting beta to take lower tail into account betahi(elem) = desiredbeta(elem) + betalo(elem); mu1(elem) = mu0 + sig(elem) .* (z1(elem) - norminv (betahi(elem))); endwhile endif endfunction ## Find alternative hypothesis parameter value P1 for t-test function mu1 = findP1t (mu0, sig, desiredpower, N, alpha, tail) if (strcmp (tail, "both")) a2 = alpha ./ 2; else a2 = alpha; endif ## Get quantiles of the normal or t distribution if (strcmp (tail, "left")) z1 = norminv(alpha); z2 = norminv(desiredpower); else # upper or two-tailed test z1 = norminv(1-a2); z2 = norminv(1-desiredpower); endif mu1 = mu0 + sig .* (z1-z2) ./ sqrt (N); ## Refine using fzero for j=1:numel (mu1) if (mu1(j) > mu0) F0 = @(mu1arg) PowerFunction_T (mu0, max (mu0, mu1arg), sig, alpha(j), ... tail, N(j)) - desiredpower(j); else F0 = @(mu1arg) desiredpower(j) - PowerFunction_T (mu0, min (mu0, ... mu1arg), sig, alpha(j), tail, N(j)); endif mu1(j) = fzero (F0, mu1(j)); endfor endfunction ## Sample size calculation for the one-sided two-sample t-test function [N1, N2] = t1testN (mu0, mu1, sig, desiredpower, alpha, tail, ratio) if (strcmp (tail, "both")) alpha = alpha ./ 2; endif ## Compute the initial value of N, approximated by normal distribution z1 = -norminv (alpha); z2 = norminv (1 - desiredpower); n_0 = ceil ((z1 - z2) .^2 .* (sig ./ abs ((mu0 - mu1))) .^ 2 * 2); ## n need to be > 1, otherwise the degree of freedom of t < 0 n_0(n_0 <= 1) = 2; N = ones (size (n_0)); ## iteratively update the sample size if (strcmp (tail, "both")) for j = 1:numel (n_0) F = @(n) nctcdf (tinv (alpha(j), n + ratio(j) .* n - 2), ... n + ratio(j) .* n - 2, abs (mu1(j) - mu0) ./ ... (sig .* sqrt (1 ./ n + 1 ./ (ratio(j) .* n)))) + ... (1 - nctcdf (- tinv (alpha(j), n + ratio(j) .* n - 2), ... n + ratio(j) .* n - 2, abs (mu1(j) - mu0) ./ ... (sig .* sqrt (1 ./ n + 1 ./ (ratio(j) .* n)))))- ... desiredpower(j); N(j) = localfzero (F, n_0(j), ratio); endfor else for j = 1:numel (n_0) F = @(n) (1 - nctcdf (- tinv (alpha(j), n + ratio(j) .* n - 2), ... n + ratio(j) .* n - 2, abs (mu1(j) - mu0) ./ (sig .* ... sqrt (1 ./ n + 1 ./ (ratio(j) .* n))))) - desiredpower(j); N(j) = localfzero (F, n_0(j), ratio); endfor endif N1 = ceil (N); N2 = ceil (ratio .* N); endfunction ## Find alternative hypothesis parameter value P1 for two-sample t-test function mu1 = findP1t2 (mu0, sig, desiredpower, N, alpha, tail, ratio) if (strcmp (tail, "both")) a2 = alpha ./ 2; else a2 = alpha; endif ## Get quantiles of the normal or t distribution if (strcmp (tail, "left")) t1 = tinv (alpha, N + ratio .* N - 2); t2 = tinv (desiredpower, N + ratio .* N - 2); else # upper or two-tailed test t1 = tinv (1 - a2, N + ratio .* N - 2); # upper tail under H0 t2 = tinv (1 - desiredpower, N + ratio .* N - 2); # lower tail under H1 endif mu1 = mu0 + sig .* (t1 - t2) .* sqrt (1 ./ N + 1 ./ (ratio .* N)); ## Refine using fzero for j = 1:numel (mu1) if (mu1(j) > mu0) F0 = @(mu1arg) PowerFunction_T2 (mu0, max (mu0, mu1arg), sig, ... alpha(j), tail, N(j), ratio(j)) - desiredpower(j); else F0 = @(mu1arg) desiredpower(j) - PowerFunction_T2 (mu0, min (mu0, ... mu1arg), sig, alpha(j), tail, N(j), ratio(j)); endif mu1(j) = fzero (F0, mu1(j)); endfor endfunction ## Find alternative hypothesis parameter value P1 for variance test function p1 = findP1v (p0, desiredpower, N, alpha, tail) ## F and Finv are the cdf and inverse cdf F = @(x,n,p1) chi2cdf (x .* (n - 1) ./ p1, n - 1); # cdf for s^2 Finv = @(p,n,p1) p1 .* chi2inv (p, n - 1) ./ (n - 1); # inverse if (strcmp (tail, "both")) alpha = alpha ./ 2; endif desiredbeta = 1 - desiredpower; ## Calculate critical values and p1 for one-sided test if (! strcmp (tail, "left")) critU = Finv (1 - alpha, N, p0); p1 = 1 ./ Finv (desiredbeta, N, 1 ./ critU); endif if (! strcmp (tail, "right")) critL = Finv (alpha, N, p0); endif if (strcmp (tail, "left")) p1 = 1 ./ Finv (desiredpower, N, 1 ./ critL); endif if (strcmp (tail, "both")) ## For 2-sided test, we have the upper tail probability under H1. ## Refine by taking the other tail into account. elem = 1:numel (alpha); betahi = desiredbeta; betalo = zeros (size (desiredbeta)); while (true) ## Compute probability of being in the lower tail under H1 betalo(elem) = F (critL(elem), N(elem), p1(elem)); ## See if the upper and lower probabilities are close enough obsbeta = betahi(elem) - betalo(elem); elem = elem(abs (obsbeta - desiredbeta(elem)) > 1e-6 * desiredbeta(elem)); if (isempty (elem)) break endif ## Find a new mu1 by adjusting beta to take lower tail into account betahi(elem) = desiredbeta(elem) + betalo(elem); p1(elem) = 1 ./ Finv (betahi(elem), N(elem), 1 ./ critU(elem)); endwhile endif endfunction ## Find alternative hypothesis parameter value P1 for p test function p1 = findP1p (p0, desiredpower, N, alpha, tail) ## Get critical values [critL, critU] = getcritP (p0, N, alpha, tail); ## Use a normal approximation to find P1 values sigma = sqrt (p0 .* (1 - p0) ./ N); p1 = findP1z (p0, sigma, desiredpower, N, alpha, tail); ## Problem if we have no critical region left if (strcmp (tail, "both")) t = (critL == 0 & critU == N); elseif (strcmp (tail, "right")) t = (critU == N); else t = (critL == 0); endif if (any (t)) warning ("sampsizepwr: No Valid Parameter"); p1(t) = NaN; endif ## Force in bounds t = p1 <= 0; if (any (t(:))) p1(t) = p0 / 2; end t = p1 >= 1; if (any (t(:))) p1(t) = 1 - p0 / 2; end ## Refine using fzero for j=1:numel(p1) if (! isnan (p1(j))); if (p1(j) > p0) F0 = @(p1arg) PowerFunction_P (p0, max (p0, min (1, p1arg)), ... alpha(j), tail, N(j), critL(j), critU(j)) - desiredpower(j); else F0 = @(p1arg) desiredpower(j) - PowerFunction_P (p0, max (0, ... min (p0, p1arg)), alpha(j), tail, N(j), critL(j), critU(j)); endif p1(j) = fzero (F0, p1(j)); endif endfor endfunction ## Find alternative hypothesis parameter value P1 for r test function p1 = findP1r (p0, desiredpower, N, alpha, tail) ## Compute only for 2-tailed test if (! strcmp (tail, "both")) error ("sampsizepwr: only 2-tailed testing for regression coefficient."); endif ## Set initial search boundaries for p1 p1_lo = eps; p1_hi = 1 - eps; ## Compute initial sample size N0 according to P0, POWER and ALPHA N0 = rtestN (p0, 0, desiredpower, alpha, tail); ## Find P0 for N0 == N while (N != N0) if (N0 < N) p1_hi = p0; p1 = (p0 + p1_lo) / 2; p0 = p1; N0 = rtestN (p1, 0, desiredpower, alpha, tail); else p1_lo = p0; p1 = (p0 + p1_hi) / 2; p0 = p1; N0 = rtestN (p1, 0, desiredpower, alpha, tail); endif endwhile endfunction ## Get upper and lower critical values for binomial (p) test. function [critL, critU] = getcritP (p0, N, alpha, tail) ## For two-sided tests, this function tries to compute critical values ## favorable for p0<.5. It does this by allocating alpha/2 to the lower ## tail where the probabilities come in larger chunks, then using any ## left-over alpha, probably more than alpha/2, for the upper tail. ## Get part of alpha available for lower tail if (strcmp (tail, "both")) Alo = alpha ./ 2; elseif (strcmp (tail, "left")) Alo = alpha; else Alo = 0; endif ## Calculate critical values critU = N; critL = zeros(size(N)); if (! strcmp (tail, "right")) critL = binoinv (Alo, N, p0); Alo = binocdf (critL, N, p0); t = (critL < N) & (Alo <= alpha / 2); critL(t) = critL(t) + 1; Alo(! t) = Alo(! t) - binopdf (critL(! t), N(! t), p0); endif if (! strcmp (tail, "left")) Aup = max(0, alpha - Alo); critU = binoinv(1 - Aup, N, p0); endif endfunction ## Sample size calculation via binary search function N = searchbinaryN (F, lohi, p0, p1, desiredpower, alpha, tail) ## Find uper and lower bounds nlo = repmat(lohi(1),size(alpha)); nhi = repmat(lohi(2),size(alpha)); obspower = F(p0,p1,alpha,tail,nhi); ## Iterate on n until we achieve the desired power elem = 1:numel (alpha); while (! isempty (elem)) elem = elem(obspower(elem) < desiredpower(elem)); nhi(elem) = nhi(elem) * 2; obspower(elem) = F (p0, p1(elem), alpha(elem), tail, nhi(elem)); endwhile ## Binary search between these bounds for required sample size elem = find(nhi > nlo+1); while (! isempty (elem)) n = floor ((nhi(elem) + nlo(elem)) / 2); obspower = F (p0, p1(elem), alpha(elem), tail, n); toohigh = (obspower > desiredpower(elem)); nhi(elem(toohigh)) = n(toohigh); nlo(elem(! toohigh)) = n(! toohigh); elem = elem(nhi(elem) > nlo(elem) + 1); endwhile N = nhi; endfunction ## Adjust sample size to take discreteness into account function N = adjdiscreteN (N, PowerFunction, p0, p1, alpha, tail, power) for j=1:numel(N) allN = 1:N(j); obspower = PowerFunction (p0, p1(j), alpha(j), tail, allN); N(j) = allN(find (obspower >= power(j), 1, "first")); endfor endfunction ## Normal power calculation function power = PowerFunction_N (mu0, mu1, sig, alpha, tail, n) S = sig ./ sqrt (n); if (strcmp (tail, "both")) critL = norminv (alpha / 2, mu0, S); critU = mu0 + (mu0 - critL); power = normcdf (critL, mu1, S) + normcdf (-critU, -mu1, S); elseif (strcmp (tail, "right")) crit = mu0 + (mu0 - norminv (alpha, mu0, S)); power = normcdf (-crit, -mu1, S); else crit = norminv (alpha, mu0, S); power = normcdf (crit, mu1, S); endif endfunction ## T power calculation function power = PowerFunction_T (mu0, mu1, sig, alpha, tail, n) S = sig ./ sqrt (n); ncp = (mu1 - mu0) ./ S; if (strcmp (tail, "both")) critL = tinv (alpha / 2, n - 1); critU = -critL; power = nctcdf (critL, n - 1, ncp) + nctcdf (-critU, n - 1, -ncp); elseif (strcmp (tail, "right")) crit = tinv (1 - alpha, n - 1); power = nctcdf (-crit, n - 1, -ncp); else crit = tinv (alpha, n - 1); power = nctcdf (crit, n - 1, ncp); endif endfunction ## Two-sample T power calculation function power = PowerFunction_T2 (mu0, mu1, sig, alpha, tail, n, ratio) ncp = (mu1 - mu0) ./ (sig .* sqrt (1 ./ n + 1 ./ (ratio .* n))); if (strcmp (tail, "both")) critL = tinv (alpha / 2, n + ratio .* n - 2); critU = -critL; power = nctcdf (critL, n + ratio .* n - 2, ncp) + ... nctcdf (-critU, n + ratio .* n - 2, -ncp); elseif (strcmp (tail, "right")) crit = tinv (1 - alpha, n + ratio .* n - 2); power = nctcdf (-crit, n + ratio .* n - 2, -ncp); else crit = tinv (alpha, n + ratio .* n - 2); power = nctcdf (crit, n + ratio .* n - 2, ncp); endif endfunction ## Chi-square power calculation function power = PowerFunction_V (v0, v1, alpha, tail, n) if (strcmp (tail, "both")) critU = v0 .* chi2inv (1 - alpha / 2, n - 1); critL = v0 .* chi2inv (alpha / 2, n - 1); power = chi2cdf (critL ./ v1, n - 1) + chi2cdf (critU ./ v1, n - 1); elseif (strcmp (tail, "right")) crit = v0 .* chi2inv (1 - alpha, n - 1); power = chi2cdf (crit ./ v1, n - 1); else crit = v0 .* chi2inv (alpha, n - 1); power = chi2cdf (crit ./ v1, n - 1); endif endfunction ## Binomial power calculation function [power, critL, critU] = PowerFunction_P (p0, p1, alpha, ... tail, n, critL, critU) if (nargin < 6) [critL, critU] = getcritP (p0, n, alpha, tail); endif if (strcmp (tail, "both")) power = binocdf (critL - 1, n, p1) + 1 - binocdf (critU, n, p1); elseif (strcmp (tail, "right")) power = 1 - binocdf (critU , n, p1); else power = binocdf (critL - 1, n, p1); endif endfunction ## Regression power calculation function power = PowerFunction_R (r0, r1, alpha, tail, n) ## Compute only for 2-tailed test if (! strcmp (tail, "both")) error ("sampsizepwr: only 2-tailed testing for regression coefficient."); endif ## Set initial search boundaries for power dp_lo = eps; dp_hi = 1 - eps; power = 0.5; ## Compute initial sample size N0 according to P0, POWER and ALPHA N0 = rtestN (r0, r1, power, alpha, tail); ## Find POWER for N0 == N while (N != N0) if (N0 < N) dp_hi = power; power = (power + dp_lo) / 2; N0 = rtestN (r0, r1, power, alpha, tail); else dp_lo = power; power = (power + pd_hi) / 2; N0 = rtestN (r0, r1, power, alpha, tail); endif endwhile endfunction ## Local zero function for "t2" test function N = localfzero (F, N0, ratio) ## Set minN according to ratio if (ratio >= 2) minN = 1; else minN = 2; endif ## Return minN if function gives a value above zero if (F(minN) > 0) N = minN; return; endif ## Make sure that fzero does not try values below minN if (N0 == minN) N0 = N0 + 1; endif ## Find solution if (F(N0) > 0) N = fzero (F, [minN, N0], optimset ('TolX',1e-6)); # N0 is an upper bound else N = fzero (F, N0, optimset ('TolX',1e-6)); # N0 is a starting value endif endfunction ## Demos %!demo %! ## Compute the mean closest to 100 that can be determined to be %! ## significantly different from 100 using a t-test with a sample size %! ## of 60 and a power of 0.8. %! mu1 = sampsizepwr ("t", [100, 10], [], 0.8, 60); %! disp (mu1); %!demo %! ## Compute the sample sizes required to distinguish mu0 = 100 from %! ## mu1 = 110 by a two-sample t-test with a ratio of the larger and the %! ## smaller sample sizes of 1.5 and a power of 0.6. %! [N1,N2] = sampsizepwr ("t2", [100, 10], 110, 0.6, [], "ratio", 1.5) %!demo %! ## Compute the sample size N required to distinguish p=.26 from p=.2 %! ## with a binomial test. The result is approximate, so make a plot to %! ## see if any smaller N values also have the required power of 0.6. %! Napprox = sampsizepwr ("p", 0.2, 0.26, 0.6); %! nn = 1:250; %! pwr = sampsizepwr ("p", 0.2, 0.26, [], nn); %! Nexact = min (nn(pwr >= 0.6)); %! plot(nn,pwr,'b-', [Napprox Nexact],pwr([Napprox Nexact]),'ro'); %! grid on %!demo %! ## The company must test 52 bottles to detect the difference between a mean %! ## volume of 100 mL and 102 mL with a power of 0.80. Generate a power curve %! ## to visualize how the sample size affects the power of the test. %! %! nout = sampsizepwr('t',[100 5],102,0.80); %! nn = 1:100; %! pwrout = sampsizepwr('t',[100 5],102,[],nn); %! %! figure; %! plot (nn, pwrout, "b-", nout, 0.8, "ro") %! title ("Power versus Sample Size") %! xlabel ("Sample Size") %! ylabel ("Power") ## Input validation %!error ... %! out = sampsizepwr ([], [100, 10], [], 0.8, 60); %!error ... %! out = sampsizepwr (3, [100, 10], [], 0.8, 60); %!error ... %! out = sampsizepwr ({"t", "t2"}, [100, 10], [], 0.8, 60); %!error ... %! out = sampsizepwr ("reg", [100, 10], [], 0.8, 60); %!error ... %! out = sampsizepwr ("t", ["a", "e"], [], 0.8, 60); %!error ... %! out = sampsizepwr ("z", 100, [], 0.8, 60); %!error ... %! out = sampsizepwr ("t", 100, [], 0.8, 60); %!error ... %! out = sampsizepwr ("t2", 60, [], 0.8, 60); %!error ... %! out = sampsizepwr ("var", [100, 10], [], 0.8, 60); %!error ... %! out = sampsizepwr ("p", [100, 10], [], 0.8, 60); %!error ... %! out = sampsizepwr ("r", [100, 10], [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("z", [100, 10], [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("t", [100, 10], [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("var", 2, [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("p", 0.1, [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("r", 0.5, [], 0.8, 60); %!error ... %! out = sampsizepwr ("z", [100, 0], [], 0.8, 60); %!error ... %! out = sampsizepwr ("z", [100, -5], [], 0.8, 60); %!error ... %! out = sampsizepwr ("t", [100, 0], [], 0.8, 60); %!error ... %! out = sampsizepwr ("t", [100, -5], [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("t2", [100, 0], [], 0.8, 60); %!error ... %! [out, N1] = sampsizepwr ("t2", [100, -5], [], 0.8, 60); %!error ... %! out = sampsizepwr ("var", 0, [], 0.8, 60); %!error ... %! out = sampsizepwr ("var", -5, [], 0.8, 60); %!error ... %! out = sampsizepwr ("p", 0, [], 0.8, 60); %!error ... %! out = sampsizepwr ("p", 1.2, [], 0.8, 60); %!error ... %! out = sampsizepwr ("r", -1.5, [], 0.8, 60); %!error ... %! out = sampsizepwr ("r", -1, [], 0.8, 60); %!error ... %! out = sampsizepwr ("r", 1.2, [], 0.8, 60); %!error ... %! out = sampsizepwr ("r", 0, [], 0.8, 60); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "alpha", -0.2); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "alpha", 0); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "alpha", 1.5); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "alpha", "zero"); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "tail", 1.5); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "tail", {"both", "left"}); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "tail", "other"); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "ratio", "some"); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "ratio", 0.5); %!error ... %! out = sampsizepwr ("r", 0.2, [], 0.8, 60, "ratio", [2, 1.3, 0.3]); %!error ... %! out = sampsizepwr ("z", [100, 5], [], [], 60); %!error ... %! out = sampsizepwr ("z", [100, 5], 110, [], []); %!error ... %! out = sampsizepwr ("z", [100, 5], [], 0.8, []); %!error ... %! out = sampsizepwr ("z", [100, 5], 110, 0.8, 60); %!error ... %! out = sampsizepwr ("z", [100, 5], "mu", [], 60); %!error ... %! out = sampsizepwr ("var", 5, -1, [], 60); %!error ... %! out = sampsizepwr ("p", 0.8, 1.2, [], 60, "tail", "right"); %!error ... %! out = sampsizepwr ("r", 0.8, 1.2, [], 60); %!error ... %! out = sampsizepwr ("r", 0.8, -1.2, [], 60); %!error ... %! out = sampsizepwr ("z", [100, 5], 110, 1.2); %!error ... %! out = sampsizepwr ("z", [100, 5], 110, 0); %!error ... %! out = sampsizepwr ("z", [100, 5], 110, 0.05, [], "alpha", 0.1); %!error ... %! out = sampsizepwr ("z", [100, 5], [], [0.8, 0.7], [60, 80, 100]); %!error ... %! out = sampsizepwr ("t", [100, 5], 100, 0.8, []); %!error ... %! out = sampsizepwr ("t", [100, 5], 110, 0.8, [], "tail", "left"); %!error ... %! out = sampsizepwr ("t", [100, 5], 90, 0.8, [], "tail", "right"); ## Warning test %!warning ... %! Napprox = sampsizepwr ("p", 0.2, 0.26, 0.6); %!warning ... %! Napprox = sampsizepwr ("p", 0.30, 0.36, 0.8); ## Results validation %!test %! mu1 = sampsizepwr ("t", [100, 10], [], 0.8, 60); %! assert (mu1, 103.67704316, 1e-8); %!test %! [N1,N2] = sampsizepwr ("t2", [100, 10], 110, 0.6, [], "ratio", 1.5); %! assert (N1, 9); %! assert (N2, 14); %!test %! nn = 1:250; %! pwr = sampsizepwr ("p", 0.2, 0.26, [], nn); %! pwr_out = [0, 0.0676, 0.0176, 0.0566, 0.0181, 0.0431, 0.0802, 0.0322]; %! assert (pwr([1:8]), pwr_out, 1e-4 * ones (1,8)); %! pwr_out = [0.59275, 0.6073, 0.62166, 0.6358, 0.6497, 0.6087, 0.6229, 0.6369]; %! assert (pwr([243:end]), pwr_out, 1e-4 * ones (1,8)); %!test %! nout = sampsizepwr ("t", [100, 5], 102, 0.80); %! assert (nout, 52); %!test %! power = sampsizepwr ("t", [20, 5], 25, [], 5, "Tail", "right"); %! assert (power, 0.5797373588621888, 1e-14); %!test %! nout = sampsizepwr ("t", [20, 5], 25, 0.99, [], "Tail", "right"); %! assert (nout, 18); %!test %! p1out = sampsizepwr ("t", [20, 5], [], 0.95, 10, "Tail", "right"); %! assert (p1out, 25.65317979360237, 1e-14); %!test %! pwr = sampsizepwr ("t2", [1.4, 0.2], 1.7, [], 5, "Ratio", 2); %! assert (pwr, 0.716504004686586, 1e-14); %!test %! n = sampsizepwr ("t2", [1.4, 0.2], 1.7, 0.9, []); %! assert (n, 11); %!test %! [n1, n2] = sampsizepwr ("t2", [1.4, 0.2], 1.7, 0.9, [], "Ratio", 2); %! assert ([n1, n2], [8, 16]); statistics-release-1.7.3/inst/shadow9/000077500000000000000000000000001475240274700177015ustar00rootroot00000000000000statistics-release-1.7.3/inst/shadow9/mad.m000066400000000000000000000362751475240274700206350ustar00rootroot00000000000000## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{m} =} mad (@var{x}) ## @deftypefnx {statistics} {@var{m} =} mad (@var{x}, @var{flag}) ## @deftypefnx {statistics} {@var{m} =} mad (@var{x}, @var{flag}, @qcode{"all"}) ## @deftypefnx {statistics} {@var{m} =} mad (@var{x}, @var{flag}, @var{dim}) ## @deftypefnx {statistics} {@var{m} =} mad (@var{x}, @var{flag}, @var{vecdim}) ## ## Compute the mean or median absolute deviation (MAD). ## ## @code{mad (@var{x})} returns the mean absolute deviation of the values in ## @var{x}. @code{mad} treats NaNs as missing values and removes them. ## ## @itemize ## @item ## If @var{x} is a vector, then @code{mad} returns the mean or median absolute ## deviation of the values in @var{X}. ## @item ## If @var{x} is a matrix, then @code{mad} returns the mean or median absolute ## deviation of each column of @var{X}. ## @item ## If @var{x} is an multidimensional array, then @code{mad (@var{x})} operates ## along the first non-singleton dimension of @var{x}. ## @end itemize ## ## @code{mad (@var{x}, @var{flag})} specifies whether to compute the mean ## absolute deviation (@qcode{flag = 0}, the default) or the median absolute ## deviation (@qcode{flag = 1}). Passing an empty variable, defaults to 0. ## ## @code{mad (@var{x}, @var{flag}, @qcode{"all"})} returns the MAD of all the ## elements in @var{x}. ## ## The optional variable @var{dim} forces @code{mad} to operate over the ## specified dimension, which must be a positive integer-valued number. ## Specifying any singleton dimension in @var{x}, including any dimension ## exceeding @code{ndims (@var{x})}, will result in a MAD equal to ## @code{zeros (size (@var{x}))}, while non-finite elements are returned as NaNs. ## ## @code{mad (@var{x}, @var{flag}, @var{vecdim})} returns the MAD over the ## dimensions specified in the vector @var{vecdim}. For example, if @var{x} ## is a 2-by-3-by-4 array, then @code{mad (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the median of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to @code{mad (@var{x}, "all")}. ## Any dimension in @var{vecdim} greater than @code{ndims (@var{x})} is ignored. ## ## @seealso{median, mean, mode} ## @end deftypefn function m = mad (x, flag=0, varargin) if (nargin < 1 || nargin > 3) print_usage (); endif if (! (isnumeric (x))) error ("mad: X must be numeric."); endif ## Set initial conditions all_flag = false; perm_flag = false; vecdim_flag = false; dim = []; nvarg = numel (varargin); varg_chars = cellfun ("ischar", varargin); szx = sz_out = size (x); ndx = ndims (x); if (isempty (flag)) flag = 0; endif if (! (flag == 0 || flag == 1)) error ("mad: FLAG must be either 0 or 1."); endif ## Process optional char argument. if (any (varg_chars)) for argin = varargin(varg_chars) switch (tolower (argin{:})) case "all" all_flag = true; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif ## Process special cases for in/out size if (nvarg > 0) ## dim or vecdim provided dim = varargin{1}; vecdim_flag = ! isscalar (dim); if (! (isvector (dim) && dim > 0) || any (rem (dim, 1))) error ("mad: DIM must be a positive integer scalar or vector."); endif ## Adjust sz_out, account for possible dim > ndx by appending singletons sz_out(ndx + 1 : max (dim)) = 1; sz_out(dim(dim <= ndx)) = 1; szx(ndx + 1 : max (dim)) = 1; if (vecdim_flag) ## vecdim - try to simplify first dim = sort (dim); if (! all (diff (dim))) error ("mad: VECDIM must contain non-repeating positive integers."); endif ## dims > ndims(x) and dims only one element long don't affect mad sing_dim_x = find (szx != 1); dim(dim > ndx | szx(dim) == 1) = []; if (isempty (dim)) ## No dims left to process, return input as output m = zeros (sz_out); return; elseif (numel (dim) == numel (sing_dim_x) && unique ([dim, sing_dim_x]) == dim) ## If DIMs cover all nonsingleton ndims(x) it's equivalent to "all" ## (check lengths first to reduce unique overhead if not covered) all_flag = true; endif endif else ## Dim not provided. Determine scalar dimension. if (all_flag) ## Special case 'all': Recast input as dim1 vector, process as normal. x = x(:); szx = [length(x), 1]; dim = 1; sz_out = [1, 1]; elseif (isrow (x)) ## Special case row vector: Avoid setting dim to 1. dim = 2; sz_out = [1, 1]; elseif (ndx == 2 && szx == [0, 0]) ## Special case []: Do not apply sz_out(dim)=1 change. dim = 1; sz_out = [1, 1]; else ## General case: Set dim to first non-singleton, contract sz_out along dim (dim = find (szx != 1, 1)) || (dim = 1); sz_out(dim) = 1; endif endif if (isempty (x)) ## Empty input - output NaN m = NaN (sz_out); return; endif if (all (isnan (x)) || all (isinf (x))) ## NaN or Inf input - output NaN m = NaN (sz_out); return; endif if (szx(dim) == 1) ## Operation along singleton dimension - nothing to do m = zeros (sz_out); m(! isfinite (x)) = NaN; return; endif ## Permute dim to simplify all operations along dim1. At func. end ipermute. if (numel (dim) > 1 || (dim != 1 && ! isvector (x))) perm = 1 : ndx; if (! vecdim_flag) ## Move dim to dim 1 perm([1, dim]) = [dim, 1]; x = permute (x, perm); szx([1, dim]) = szx([dim, 1]); dim = 1; else ## Move vecdims to front perm(dim) = []; perm = [dim, perm]; x = permute (x, perm); ## Reshape all vecdims into dim1 num_dim = prod (szx(dim)); szx(dim) = []; szx = [num_dim, ones(1, numel(dim)-1), szx]; x = reshape (x, szx); dim = 1; endif perm_flag = true; endif if (isvector (x)) if (flag) # Compute median absolute deviation ## Checks above ensure either dim1 or dim2 vector x = sort (x, dim); x = x(! isnan (x)); n = length (x); k = floor ((n + 1) / 2); if (mod (n, 2)) ## odd c = x(k); v = sort (abs (x - c)); m = v(k); else ## even c = (x(k) + x(k + 1)) / 2; v = sort (abs (x - c)); m = (v(k) + v(k + 1)) / 2; endif m(sum (isinf (x)) >= 0.5 * numel (x)) = Inf; else # Compute mean absolute deviation x = x(! isnan (x)); n = length (x); m = sum (abs (x - (sum (x) / n))) / n; m(sum (isinf (x)) > 0) = Inf; endif else if (flag) # Compute median absolute deviation m = median (abs (x - median(x, dim, "omitnan")), dim, "omitnan"); m(sum (isinf (x), 1) > 0.5 * size (x, 1)) = Inf; else # Compute mean absolute deviation idx = isnan (x); n = sum (! idx, dim); x1 = x; x1(idx) = 0; m1 = sum (x1, dim) ./ n; x2 = abs (x - m1); x2(idx) = 0; m = sum (x2, dim) ./ n; m(sum (isinf (x), 1) > 0) = Inf; endif m(all (isinf (x), 1)) = NaN; endif if (perm_flag) ## Inverse permute back to correct dimensions m = ipermute (m, perm); endif endfunction %!assert (mad (1), 0) %!assert (mad (1,1), 0) %!assert (mad (1,0,3), 0) %!assert (mad (1,1,3), 0) %!assert (mad (1,[],5), 0) %!assert (mad ([1,2,3]), 2/3) %!assert (mad ([1,2,3],[]), 2/3) %!assert (mad ([1,2,3],0), 2/3) %!assert (mad ([1,2,3],1), 1) %!assert (mad ([1,2,3],0,2), 2/3) %!assert (mad ([1,2,3],[],2), 2/3) %!assert (mad ([1,2,3],1,2), 1) %!assert (mad ([1,2,3],0,1), zeros (1,3)) %!assert (mad ([1,2,3],1,1), zeros (1,3)) %!assert (mad ([1,2,3]',0,2), zeros (3,1)) %!assert (mad ([1,2,3]',1,2), zeros (3,1)) %!assert (mad ([1,2,3]',0,1), 2/3) %!assert (mad ([1,2,3]',1,1), 1) ## Test vector or matrix input with scalar DIM %!test %! A = [57, 59, 60, 100, 59, 58, 57, 58, 300, 61, 62, 60, 62, 58, 57]; %! AA = [A;2*A;3*A]; %! m0 = [38.000, 39.333, 40.000, 66.667, 39.333, 38.667, 38.000, 38.667, ... %! 200.000, 40.667, 41.333, 40.000, 41.333, 38.667, 38.000]; %! m1 = [32.569;65.138; 97.707]; %! %! assert (mad (AA), m0, 1e-3); %! assert (mad (AA,1), A); %! assert (mad (AA,1,1), A); %! assert (mad (AA,0,2), m1, 1e-3); %! assert (mad (AA,1,2), [2;4;6]); %! assert (mad (A,0,1), zeros (size (A))); %! assert (mad (A,1,1), zeros (size (A))); ## Test n-dimensional input and optional arguments "all", VECDIM %!test %! x = repmat ([2 2.1 2.2 2 NaN; 3 1 2 NaN 5; 1 1.1 1.4 5 3], [1, 1, 4]); %! m0 = repmat ([0.6667, 0.4667, 0.3111, 1.5, 1], [1, 1, 4]); %! m1 = repmat ([1, 0.1, 0.2, 1.5, 1], [1, 1, 4]); %! assert (mad (x), m0, 1e-4); %! assert (mad (x, 1), m1, 1e-14); %! assert (mad (x, [], [1, 2]), 1.0036 * ones(1,1,4), 1e-4) %! assert (mad (x, 1, [1, 2]), 0.9 * ones(1,1,4), 1e-14) %! assert (mad (x, 0, [1, 3]), m0(1,:,1), 1e-4) %! assert (mad (x, 1, [1, 3]), m1(1,:,1), 1e-14) %! assert (mad (x, 0, [2, 3]), [0.075; 1.25; 1.36], 1e-14) %! assert (mad (x, 1, [2, 3]), [0.05; 1; 0.4], 1e-14) %! assert (mad (x, 0, [1, 2, 3]) == mad (x, 0, "All")) %! assert (mad (x, 1, [1, 2, 3]) == mad (x, 1, "All")) ## Test dimension indexing with vecdim in n-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (mad (x, [], [3 2])), [10 1 1 3]); %! assert (size (mad (x, 0, [1 2])), [1 1 6 3]); %! assert (size (mad (x, 1, [1 2 4])), [1 1 6]); %! assert (size (mad (x, [], [1 4 3])), [1 40]); %! assert (size (mad (x, 1, [1 2 3 4])), [1 1]); ## Test exceeding dimensions %!assert (mad (ones (2,2), 0, 3), zeros (2,2)) %!assert (mad (ones (2,2,2), 1, 99), zeros (2,2,2)) %!assert (mad (magic (3), 1, 3), zeros (3)) %!assert (mad (magic (3), 1, [1 3]), [1, 4, 1]) %!assert (mad (magic (3), 1, [1 99]), [1, 4, 1]) ## Test empty, NaN, Inf inputs %!assert (mad ([]), NaN) %!assert (mad ([], 1), NaN) %!assert (mad (NaN), NaN) %!assert (mad (NaN, 1), NaN) %!assert (mad (Inf), NaN) %!assert (mad (Inf, 1), NaN) %!assert (mad (-Inf), NaN) %!assert (mad (-Inf, 1), NaN) %!assert (mad ([-Inf Inf]), NaN) %!assert (mad ([-Inf Inf], 1), NaN) %!assert (mad ([3 Inf]), Inf) %!assert (mad ([3 4 Inf]), Inf) %!assert (mad ([Inf 3 4]), Inf) %!assert (mad ([Inf 3 Inf]), Inf) %!assert (mad ([Inf 3 Inf], 1), Inf) %!assert (mad ([1 2; 3 Inf]), [1 Inf]) %!assert (mad ([1 2; 3 Inf], 1), [1 Inf]) %!assert (mad ([3, 4, Inf]), Inf) %!assert (mad ([Inf, 3, 4]), Inf) %!assert (mad ([3, 4, Inf], 0), Inf) %!assert (mad ([3, 4, Inf], 0, 1), [0, 0, NaN]) %!assert (mad ([3, 4, Inf], 0, 2), Inf) %!assert (mad ([3, 4, Inf], 0, 3), [0, 0, NaN]) %!assert (mad ([3, 4, Inf]', 0), Inf) %!assert (mad ([3, 4, Inf]', 0, 1), Inf) %!assert (mad ([3, 4, Inf]', 0, 2), [0; 0; NaN]) %!assert (mad ([3, 4, Inf]', 0, 3), [0; 0; NaN]) %!assert (mad ([Inf, 3, 4], 1), 1) %!assert (mad ([3, 4, Inf], 1), 1) %!assert (mad ([3, 4, Inf], 1, 1), [0, 0, NaN]) %!assert (mad ([3, 4, Inf], 1, 2), 1) %!assert (mad ([3, 4, Inf], 1, 3), [0, 0, NaN]) %!assert (mad ([3, 4, Inf]', 1), 1) %!assert (mad ([3, 4, Inf]', 1, 1), 1) %!assert (mad ([3, 4, Inf]', 1, 2), [0; 0; NaN]) %!assert (mad ([3, 4, Inf]', 1, 3), [0; 0; NaN]) %!assert (mad ([3, Inf, Inf], 1), Inf) %!assert (mad ([3, 4, 5, Inf], 1), 1) %!assert (mad ([3, 4, Inf, Inf], 1), Inf) %!assert (mad ([3, Inf, Inf, Inf], 1), Inf) %!assert (mad ([1, 2; 3, 4; Inf, Inf], 0), [Inf, Inf]) %!assert (mad ([1, 2; 3, 4; Inf, Inf], 1), [2, 2]) %!assert (mad ([1, 2; 3, Inf; Inf, Inf], 1), [2, Inf]) %!assert (mad ([1, 2; 3, 4; 5, 6; Inf, Inf], 1), [2, 2]) %!assert (mad ([1, 2; 3, 4; 5, Inf; Inf, Inf], 1), [2, Inf]) %!assert (mad ([Inf, 2; Inf, 4; Inf, Inf], 0), [NaN, Inf]) %!assert (mad ([Inf, 2; Inf, 4; Inf, Inf], 1), [NaN, 2]) %!assert (mad ([]), NaN) %!assert (mad (ones(1,0)), NaN) %!assert (mad (ones(0,1)), NaN) %!assert (mad ([], 0, 1), NaN(1,0)) %!assert (mad ([], 0, 2), NaN(0,1)) %!assert (mad ([], 0, 3), NaN(0,0)) %!assert (mad (ones(1,0), 0, 1), NaN(1,0)) %!assert (mad (ones(1,0), 0, 2), NaN(1,1)) %!assert (mad (ones(1,0), 0, 3), NaN(1,0)) %!assert (mad (ones(0,1), 0, 1), NaN(1,1)) %!assert (mad (ones(0,1), 0, 2), NaN(0,1)) %!assert (mad (ones(0,1), 0, 3), NaN(0,1)) %!assert (mad (ones(0,1,0,1), 0, 1), NaN(1,1,0)) %!assert (mad (ones(0,1,0,1), 0, 2), NaN(0,1,0)) %!assert (mad (ones(0,1,0,1), 0, 3), NaN(0,1,1)) %!assert (mad (ones(0,1,0,1), 0, 4), NaN(0,1,0)) ## Test complex inputs (should sort by abs(a)) %!assert (mad([1 3 3i 2 1i]), 1.5297, 1e-4) %!assert (mad([1 3 3i 2 1i], 1), 1) %!assert (mad([1 2 4i; 3 2i 4]), [1, 1.4142, 2.8284], 1e-4) %!assert (mad([1 2 4i; 3 2i 4], 1), [1, 1.4142, 2.8284], 1e-4) %!assert (mad([1 2 4i; 3 2i 4], 1, 2), [1; 1]) %!assert (mad([1 2 4i; 3 2i 4], 0, 2), [1.9493; 1.8084], 1e-4) ## Test all-inf handling %!assert <*65405> (mad ([-Inf Inf]), NaN) %!assert <*65405> (mad ([-Inf Inf], 0), NaN) %!assert <*65405> (mad ([-Inf Inf], 1), NaN) %!assert <*65405> (mad ([-Inf Inf]', 0), NaN) %!assert <*65405> (mad ([-Inf Inf]', 1), NaN) %!assert <*65405> (mad ([-Inf Inf]', 0, 1), NaN) %!assert <*65405> (mad ([-Inf Inf]', 0, 2), [NaN; NaN]) %!assert <*65405> (mad ([-Inf Inf]', 0, 3), [NaN; NaN]) %!assert <*65405> (mad ([-Inf Inf]', 1, 1), NaN) %!assert <*65405> (mad ([-Inf Inf]', 1, 2), [NaN; NaN]) %!assert <*65405> (mad ([-Inf Inf]', 1, 3), [NaN; NaN]) %!assert <*65405> (mad (Inf(2), 0), [NaN, NaN]) %!assert <*65405> (mad (Inf(2), 1), [NaN, NaN]) %!assert <*65405> (mad (Inf(2), 0, 1), [NaN, NaN]) %!assert <*65405> (mad (Inf(2), 0, 2), [NaN; NaN]) %!assert <*65405> (mad (Inf(2), 0, 3), NaN(2)) %!assert <*65405> (mad (Inf(2), 1, 1), [NaN, NaN]) %!assert <*65405> (mad (Inf(2), 1, 2), [NaN; NaN]) %!assert <*65405> (mad (Inf(2), 1, 3), NaN(2)) ## Test input case insensitivity %!assert (mad ([1 2 3], 0, "aLL"), 2/3) %!assert (mad ([1 2 3], 1, "aLL"), 1) ## Test input validation %!error mad () %!error mad (1, 2, 3, 4) %!error mad ("text") %!error mad ({2 3 4}) %!error mad (1, "all", 3) %!error mad (1, "b") %!error mad (1, 1, "foo") %!error mad (1, [] ,ones (2,2)) %!error mad (1, [], 1.5) %!error mad (1, [], 0) %!error mad ([1 2 3], [], [-1 1]) %!error mad(1, [], [1 2 2]) statistics-release-1.7.3/inst/shadow9/mean.m000066400000000000000000000517101475240274700210030ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## Copyright (C) 2022 Kai Torben Ohlhus ## Copyright (C) 2023 Nicholas Jankowski ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{m} =} mean (@var{x}) ## @deftypefnx {statistics} {@var{m} =} mean (@var{x}, "all") ## @deftypefnx {statistics} {@var{m} =} mean (@var{x}, @var{dim}) ## @deftypefnx {statistics} {@var{m} =} mean (@var{x}, @var{vecdim}) ## @deftypefnx {statistics} {@var{m} =} mean (@dots{}, @var{outtype}) ## @deftypefnx {statistics} {@var{m} =} mean (@dots{}, @var{nanflag}) ## ## Compute the mean of the elements of @var{x}. ## ## @itemize ## @item ## If @var{x} is a vector, then @code{mean(@var{x})} returns the ## mean of the elements in @var{x} defined as ## @tex ## $$ {\rm mean}(x) = \bar{x} = {1\over N} \sum_{i=1}^N x_i $$ ## ## @end tex ## @ifnottex ## ## @example ## mean (@var{x}) = SUM_i @var{x}(i) / N ## @end example ## ## @end ifnottex ## @noindent ## where @math{N} is the length of the @var{x} vector. ## ## @item ## If @var{x} is a matrix, then @code{mean(@var{x})} returns a row vector ## with the mean of each columns in @var{x}. ## ## @item ## If @var{x} is a multidimensional array, then @code{mean(@var{x})} ## operates along the first nonsingleton dimension of @var{x}. ## @end itemize ## ## @code{mean (@var{x}, @var{dim})} returns the mean along the operating ## dimension @var{dim} of @var{x}. For @var{dim} greater than ## @code{ndims (@var{x})}, then @var{m} = @var{x}. ## ## @code{mean (@var{x}, @var{vecdim})} returns the mean over the ## dimensions specified in the vector @var{vecdim}. For example, if @var{x} ## is a 2-by-3-by-4 array, then @code{mean (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the mean of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to @code{mean (@var{x}, "all")}. ## Any dimension in @var{vecdim} greater than @code{ndims (@var{x})} is ignored. ## ## @code{mean (@var{x}, "all")} returns the mean of all the elements in @var{x}. ## The optional flag "all" cannot be used together with @var{dim} or ## @var{vecdim} input arguments. ## ## @code{mean (@dots{}, @var{outtype})} returns the mean with a specified data ## type, using any of the input arguments in the previous syntaxes. ## @var{outtype} can take the following values: ## @itemize ## @item "default" ## Output is of type double, unless the input is single in which case the output ## is of type single. ## ## @item "double" ## Output is of type double. ## ## @item "native". ## Output is of the same type as the input (@code{class (@var{x})}), unless the ## input is logical in which case the output is of type double or a character ## array in which case an error is produced. ## @end itemize ## ## @code{mean (@dots{}, @var{nanflag})} specifies whether to exclude NaN values ## from the calculation, using any of the input argument combinations in ## previous syntaxes. By default, NaN values are included in the calculation ## (@var{nanflag} has the value "includenan"). To exclude NaN values, set the ## value of @var{nanflag} to "omitnan". ## ## @seealso{trimmean, median, mad, mode} ## @end deftypefn function m = mean (x, varargin) if (nargin < 1 || nargin > 4) print_usage (); endif ## Set initial conditions all_flag = false; omitnan = false; out_flag = false; nvarg = numel (varargin); varg_chars = cellfun ("ischar", varargin); outtype = "default"; szx = size (x); ndx = ndims (x); if (nvarg > 1 && ! varg_chars(2:end)) ## Only first varargin can be numeric print_usage (); endif ## Process any other char arguments. if (any (varg_chars)) for argin = varargin(varg_chars) switch (lower (argin{:})) case "all" all_flag = true; case "omitnan" omitnan = true; case "includenan" omitnan = false; case "default" if (out_flag) error ("mean: only one OUTTYPE can be specified.") endif if (isa (x, "single")) outtype = "single"; else outtype = "double"; endif out_flag = true; case "native" outtype = class (x); if (out_flag) error ("mean: only one OUTTYPE can be specified.") elseif (strcmp (outtype, "logical")) outtype = "double"; elseif (strcmp (outtype, "char")) error ("mean: OUTTYPE 'native' cannot be used with char type inputs."); endif out_flag = true; case "double" if (out_flag) error ("mean: only one OUTTYPE can be specified.") endif outtype = "double"; out_flag = true; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif if (strcmp (outtype, "default")) if (isa (x, "single")) outtype = "single"; else outtype = "double"; endif endif if ((nvarg > 1) || ((nvarg == 1) && ! (isnumeric (varargin{1})))) ## After trimming char inputs can only be one varargin left, must be numeric print_usage (); endif if (! (isnumeric (x) || islogical (x) || ischar (x))) error ("mean: X must be either a numeric, boolean, or character array."); endif ## Process special cases for input/output sizes if (nvarg == 0) ## Single numeric input argument, no dimensions given. if (all_flag) x = x(:); if (omitnan) x = x(! isnan (x)); endif if (any (isa (x, {"int64", "uint64"}))) m = int64_mean (x, 1, numel (x), outtype); else m = sum (x, "double") ./ numel (x); endif else ## Find the first non-singleton dimension. (dim = find (szx != 1, 1)) || (dim = 1); n = szx(dim); if (omitnan) idx = isnan (x); n = sum (! idx, dim); x(idx) = 0; endif if (any (isa (x, {"int64", "uint64"}))) m = int64_mean (x, dim, n, outtype); else m = sum (x, dim, "double") ./ n; endif endif else ## Two numeric input arguments, dimensions given. Note scalar is vector! vecdim = varargin{1}; if (isempty (vecdim) || ! (isvector (vecdim) && all (vecdim > 0) && all (rem (vecdim, 1)==0))) error ("mean: DIM must be a positive integer scalar or vector."); endif if (ndx == 2 && isempty (x) && szx == [0,0]) ## FIXME: This special case handling could be removed once sum ## compatibly handles all sizes of empty inputs. sz_out = szx; sz_out (vecdim(vecdim <= ndx)) = 1; m = NaN (sz_out); else if (isscalar (vecdim)) if (vecdim > ndx) m = x; else n = szx(vecdim); if (omitnan) nanx = isnan (x); n = sum (! nanx, vecdim); x(nanx) = 0; endif if (any (isa (x, {"int64", "uint64"}))) m = int64_mean (x, vecdim, n, outtype); else m = sum (x, vecdim, "double") ./ n; endif endif else vecdim = sort (vecdim); if (! all (diff (vecdim))) error ("mean: VECDIM must contain non-repeating positive integers."); endif ## Ignore dimensions in VECDIM larger than actual array vecdim(find (vecdim > ndims (x))) = []; if (isempty (vecdim)) m = x; else ## Calculate permutation vector remdims = 1 : ndx; # All dimensions remdims(vecdim) = []; # Delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is equivalent to 'all' flag if (nremd == 0) x = x(:); if (omitnan) x = x(! isnan (x)); endif if (any (isa (x, {"int64", "uint64"}))) m = int64_mean (x, 1, numel (x), outtype); else m = sum (x, "double") ./ numel (x); endif else ## Permute to push vecdims to back perm = [remdims, vecdim]; x = permute (x, perm); ## Reshape to squash all vecdims in final dimension sznew = [szx(remdims), prod(szx(vecdim))]; x = reshape (x, sznew); ## Calculate mean on final dimension dim = nremd + 1; if (omitnan) nanx = isnan (x); x(nanx) = 0; n = sum (! nanx, dim); else n = sznew(dim); endif if (any (isa (x, {"int64", "uint64"}))) m = int64_mean (x, dim, n, outtype); else m = sum (x, dim, "double") ./ n; endif ## Inverse permute back to correct dimensions m = ipermute (m, perm); endif endif endif endif endif ## Convert output if necessary if (! strcmp (class (m), outtype)) if (! islogical (x)) m = feval (outtype, m); endif endif endfunction function m = int64_mean (x, dim, n, outtype) ## Avoid int overflow in large ints. Smaller ints processed as double ## avoids overflow. Large int64 values as double can have floating pt error. ## Use integer math and remainder correction to avoid this. if (any (abs (x(:)) >= flintmax / n)) rmdr = double (rem (x, n)) / n; rmdr_hilo = logical (int8 (rmdr)); # Integer rounding direction indicator ## Native int summation to prevent double precision error, ## then add back in lost round-up/down remainders. m = sum (x/n, dim, "native"); ## rmdr.*!rmdr_hilo = remainders that were rounded down in abs val ## signs retained, can be summed and added back. ## rmdr.*rmdr_hilo = remainders that were rounded up in abs val. ## need to add back difference between 1 and rmdr, retaining sign. rmdr = sum (rmdr .* !rmdr_hilo, dim) - ... sum ((1 - abs (rmdr)) .* rmdr_hilo .* sign(rmdr), dim); if (any (abs (m(:)) >= flintmax)) if (any (strcmp (outtype, {"int64", "uint64"}))) m += rmdr; else m = double (m) + rmdr; endif else m = double(m) + rmdr; switch (outtype) case "int64" m = int64 (m); case "uint64" m = uint64 (m); endswitch endif else m = double (sum (x, dim, "native")) ./ n; endif endfunction %!test %! x = -10:10; %! y = x'; %! z = [y, y+10]; %! assert (mean (x), 0); %! assert (mean (y), 0); %! assert (mean (z), [0, 10]); %!assert (mean (magic (3), 1), [5, 5, 5]) %!assert (mean (magic (3), 2), [5; 5; 5]) %!assert (mean (logical ([1 0 1 1])), 0.75) %!assert (mean (single ([1 0 1 1])), single (0.75)) %!assert (mean ([1 2], 3), [1 2]) ## Test outtype option %!test %! in = [1 2 3]; %! out = 2; %! assert (mean (in, "default"), mean (in)); %! assert (mean (in, "default"), out); %! assert (mean (in, "double"), out); %! assert (mean (in, "native"), out); %!test %! in = single ([1 2 3]); %! out = 2; %! assert (mean (in, "default"), mean (in)); %! assert (mean (in, "default"), single (out)); %! assert (mean (in, "double"), out); %! assert (mean (in, "native"), single (out)); %!test %! in = logical ([1 0 1]); %! out = 2/3; %! assert (mean (in, "default"), mean (in), eps); %! assert (mean (in, "default"), out, eps); %! assert (mean (in, "double"), out, eps); %! assert (mean (in, "native"), out, eps); %!test %! in = char ("ab"); %! out = 97.5; %! assert (mean (in, "default"), mean (in), eps); %! assert (mean (in, "default"), out, eps); %! assert (mean (in, "double"), out, eps); %!test %! in = uint8 ([1 2 3]); %! out = 2; %! assert (mean (in, "default"), mean (in)); %! assert (mean (in, "default"), out); %! assert (mean (in, "double"), out); %! assert (mean (in, "native"), uint8 (out)); %!test %! in = uint8 ([0 1 2 3]); %! out = 1.5; %! out_u8 = 2; %! assert (mean (in, "default"), mean (in), eps); %! assert (mean (in, "default"), out, eps); %! assert (mean (in, "double"), out, eps); %! assert (mean (in, "native"), uint8 (out_u8)); %! assert (class (mean (in, "native")), "uint8"); %!test # internal sum exceeding intmax %! in = uint8 ([3 141 141 255]); %! out = 135; %! assert (mean (in, "default"), mean (in)); %! assert (mean (in, "default"), out); %! assert (mean (in, "double"), out); %! assert (mean (in, "native"), uint8 (out)); %! assert (class (mean (in, "native")), "uint8"); %!test # fractional answer with internal sum exceeding intmax %! in = uint8 ([1 141 141 255]); %! out = 134.5; %! out_u8 = 135; %! assert (mean (in, "default"), mean (in)); %! assert (mean (in, "default"), out); %! assert (mean (in, "double"), out); %! assert (mean (in, "native"), uint8 (out_u8)); %! assert (class (mean (in, "native")), "uint8"); %!test <54567> # large int64 sum exceeding intmax and double precision limit %! in_same = uint64 ([intmax("uint64") intmax("uint64")-2]); %! out_same = intmax ("uint64")-1; %! in_opp = int64 ([intmin("int64"), intmax("int64")-1]); %! out_opp = -1; %! in_neg = int64 ([intmin("int64") intmin("int64")+2]); %! out_neg = intmin ("int64")+1; %! %! ## both positive %! assert (mean (in_same, "default"), mean (in_same)); %! assert (mean (in_same, "default"), double (out_same)); %! assert (mean (in_same, "double"), double (out_same)); %! assert (mean (in_same, "native"), uint64 (out_same)); %! assert (class (mean (in_same, "native")), "uint64"); %! %! ## opposite signs %! assert (mean (in_opp, "default"), mean (in_opp)); %! assert (mean (in_opp, "default"), double (out_opp)); %! assert (mean (in_opp, "double"), double (out_opp)); %! assert (mean (in_opp, "native"), int64 (out_opp)); %! assert (class (mean (in_opp, "native")), "int64"); %! %! ## both negative %! assert (mean (in_neg, "default"), mean (in_neg)); %! assert (mean (in_neg, "default"), double(out_neg)); %! assert (mean (in_neg, "double"), double(out_neg)); %! assert (mean (in_neg, "native"), int64(out_neg)); %! assert (class (mean (in_neg, "native")), "int64"); ## Additional tests int64 and double precision limits %!test <54567> %! in = [(intmin('int64')+5), (intmax('int64'))-5]; %! assert (mean (in, "native"), int64(-1)); %! assert (class (mean (in, "native")), "int64"); %! assert (mean (double(in)), double(0) ); %! assert (mean (in), double(-0.5) ); %! assert (mean (in, "default"), double(-0.5) ); %! assert (mean (in, "double"), double(-0.5) ); %! assert (mean (in, "all", "native"), int64(-1)); %! assert (mean (in, 2, "native"), int64(-1)); %! assert (mean (in, [1 2], "native"), int64(-1)); %! assert (mean (in, [2 3], "native"), int64(-1)); %! assert (mean ([intmin("int64"), in, intmax("int64")]), double(-0.5)) %! assert (mean ([in; int64([1 3])], 2, "native"), int64([-1; 2])); ## Test input and optional arguments "all", DIM, "omitnan". %!test %! x = [-10:10]; %! y = [x;x+5;x-5]; %! assert (mean (x), 0); %! assert (mean (y, 2), [0, 5, -5]'); %! assert (mean (y, "all"), 0); %! y(2,4) = NaN; %! assert (mean (y', "omitnan"), [0 5.35 -5]); %! z = y + 20; %! assert (mean (z, "all"), NaN); %! assert (mean (z, "all", "includenan"), NaN); %! assert (mean (z, "all", "omitnan"), 20.03225806451613, 4e-14); %! m = [20 NaN 15]; %! assert (mean (z'), m); %! assert (mean (z', "includenan"), m); %! m = [20 25.35 15]; %! assert (mean (z', "omitnan"), m); %! assert (mean (z, 2, "omitnan"), m'); %! assert (mean (z, 2, "native", "omitnan"), m'); %! assert (mean (z, 2, "omitnan", "native"), m'); ## Test boolean input %!test %! assert (mean (true, "all"), 1); %! assert (mean (false), 0); %! assert (mean ([true false true]), 2/3, 4e-14); %! assert (mean ([true false true], 1), [1 0 1]); %! assert (mean ([true false NaN], 1), [1 0 NaN]); %! assert (mean ([true false NaN], 2), NaN); %! assert (mean ([true false NaN], 2, "omitnan"), 0.5); %! assert (mean ([true false NaN], 2, "omitnan", "native"), 0.5); ## Test char inputs %!assert (mean ("abc"), double (98)) %!assert (mean ("ab"), double (97.5), eps) %!assert (mean ("abc", "double"), double (98)) %!assert (mean ("abc", "default"), double (98)) ## Test NaN inputs %!test %! x = magic (4); %! x([2, 9:12]) = NaN; %! assert (mean (x), [NaN 8.5, NaN, 8.5], eps); %! assert (mean (x,1), [NaN 8.5, NaN, 8.5], eps); %! assert (mean (x,2), NaN(4,1), eps); %! assert (mean (x,3), x, eps); %! assert (mean (x, 'omitnan'), [29/3, 8.5, NaN, 8.5], eps); %! assert (mean (x, 1, 'omitnan'), [29/3, 8.5, NaN, 8.5], eps); %! assert (mean (x, 2, 'omitnan'), [31/3; 9.5; 28/3; 19/3], eps); %! assert (mean (x, 3, 'omitnan'), x, eps); ## Test empty inputs %!assert (mean ([]), NaN(1,1)) %!assert (mean (single([])), NaN(1,1,"single")) %!assert (mean ([], 1), NaN(1,0)) %!assert (mean ([], 2), NaN(0,1)) %!assert (mean ([], 3), NaN(0,0)) %!assert (mean (ones(1,0)), NaN(1,1)) %!assert (mean (ones(1,0), 1), NaN(1,0)) %!assert (mean (ones(1,0), 2), NaN(1,1)) %!assert (mean (ones(1,0), 3), NaN(1,0)) %!assert (mean (ones(0,1)), NaN(1,1)) %!assert (mean (ones(0,1), 1), NaN(1,1)) %!assert (mean (ones(0,1), 2), NaN(0,1)) %!assert (mean (ones(0,1), 3), NaN(0,1)) %!assert (mean (ones(0,1,0)), NaN(1,1,0)) %!assert (mean (ones(0,1,0), 1), NaN(1,1,0)) %!assert (mean (ones(0,1,0), 2), NaN(0,1,0)) %!assert (mean (ones(0,1,0), 3), NaN(0,1,1)) %!assert (mean (ones(0,0,1,0)), NaN(1,0,1,0)) %!assert (mean (ones(0,0,1,0), 1), NaN(1,0,1,0)) %!assert (mean (ones(0,0,1,0), 2), NaN(0,1,1,0)) %!assert (mean (ones(0,0,1,0), 3), NaN(0,0,1,0)) ## Test dimension indexing with vecdim in N-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (mean (x, [3 2])), [10 1 1 3]); %! assert (size (mean (x, [1 2])), [1 1 6 3]); %! assert (size (mean (x, [1 2 4])), [1 1 6]); %! assert (size (mean (x, [1 4 3])), [1 40]); %! assert (size (mean (x, [1 2 3 4])), [1 1]); ## Test exceeding dimensions %!assert (mean (ones (2,2), 3), ones (2,2)) %!assert (mean (ones (2,2,2), 99), ones (2,2,2)) %!assert (mean (magic (3), 3), magic (3)) %!assert (mean (magic (3), [1 3]), [5, 5, 5]) %!assert (mean (magic (3), [1 99]), [5, 5, 5]) ## Test results with vecdim in N-dimensional arrays and "omitnan" %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! m = repmat ([10.5;15.5], [5 1 1 3]); %! assert (mean (x, [3 2]), m, 4e-14); %! x(2,5,6,3) = NaN; %! m(2,1,1,3) = NaN; %! assert (mean (x, [3 2]), m, 4e-14); %! m(2,1,1,3) = 15.52301255230125; %! assert (mean (x, [3 2], "omitnan"), m, 4e-14); ## Test input case insensitivity %!assert (mean ([1 2 3], "aLL"), 2) %!assert (mean ([1 2 3], "OmitNan"), 2) %!assert (mean ([1 2 3], "DOUBle"), 2) ## Test limits of single precision summation limits on each code path %!assert <*63848> (mean (ones (80e6, 1, "single")), 1, eps) %!assert <*63848> (mean (ones (80e6, 1, "single"), "all"), 1, eps) %!assert <*63848> (mean (ones (80e6, 1, "single"), 1), 1, eps) %!assert <*63848> (mean (ones (80e6, 1, "single"), [1 2]), 1, eps) %!assert <*63848> (mean (ones (80e6, 1, "single"), [1 3]), 1, eps) ## Test limits of double precision summation %!assert <63848> (mean ([flintmax("double"), ones(1, 2^8-1, "double")]), ... %! 35184372088833-1/(2^8), eps(35184372088833)) ## Test input validation %!error mean () %!error mean (1, 2, 3) %!error mean (1, 2, 3, 4) %!error mean (1, "all", 3) %!error mean (1, "b") %!error mean (1, 1, "foo") %!error mean ("abc", "native") %!error mean ({1:5}) %!error mean (1, ones (2,2)) %!error mean (1, 1.5) %!error mean (1, 0) %!error mean (1, []) %!error mean (1, -1) %!error mean (1, -1.5) %!error mean (1, NaN) %!error mean (1, Inf) %!error mean (repmat ([1:20;6:25], [5 2]), -1) %!error mean (repmat ([1:5;5:9], [5 2]), [1 -1]) %!error mean (1, ones(1,0)) %!error mean (1, [2 2]) statistics-release-1.7.3/inst/shadow9/median.m000066400000000000000000000630111475240274700213150ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## Copyright (C) 2023 Nicholas Jankowski ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{m} =} median (@var{x}) ## @deftypefnx {statistics} {@var{m} =} median (@var{x}, "all") ## @deftypefnx {statistics} {@var{m} =} median (@var{x}, @var{dim}) ## @deftypefnx {statistics} {@var{m} =} median (@var{x}, @var{vecdim}) ## @deftypefnx {statistics} {@var{m} =} median (@dots{}, @var{outtype}) ## @deftypefnx {statistics} {@var{m} =} median (@dots{}, @var{nanflag}) ## ## Compute the median value of the elements of @var{x}. ## ## When the elements of @var{x} are sorted, say ## @code{@var{s} = sort (@var{x})}, the median is defined as ## @tex ## $$ {\rm median} (x) = \cases{s(\lceil N/2\rceil), & $N$ odd; ## \cr (s(N/2)+s(N/2+1))/2, & $N$ even.} $$ ## ## @end tex ## @ifnottex ## ## @example ## @group ## | @var{s}(ceil (N/2)) N odd ## median (@var{x}) = | ## | (@var{s}(N/2) + @var{s}(N/2+1))/2 N even ## @end group ## @end example ## ## @end ifnottex ## @noindent ## where @math{N} is the number of elements of @var{x}. ## ## If @var{x} is an array, then @code{median (@var{x})} operates along the first ## non-singleton dimension of @var{x}. ## ## The optional variable @var{dim} forces @code{median} to operate over the ## specified dimension, which must be a positive integer-valued number. ## Specifying any singleton dimension in @var{x}, including any dimension ## exceeding @code{ndims (@var{x})}, will result in a median equal to @var{x}. ## ## @code{median (@var{x}, @var{vecdim})} returns the median over the ## dimensions specified in the vector @var{vecdim}. For example, if @var{x} ## is a 2-by-3-by-4 array, then @code{median (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the median of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to ## @code{median (@var{x}, "all")}. Any dimension in @var{vecdim} greater than ## @code{ndims (@var{x})} is ignored. ## ## @code{median (@var{x}, "all")} returns the median of all the elements in ## @var{x}. The optional flag "all" cannot be used together with @var{dim} or ## @var{vecdim} input arguments. ## ## @code{median (@dots{}, @var{outtype})} returns the median with a specified ## data type, using any of the input arguments in the previous syntaxes. ## @var{outtype} can take the following values: ## @itemize ## @item @qcode{"default"} ## Output is of type double, unless the input is single in which case the ## output is of type single. ## ## @item @qcode{"double"} ## Output is of type double. ## ## @item @qcode{"native"} ## Output is of the same type as the input (@code{class (@var{x})}), unless the ## input is logical in which case the output is of type double. ## @end itemize ## ## The optional variable @var{nanflag} specifies whether to include or exclude ## NaN values from the calculation using any of the previously specified input ## argument combinations. The default value for @var{nanflag} is ## @qcode{"includenan"} which keeps NaN values in the calculation. To ## exclude NaN values set the value of @var{nanflag} to @qcode{"omitnan"}. ## The output will still contain NaN values if @var{x} consists of all NaN ## values in the operating dimension. ## ## @seealso{mean, mad, mode} ## @end deftypefn function m = median (x, varargin) if (nargin < 1 || nargin > 4) print_usage (); endif if (! (isnumeric (x) || islogical (x))) error ("median: X must be either numeric or logical."); endif ## Set initial conditions all_flag = false; omitnan = false; perm_flag = false; out_flag = false; vecdim_flag = false; dim = []; nvarg = numel (varargin); varg_chars = cellfun ("ischar", varargin); szx = sz_out = size (x); ndx = ndims (x); outtype = class (x); if (nvarg > 1 && ! varg_chars(2:end)) ## Only first varargin can be numeric print_usage (); endif ## Process any other char arguments. if (any (varg_chars)) for argin = varargin(varg_chars) switch (tolower (argin{:})) case "all" all_flag = true; case "omitnan" omitnan = true; case "includenan" omitnan = false; case "native" if (out_flag) error ("median: only one OUTTYPE can be specified.") endif if (strcmp (outtype, "logical")) outtype = "double"; endif out_flag = true; case "default" if (out_flag) error ("median: only one OUTTYPE can be specified.") endif if (! strcmp (outtype, "single")) outtype = "double"; endif out_flag = true; case "double" if (out_flag) error ("median: only one OUTTYPE can be specified.") endif outtype = "double"; out_flag = true; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif if ((nvarg == 1 && ! isnumeric (varargin{1})) || nvarg > 1) ## After trimming char inputs should only be one numeric varargin left print_usage (); endif ## Process special cases for in/out size if (nvarg > 0) ## dim or vecdim provided if (all_flag) error ("median: 'all' cannot be used with DIM or VECDIM options."); endif dim = varargin{1}; vecdim_flag = ! isscalar (dim); if (! (isvector (dim) && dim > 0) || any (rem (dim, 1))) error ("median: DIM must be a positive integer scalar or vector."); endif ## Adjust sz_out, account for possible dim > ndx by appending singletons sz_out(ndx + 1 : max (dim)) = 1; sz_out(dim(dim <= ndx)) = 1; szx(ndx + 1 : max (dim)) = 1; if (vecdim_flag) ## vecdim - try to simplify first dim = sort (dim); if (! all (diff (dim))) error ("median: VECDIM must contain non-repeating positive integers."); endif ## dims > ndims(x) and dims only one element long don't affect median sing_dim_x = find (szx != 1); dim(dim > ndx | szx(dim) == 1) = []; if (isempty (dim)) ## No dims left to process, return input as output if (! strcmp (class (x), outtype)) m = feval (outtype, x); # convert to outtype else m = x; endif return; elseif (numel (dim) == numel (sing_dim_x) && unique ([dim, sing_dim_x]) == dim) ## If DIMs cover all nonsingleton ndims(x) it's equivalent to "all" ## (check lengths first to reduce unique overhead if not covered) all_flag = true; endif endif else ## Dim not provided. Determine scalar dimension. if (all_flag) ## Special case 'all': Recast input as dim1 vector, process as normal. x = x(:); szx = [length(x), 1]; dim = 1; sz_out = [1 1]; elseif (isrow (x)) ## Special case row vector: Avoid setting dim to 1. dim = 2; sz_out = [1, 1]; elseif (ndx == 2 && szx == [0, 0]) ## Special case []: Do not apply sz_out(dim)=1 change. dim = 1; sz_out = [1, 1]; else ## General case: Set dim to first non-singleton, contract sz_out along dim (dim = find (szx != 1, 1)) || (dim = 1); sz_out(dim) = 1; endif endif if (isempty (x)) ## Empty input - output NaN or class equivalent in pre-determined size switch (outtype) case {"double", "single"} m = NaN (sz_out, outtype); case ("logical") m = false (sz_out); otherwise m = cast (NaN (sz_out), outtype); endswitch return; endif if (all (isnan (x)(:))) ## all NaN input, output single or double NaNs in pre-determined size m = NaN(sz_out, outtype); return endif if (szx(dim) == 1) ## Operation along singleton dimension - nothing to do if (! strcmp (class (x), outtype)) m = feval (outtype, x); # convert to outtype else m = x; endif return; endif ## Permute dim to simplify all operations along dim1. At func. end ipermute. if (numel (dim) > 1 || (dim != 1 && ! isvector (x))) perm = 1 : ndx; if (! vecdim_flag) ## Move dim to dim 1 perm([1, dim]) = [dim, 1]; x = permute (x, perm); szx([1, dim]) = szx([dim, 1]); dim = 1; else ## Move vecdims to front perm(dim) = []; perm = [dim, perm]; x = permute (x, perm); ## Reshape all vecdims into dim1 num_dim = prod (szx(dim)); szx(dim) = []; szx = [num_dim, ones(1, numel(dim)-1), szx]; x = reshape (x, szx); dim = 1; endif perm_flag = true; endif ## Find column locations of NaNs nanfree = ! any (isnan (x), dim); if (omitnan && nanfree(:)) ## Don't use omitnan path if no NaNs are present. Prevents any data types ## without a defined NaN from following the omitnan codepath. omitnan = false; endif x = sort (x, dim); # Note: pushes any NaN's to end for omitnan compatibility if (omitnan) ## Ignore any NaN's in data. Each operating vector might have a ## different number of non-NaN data points. if (isvector (x)) ## Checks above ensure either dim1 or dim2 vector x = x(! isnan (x)); n = length (x); k = floor ((n + 1) / 2); if (mod (n, 2)) ## odd m = x(k); else ## even m = (x(k) + x(k + 1)) / 2; endif else ## Each column may have a different n and k. Force index column vector ## for consistent orientation for 2D and nD inputs, then use sub2ind to ## get correct element(s) for each column. n = sum (! isnan (x), 1)(:); k = floor ((n + 1) / 2); m_idx_odd = mod (n, 2) & n; m_idx_even = (! m_idx_odd) & n; m = NaN ([1, szx(2 : end)]); if (ndims (x) > 2) szx = [szx(1), prod(szx(2 : end))]; endif ## Grab kth value, k possibly different for each column if (any (m_idx_odd)) x_idx_odd = sub2ind (szx, k(m_idx_odd), find (m_idx_odd)); m(m_idx_odd) = x(x_idx_odd); endif if (any (m_idx_even)) k_even = k(m_idx_even); x_idx_even = sub2ind (szx, [k_even, k_even+1], ... (find (m_idx_even))(:,[1 1])); m(m_idx_even) = sum (x(x_idx_even), 2) / 2; endif endif else ## No "omitnan". All 'vectors' uniform length. ## All types without a NaN value will use this path. if (all (!nanfree)) m = NaN (sz_out); else if (isvector (x)) n = length (x); k = floor ((n + 1) / 2); m = x(k); if (! mod (n, 2)) ## Even if (any (isinf ([x(k), x(k+1)]))) ## If either center value is Inf, replace m by +/-Inf or NaN. m = x(k) + x(k+1); elseif (any (isa (x, "integer"))) ## avoid int overflow issues m2 = x(k+1); if (sign(m) != sign(m2)) m += m2; m /= 2; else m += (m2 - m) / 2; endif else m += (x(k+1) - m) / 2; endif endif else ## Nonvector, all operations were permuted to be along dim 1 n = szx(1); k = floor ((n + 1) / 2); if (isfloat (x)) m = NaN ([1, szx(2 : end)]); else m = zeros ([1, szx(2 : end)], outtype); endif if (! mod (n, 2)) ## Even if (any (isa(x, "integer"))) ## avoid int overflow issues ## Use flattened index to simplify N-D operations m(1,:) = x(k, :); m2 = x(k+1, :); samesign = prod (sign ([m(1,:); m2]), 1) == 1; m(1,:) = samesign .* m(1,:) + ... (m2 + !samesign .* m(1,:) - samesign .* m(1,:)) / 2; else m(nanfree) = (x(k, nanfree) + x(k+1, nanfree)) / 2; endif else ## Odd. Use flattened index to simplify n-D operations m(nanfree) = x(k, nanfree); endif endif endif endif if (perm_flag) ## Inverse permute back to correct dimensions m = ipermute (m, perm); endif ## Convert output type as requested if (! strcmp (class (m), outtype)) m = feval (outtype, m); endif endfunction %!assert (median (1), 1) %!assert (median ([1,2,3]), 2) %!assert (median ([1,2,3]'), 2) %!assert (median (cat(3,3,1,2)), 2) %!assert (median ([3,1,2]), 2) %!assert (median ([2,4,6,8]), 5) %!assert (median ([8,2,6,4]), 5) %!assert (median (single ([1,2,3])), single (2)) %!assert (median ([1,2], 3), [1,2]) %!test %! x = [1, 2, 3, 4, 5, 6]; %! x2 = x'; %! y = [1, 2, 3, 4, 5, 6, 7]; %! y2 = y'; %! %! assert (median (x) == median (x2) && median (x) == 3.5); %! assert (median (y) == median (y2) && median (y) == 4); %! assert (median ([x2, 2 * x2]), [3.5, 7]); %! assert (median ([y2, 3 * y2]), [4, 12]); ## Test outtype option %!test %! in = [1 2 3]; %! out = 2; %! assert (median (in, "default"), median (in)); %! assert (median (in, "default"), out); %!test %! in = single ([1 2 3]); %! out = 2; %! assert (median (in, "default"), single (median (in))); %! assert (median (in, "default"), single (out)); %! assert (median (in, "double"), double (out)); %! assert (median (in, "native"), single (out)); %!test %! in = uint8 ([1 2 3]); %! out = 2; %! assert (median (in, "default"), double (median (in))); %! assert (median (in, "default"), double (out)); %! assert (median (in, "double"), out); %! assert (median (in, "native"), uint8 (out)); %!test %! in = logical ([1 0 1]); %! out = 1; %! assert (median (in, "default"), double (median (in))); %! assert (median (in, "default"), double (out)); %! assert (median (in, "double"), double (out)); %! assert (median (in, "native"), double (out)); ## Test single input and optional arguments "all", DIM, "omitnan") %!test %! x = repmat ([2 2.1 2.2 2 NaN; 3 1 2 NaN 5; 1 1.1 1.4 5 3], [1, 1, 4]); %! y = repmat ([2 1.1 2 NaN NaN], [1, 1, 4]); %! assert (median (x), y); %! assert (median (x, 1), y); %! y = repmat ([2 1.1 2 3.5 4], [1, 1, 4]); %! assert (median (x, "omitnan"), y); %! assert (median (x, 1, "omitnan"), y); %! y = repmat ([2.05; 2.5; 1.4], [1, 1, 4]); %! assert (median (x, 2, "omitnan"), y); %! y = repmat ([NaN; NaN; 1.4], [1, 1, 4]); %! assert (median (x, 2), y); %! assert (median (x, "all"), NaN); %! assert (median (x, "all", "omitnan"), 2); %!assert (median (cat (3, 3, 1, NaN, 2), "omitnan"), 2) %!assert (median (cat (3, 3, 1, NaN, 2), 3, "omitnan"), 2) ## Test boolean input %!test %! assert (median (true, "all"), logical (1)); %! assert (median (false), logical (0)); %! assert (median ([true false true]), true); %! assert (median ([true false true], 2), true); %! assert (median ([true false true], 1), logical ([1 0 1])); %! assert (median ([true false NaN], 1), [1 0 NaN]); %! assert (median ([true false NaN], 2), NaN); %! assert (median ([true false NaN], 2, "omitnan"), 0.5); %! assert (median ([true false NaN], 2, "omitnan", "native"), double(0.5)); ## Test dimension indexing with vecdim in n-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5 2 6 3]); %! assert (size (median (x, [3 2])), [10 1 1 3]); %! assert (size (median (x, [1 2])), [1 1 6 3]); %! assert (size (median (x, [1 2 4])), [1 1 6]); %! assert (size (median (x, [1 4 3])), [1 40]); %! assert (size (median (x, [1 2 3 4])), [1 1]); ## Test exceeding dimensions %!assert (median (ones (2,2), 3), ones (2,2)) %!assert (median (ones (2,2,2), 99), ones (2,2,2)) %!assert (median (magic (3), 3), magic (3)) %!assert (median (magic (3), [1 3]), [4, 5, 6]) %!assert (median (magic (3), [1 99]), [4, 5, 6]) ## Test results with vecdim in n-dimensional arrays and "omitnan" %!test %! x = repmat ([2 2.1 2.2 2 NaN; 3 1 2 NaN 5; 1 1.1 1.4 5 3], [1, 1, 4]); %! assert (median (x, [3 2]), [NaN NaN 1.4]'); %! assert (median (x, [3 2], "omitnan"), [2.05 2.5 1.4]'); %! assert (median (x, [1 3]), [2 1.1 2 NaN NaN]); %! assert (median (x, [1 3], "omitnan"), [2 1.1 2 3.5 4]); ## Test empty, NaN, Inf inputs %!assert (median (NaN), NaN) %!assert (median (NaN, "omitnan"), NaN) %!assert (median (NaN (2)), [NaN NaN]) %!assert (median (NaN (2), "omitnan"), [NaN NaN]) %!assert (median ([1 NaN 3]), NaN) %!assert (median ([1 NaN 3], 1), [1 NaN 3]) %!assert (median ([1 NaN 3], 2), NaN) %!assert (median ([1 NaN 3]'), NaN) %!assert (median ([1 NaN 3]', 1), NaN) %!assert (median ([1 NaN 3]', 2), [1; NaN; 3]) %!assert (median ([1 NaN 3], "omitnan"), 2) %!assert (median ([1 NaN 3]', "omitnan"), 2) %!assert (median ([1 NaN 3], 1, "omitnan"), [1 NaN 3]) %!assert (median ([1 NaN 3], 2, "omitnan"), 2) %!assert (median ([1 NaN 3]', 1, "omitnan"), 2) %!assert (median ([1 NaN 3]', 2, "omitnan"), [1; NaN; 3]) %!assert (median ([1 2 NaN 3]), NaN) %!assert (median ([1 2 NaN 3], "omitnan"), 2) %!assert (median ([1,2,NaN;4,5,6;NaN,8,9]), [NaN, 5, NaN]) %!assert <*64011> (median ([1,2,NaN;4,5,6;NaN,8,9], "omitnan"), [2.5, 5, 7.5], eps) %!assert (median ([1 2 ; NaN 4]), [NaN 3]) %!assert (median ([1 2 ; NaN 4], "omitnan"), [1 3]) %!assert (median ([1 2 ; NaN 4], 1, "omitnan"), [1 3]) %!assert (median ([1 2 ; NaN 4], 2, "omitnan"), [1.5; 4], eps) %!assert (median ([1 2 ; NaN 4], 3, "omitnan"), [1 2 ; NaN 4]) %!assert (median ([NaN 2 ; NaN 4]), [NaN 3]) %!assert (median ([NaN 2 ; NaN 4], "omitnan"), [NaN 3]) %!assert (median (ones (1, 0, 3)), NaN (1, 1, 3)) ## Test all NaN vectors and arrays - see bug #65405 %!assert <*65405> (median ([NaN NaN], 1, "omitnan"), [NaN NaN]) %!assert <*65405> (median ([NaN NaN], 2, "omitnan"), NaN) %!assert <*65405> (median ([NaN NaN]', 1, "omitnan"), NaN) %!assert <*65405> (median ([NaN NaN]', 2, "omitnan"), [NaN; NaN]) %!assert <*65405> (median ([NaN NaN], "omitnan"), NaN) %!assert <*65405> (median ([NaN NaN]', "omitnan"), NaN) %!assert <*65405> (median (NaN(1,9), 1, "omitnan"), NaN(1,9)) %!assert <*65405> (median (NaN(1,9), 2, "omitnan"), NaN) %!assert <*65405> (median (NaN(1,9), 3, "omitnan"), NaN(1,9)) %!assert <*65405> (median (NaN(9,1), 1, "omitnan"), NaN) %!assert <*65405> (median (NaN(9,1), 2, "omitnan"), NaN(9,1)) %!assert <*65405> (median (NaN(9,1), 3, "omitnan"), NaN(9,1)) %!assert <*65405> (median (NaN(9,2), 1, "omitnan"), NaN(1,2)) %!assert <*65405> (median (NaN(9,2), 2, "omitnan"), NaN(9,1)) %!assert <*65405> (median (NaN(9,2), "omitnan"), NaN(1,2)) ## Test single inputs %!assert (median (NaN("single")), NaN("single")) %!assert (median (NaN("single"), "omitnan"), NaN("single")) %!assert (median (NaN("single"), "double"), NaN("double")) %!assert (median (single([1 2 ; NaN 4])), single([NaN 3])) %!assert (median (single([1 2 ; NaN 4]), "double"), double([NaN 3])) %!assert (median (single([1 2 ; NaN 4]), "omitnan"), single([1 3])) %!assert (median (single([1 2 ; NaN 4]), "omitnan", "double"), double([1 3])) %!assert (median (single([NaN 2 ; NaN 4]), "double"), double([NaN 3])) %!assert (median (single([NaN 2 ; NaN 4]), "omitnan"), single([NaN 3])) %!assert (median (single([NaN 2 ; NaN 4]), "omitnan", "double"), double([NaN 3])) ## Test omitnan with 2D & 3D inputs to confirm correct sub2ind orientation %!test <*64011> %! x = [magic(3), magic(3)]; %! x([3, 7, 11, 12, 16, 17]) = NaN; %! ynan = [NaN, 5, NaN, NaN, 5, NaN]; %! yomitnan = [5.5, 5, 4.5, 8, 5, 2]; %! assert (median (x), ynan); %! assert (median (x, "omitnan"), yomitnan, eps); %! assert (median (cat (3, x, x)), cat (3, ynan, ynan)); %! assert (median (cat (3, x, x), "omitnan"), cat (3, yomitnan, yomitnan), eps); %!assert (median (Inf), Inf) %!assert (median (-Inf), -Inf) %!assert (median ([-Inf Inf]), NaN) %!assert (median ([3 Inf]), Inf) %!assert (median ([3 4 Inf]), 4) %!assert (median ([Inf 3 4]), 4) %!assert (median ([Inf 3 Inf]), Inf) %!assert (median ([1, 2, Inf]), 2) %!assert (median ([1, 2, Inf, Inf]), Inf) %!assert (median ([1, -Inf, Inf, Inf]), Inf) %!assert (median ([-Inf, -Inf, Inf, Inf]), NaN) %!assert (median([-Inf, Inf, Inf, Inf]), Inf) %!assert (median([-Inf, -Inf, -Inf, Inf]), -Inf) %!assert (median([-Inf, -Inf, -Inf, 2]), -Inf) %!assert (median([-Inf, -Inf, 1, 2]), -Inf) %!assert (median ([]), NaN) %!assert (median (ones(1,0)), NaN) %!assert (median (ones(0,1)), NaN) %!assert (median ([], 1), NaN(1,0)) %!assert (median ([], 2), NaN(0,1)) %!assert (median ([], 3), NaN(0,0)) %!assert (median (ones(1,0), 1), NaN(1,0)) %!assert (median (ones(1,0), 2), NaN(1,1)) %!assert (median (ones(1,0), 3), NaN(1,0)) %!assert (median (ones(0,1), 1), NaN(1,1)) %!assert (median (ones(0,1), 2), NaN(0,1)) %!assert (median (ones(0,1), 3), NaN(0,1)) %!assert (median (ones(0,1,0,1), 1), NaN(1,1,0)) %!assert (median (ones(0,1,0,1), 2), NaN(0,1,0)) %!assert (median (ones(0,1,0,1), 3), NaN(0,1,1)) %!assert (median (ones(0,1,0,1), 4), NaN(0,1,0)) ## Test complex inputs (should sort by abs(a)) %!assert (median([1 3 3i 2 1i]), 2) %!assert (median([1 2 4i; 3 2i 4]), [2, 1+1i, 2+2i]) ## Test multidimensional arrays %!shared a, b, x, y %! old_state = rand ("state"); %! restore_state = onCleanup (@() rand ("state", old_state)); %! rand ("state", 2); %! a = rand (2,3,4,5); %! b = rand (3,4,6,5); %! x = sort (a, 4); %! y = sort (b, 3); %!assert <*35679> (median (a, 4), x(:, :, :, 3)) %!assert <*35679> (median (b, 3), (y(:, :, 3, :) + y(:, :, 4, :))/2) %!shared ## Clear shared to prevent variable echo for any later test failures ## Test n-dimensional arrays with odd non-NaN data points %!test %! x = ones(15,1,4); %! x([13,15],1,:) = NaN; %! assert (median (x, 1, "omitnan"), ones (1,1,4)) ## Test non-floating point types %!assert (median ([true, false]), true) %!assert (median (logical ([])), false) %!assert (median (uint8 ([1, 3])), uint8 (2)) %!assert (median (uint8 ([])), uint8 (NaN)) %!assert (median (uint8 ([NaN 10])), uint8 (5)) %!assert (median (int8 ([1, 3, 4])), int8 (3)) %!assert (median (int8 ([])), int8 (NaN)) %!assert (median (single ([1, 3, 4])), single (3)) %!assert (median (single ([1, 3, NaN])), single (NaN)) ## Test same sign int overflow when getting mean of even number of values %!assert <54567> (median (uint8 ([253, 255])), uint8 (254)) %!assert <54567> (median (uint8 ([253, 254])), uint8 (254)) %!assert <54567> (median (int8 ([127, 126, 125, 124; 1 3 5 9])), ... %! int8 ([64 65 65 67])) %!assert <54567> (median (int8 ([127, 126, 125, 124; 1 3 5 9]), 2), ... %! int8 ([126; 4])) %!assert <54567> (median (int64 ([intmax("int64"), intmax("int64")-2])), ... %! intmax ("int64") - 1) %!assert <54567> (median ( ... %! int64 ([intmax("int64"), intmax("int64")-2; 1 2]), 2), ... %! int64([intmax("int64") - 1; 2])) %!assert <54567> (median (uint64 ([intmax("uint64"), intmax("uint64")-2])), ... %! intmax ("uint64") - 1) %!assert <54567> (median ( ... %! uint64 ([intmax("uint64"), intmax("uint64")-2; 1 2]), 2), ... %! uint64([intmax("uint64") - 1; 2])) ## Test opposite sign int overflow when getting mean of even number of values %!assert <54567> (median (... %! [intmin('int8') intmin('int8')+5 intmax('int8')-5 intmax('int8')]), ... %! int8(-1)) %!assert <54567> (median ([int8([1 2 3 4]); ... %! intmin('int8') intmin('int8')+5 intmax('int8')-5 intmax('int8')], 2), ... %! int8([3;-1])) %!assert <54567> (median (... %! [intmin('int64') intmin('int64')+5 intmax('int64')-5 intmax('int64')]), ... %! int64(-1)) %!assert <54567> (median ([int64([1 2 3 4]); ... %! intmin('int64') intmin('int64')+5 intmax('int64')-5 intmax('int64')], 2), ... %! int64([3;-1])) ## Test int accuracy loss doing mean of close int64/uint64 values as double %!assert <54567> (median ([intmax("uint64"), intmax("uint64")-2]), ... %! intmax("uint64")-1) %!assert <54567> (median ([intmax("uint64"), intmax("uint64")-2], "default"), ... %! double(intmax("uint64")-1)) %!assert <54567> (median ([intmax("uint64"), intmax("uint64")-2], "double"), ... %! double(intmax("uint64")-1)) %!assert <54567> (median ([intmax("uint64"), intmax("uint64")-2], "native"), ... %! intmax("uint64")-1) ## Test input case insensitivity %!assert (median ([1 2 3], "aLL"), 2) %!assert (median ([1 2 3], "OmitNan"), 2) %!assert (median ([1 2 3], "DOUBle"), 2) ## Test input validation %!error median () %!error median (1, 2, 3) %!error median (1, 2, 3, 4) %!error median (1, "all", 3) %!error median (1, "b") %!error median (1, 1, "foo") %!error <'all' cannot be used with> median (1, 3, "all") %!error <'all' cannot be used with> median (1, [2 3], "all") %!error median ({1:5}) %!error median ("char") %!error median(1, "double", "native") %!error median (1, ones (2,2)) %!error median (1, 1.5) %!error median (1, 0) %!error median ([1 2 3], [-1 1]) %!error median(1, [1 2 2]) statistics-release-1.7.3/inst/shadow9/std.m000066400000000000000000000755151475240274700206660ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## Copyright (C) 2023 Nicholas Jankowski ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{s} =} std (@var{x}) ## @deftypefnx {statistics} {@var{s} =} std (@var{x}, @var{w}) ## @deftypefnx {statistics} {@var{s} =} std (@var{x}, @var{w}, "all") ## @deftypefnx {statistics} {@var{s} =} std (@var{x}, @var{w}, @var{dim}) ## @deftypefnx {statistics} {@var{s} =} std (@var{x}, @var{w}, @var{vecdim}) ## @deftypefnx {statistics} {@var{s} =} std (@dots{}, @var{nanflag}) ## @deftypefnx {statistics} {[@var{s}, @var{m}] =} std (@dots{}) ## ## Compute the standard deviation of the elements of @var{x}. ## ## @itemize ## @item ## If @var{x} is a vector, then @code{std (@var{x})} returns the standard ## deviation of the elements in @var{x} defined as ## @tex ## $$ {\rm std}(x) = \sqrt{{1\over N-1} \sum_{i=1}^N |x_i - \bar x |^2} $$ ## ## @end tex ## @ifnottex ## ## @example ## std (@var{x}) = sqrt ((1 / (N-1)) * SUM_i (|@var{x}(i) - mean (@var{x})|^2)) ## @end example ## ## @end ifnottex ## @noindent ## where @math{N} is the length of the @var{x} vector. ## ## @item ## If @var{x} is a matrix, then @code{std (@var{x})} returns a row vector with ## the standard deviation of each column in @var{x}. ## ## @item ## If @var{x} is a multi-dimensional array, then @code{std (@var{x})} operates ## along the first non-singleton dimension of @var{x}. ## @end itemize ## ## @code{std (@var{x}, @var{w})} specifies a weighting scheme. When @var{w} = 0 ## (default), the standard deviation is normalized by N-1 (population standard ## deviation), where N is the number of observations. When @var{w} = 1, the ## standard deviation is normalized by the number of observations (sample ## standard deviation). To use the default value you may pass an empty input ## argument [] before entering other options. ## ## @var{w} can also be an array of non-negative numbers. When @var{w} is a ## vector, it must have the same length as the number of elements in the ## operating dimension of @var{x}. If @var{w} is a matrix or n-D array, or the ## operating dimension is supplied as a @var{vecdim} or "all", @var{w} must be ## the same size as @var{x}. NaN values are permitted in @var{w}, will be ## multiplied with the associated values in @var{x}, and can be excluded by the ## @var{nanflag} option. ## ## @code{std (@var{x}, [], @var{dim})} returns the standard deviation along the ## operating dimension @var{dim} of @var{x}. For @var{dim} greater than ## @code{ndims (@var{x})}, then @var{s} is returned as zeros of the same size as ## @var{x} and @var{m} = @var{x}. ## ## @code{std (@var{x}, [], @var{vecdim})} returns the standard deviation over ## the dimensions specified in the vector @var{vecdim}. For example, if @var{x} ## is a 2-by-3-by-4 array, then @code{var (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the standard ## deviation of the elements on the corresponding page of @var{x}. ## If @var{vecdim} indexes all dimensions of @var{x}, then it is equivalent to ## @code{std (@var{x}, "all")}. Any dimension in @var{vecdim} greater than ## @code{ndims (@var{x})} is ignored. ## ## @code{std (@var{x}, "all")} returns the standard deviation of all the ## elements in @var{x}. The optional flag "all" cannot be used together with ## @var{dim} or @var{vecdim} input arguments. ## ## @code{std (@dots{}, @var{nanflag})} specifies whether to exclude NaN values ## from the calculation using any of the input argument combinations in previous ## syntaxes. The default value for @var{nanflag} is "includenan", and keeps NaN ## values in the calculation. To exclude NaN values, set the value of ## @var{nanflag} to "omitnan". ## ## @code{[@var{s}, @var{m}] = std (@dots{})} also returns the mean of the ## elements of @var{x} used to calculate the standard deviation. If @var{s} is ## the weighted standard deviation, then @var{m} is the weighted mean. ## ## @seealso{var, mean} ## @end deftypefn function [s, m] = std (x, varargin) if (nargin < 1 || nargin > 4) print_usage (); endif ## initialize variables all_flag = false; omitnan = false; nvarg = numel (varargin); varg_chars = cellfun ('ischar', varargin); ## Check all char arguments. if (nvarg == 3 && ! varg_chars(3)) print_usage (); endif if (any (varg_chars)) for i = varargin(varg_chars) switch (lower (i{:})) case "all" all_flag = true; case "omitnan" omitnan = true; case "includenan" omitnan = false; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif # FIXME: when sparse can use broadcast ops, remove sparse checks and hacks sprs_x = issparse (x); w = 0; weighted = false; # true if weight vector/array used vecdim = []; vecempty = true; vecdim_scalar_vector = [false, false]; # [false, false] for empty vecdim szx = size (x); ndx = ndims (x); ## Check numeric arguments if (! (isnumeric (x))) error ("std: X must be a numeric vector or matrix."); endif if (isa (x, "single")) outtype = "single"; else outtype = "double"; endif if (nvarg > 0) if (nvarg > 2 || any (! cellfun ('isnumeric', varargin))) print_usage (); endif ## Process weight input if (any (varargin{1} < 0)) error ("std: weights must not contain any negative values."); endif if (isscalar (varargin{1})) w = varargin{1}; if (! (w == 0 || w == 1) && ! isscalar (x)) error ("std: normalization scalar must be either 0 or 1."); endif elseif (numel (varargin{1}) > 1) weights = varargin{1}; weighted = true; endif if (nvarg > 1) ## Process dimension input vecdim = varargin{2}; if (! (vecempty = isempty (vecdim))) ## Check for empty vecdim, won't change vsv if nonzero size empty vecdim_scalar_vector = [isscalar(vecdim), isvector(vecdim)]; endif if (! (vecdim_scalar_vector(2) && all (vecdim > 0)) ... || any (rem (vecdim, 1))) error ("std: DIM must be a positive integer scalar or vector."); endif if (vecdim_scalar_vector(1) && vecdim > ndx && ! isempty (x)) s = zeros (szx, outtype); sn = ! isfinite (x); s(sn) = NaN; m = x; return; endif if (vecdim_scalar_vector == [0 1] && (! all (diff (sort (vecdim))))) error ("std: VECDIM must contain non-repeating positive integers."); endif endif endif ## Check for conflicting input arguments if (all_flag && ! vecempty) error ("std: 'all' flag cannot be used with DIM or VECDIM options."); endif if (weighted) if (all_flag) if (isvector (weights)) if (numel (weights) != numel (x)) error ("std: weight vector element count does not match X."); endif elseif (! (isequal (size (weights), szx))) error ("std: weight matrix or array does not match X in size."); endif elseif (vecempty) dim = find (szx > 1, 1); if length (dim) == 0 dim = 1; endif if (isvector (weights)) if (numel (weights) != szx(dim)) error (["std: weight vector length does not match operating ", ... "dimension."]); endif elseif (! isequal (size (weights), szx)) error ("std: weight matrix or array does not match X in size."); endif elseif (vecdim_scalar_vector(1)) if (isvector (weights)) if (numel (weights) != szx(vecdim)) error (["std: weight vector length does not match operating ", ... "dimension."]); endif elseif (! isequal (size (weights), szx)) error ("std: weight matrix or array does not match X in size."); endif elseif (vecdim_scalar_vector(2) && ! (isequal (size (weights), szx))) error ("std: weight matrix or array does not match X in size."); endif endif ## Force output for X being empty or scalar if (isempty (x)) if (vecempty && (ndx == 2 || all ((szx) == 0))) s = NaN (outtype); if (nargout > 1) m = NaN (outtype); endif return; endif if (vecdim_scalar_vector(1)) szx(vecdim) = 1; s = NaN (szx, outtype); if (nargout > 1) m = NaN (szx, outtype); endif return; endif endif if (isscalar (x)) if (isfinite (x)) s = zeros (outtype); else s = NaN (outtype); endif if (nargout > 1) m = x; endif return; endif if (nvarg == 0) ## Only numeric input argument, no dimensions or weights. if (all_flag) x = x(:); if (omitnan) x = x(! isnan (x)); endif n = length (x); m = sum (x) ./ n; s = sqrt (sum (abs (x - m) .^ 2) ./ (n - 1 + w)); if (n == 1) s = 0; endif else dim = find (szx > 1, 1); if length (dim) == 0 dim = 1; endif n = szx(dim); if (omitnan) n = sum (! isnan (x), dim); xn = isnan (x); x(xn) = 0; endif m = sum (x, dim) ./ n; dims = ones (1, ndx); dims(dim) = szx(dim); if (sprs_x) m_exp = repmat (m, dims); else m_exp = m .* ones (dims); endif if (omitnan) x(xn) = m_exp(xn); endif s = sqrt (sumsq (x - m_exp, dim) ./ (n - 1 + w)); if (numel (n) == 1) divby0 = n .* ones (size (s)) == 1; else divby0 = n == 1; endif s(divby0) = 0; endif elseif (nvarg == 1) ## Two numeric input arguments, w or weights given. if (all_flag) x = x(:); if (weighted) weights = weights(:); wx = weights .* x; else weights = ones (length (x), 1); wx = x; endif if (omitnan) xn = isnan (wx); wx = wx(! xn); weights = weights(! xn); x = x(! xn); endif n = length (wx); m = sum (wx) ./ sum (weights); if (weighted) s = sqrt (sum (weights .* (abs (x - m) .^ 2)) ./ sum (weights)); else s = sqrt (sum (weights .* (abs (x - m) .^ 2)) ./ (n - 1 + w)); if (n == 1) s = 0; endif endif else dim = find (szx > 1, 1); if length (dim) == 0 dim = 1; endif if (! weighted) weights = ones (szx); wx = x; else if (isvector (weights)) dims = 1:ndx; dims([1, dim]) = [dim, 1]; weights = zeros (szx) + permute (weights(:), dims); endif wx = weights .* x; endif n = size (wx, dim); if (omitnan) xn = isnan (wx); n = sum (! xn, dim); wx(xn) = 0; weights(xn) = 0; endif m = sum (wx, dim) ./ sum (weights, dim); dims = ones (1, ndims (wx)); dims(dim) = size (wx, dim); if (sprs_x) m_exp = repmat (m, dims); else m_exp = m .* ones (dims); endif if (omitnan) x(xn) = m_exp(xn); endif if (weighted) s = sqrt (sum (weights .* ((x - m_exp) .^ 2), dim) ... ./ sum (weights, dim)); else s = sqrt (sumsq (x - m_exp, dim) ./ (n - 1 + w)); if (numel (n) == 1) divby0 = n .* ones (size (s)) == 1; else divby0 = n == 1; endif s(divby0) = 0; endif endif elseif (nvarg == 2) ## Three numeric input arguments, both w or weights and dim or vecdim given. if (vecdim_scalar_vector(1)) if (!weighted) weights = ones (szx); wx = x; else if (isvector (weights)) dims = 1:ndx; dims([1, vecdim]) = [vecdim, 1]; weights = zeros (szx) + permute (weights(:), dims); endif wx = weights .* x; endif n = size (wx, vecdim); if (omitnan) n = sum (! isnan (wx), vecdim); xn = isnan (wx); wx(xn) = 0; weights(xn) = 0; endif m = sum (wx, vecdim) ./ sum (weights, vecdim); dims = ones (1, ndims (wx)); dims(vecdim) = size (wx, vecdim); if (sprs_x) m_exp = repmat (m, dims); else m_exp = m .* ones (dims); endif if (omitnan) x(xn) = m_exp(xn); endif if (weighted) s = sqrt (sum (weights .* ((x - m_exp) .^ 2), vecdim) ... ./ sum (weights, vecdim)); else s = sumsq (x - m_exp, vecdim); sn = isnan (s); s = sqrt (s ./ (n - 1 + w)); if (numel (n) == 1) divby0 = n .* ones (size (s)) == 1; else divby0 = n == 1; endif s(divby0) = 0; s(sn) = NaN; endif else ## Weights and nonscalar vecdim specified ## Ignore exceeding dimensions in VECDIM remdims = 1 : ndx; # all dimensions vecdim(find (vecdim > ndx)) = []; ## Calculate permutation vector remdims(vecdim) = []; # delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is similar to all flag if (nremd == 0) x = x(:); if (weighted) weights = weights(:); wx = weights .* x; else weights = ones (length (x), 1); wx = x; endif if (omitnan) xn = isnan (wx); wx = wx(! xn); weights = weights(! xn); x = x(! xn); endif n = length (wx); m = sum (wx) ./ sum (weights); if (weighted) s = sqrt (sum (weights .* (abs (x - m) .^ 2)) ./ sum (weights)); else s = sqrt (sum (weights .* (abs (x - m) .^ 2)) ./ (n - 1 + w)); if (n == 1) s = 0; endif endif else ## Apply weights if (weighted) wx = weights .* x; else weights = ones (szx); wx = x; endif ## Permute to bring remaining dims forward perm = [remdims, vecdim]; wx = permute (wx, perm); weights = permute (weights, perm); x = permute (x, perm); ## Reshape to put all vecdims in final dimension szwx = size (wx); sznew = [szwx(1:nremd), prod(szwx(nremd+1:end))]; wx = reshape (wx, sznew); weights = reshape (weights, sznew); x = reshape (x, sznew); ## Calculate var on single, squashed dimension dim = nremd + 1; n = size (wx, dim); if (omitnan) xn = isnan (wx); n = sum (! xn, dim); wx(xn) = 0; weights(xn) = 0; endif m = sum (wx, dim) ./ sum (weights, dim); m_exp = zeros (size (wx)) + m; if (omitnan) x(xn) = m_exp(xn); endif if (weighted) s = sqrt (sum (weights .* ((x - m_exp) .^ 2), dim) ... ./ sum (weights, dim)); else s = sqrt (sumsq (x - m_exp, dim) ./ (n - 1 + w)); if (numel (n) == 1) divby0 = n .* ones (size (s)) == 1; else divby0 = n == 1; endif s(divby0) = 0; endif ## Inverse permute back to correct dimensions s = ipermute (s, perm); if (nargout > 1) m = ipermute (m, perm); endif endif endif endif ## Preserve class type if (nargout < 2) if strcmp (outtype, "single") s = single (s); else s = double (s); endif else if strcmp (outtype, "single") s = single (s); m = single (m); else s = double (s); m = double (m); endif endif endfunction %!assert (std (13), 0) %!assert (std (single (13)), single (0)) %!assert (std ([1,2,3]), 1) %!assert (std ([1,2,3], 1), sqrt (2/3), eps) %!assert (std ([1,2,3], [], 1), [0,0,0]) %!assert (std ([1,2,3], [], 3), [0,0,0]) %!assert (std (5, 99), 0) %!assert (std (5, 99, 1), 0) %!assert (std (5, 99, 2), 0) %!assert (std ([5 3], [99 99], 2), 1) %!assert (std ([1:7], [1:7]), sqrt (3)) %!assert (std ([eye(3)], [1:3]), sqrt ([5/36, 2/9, 1/4]), eps) %!assert (std (ones (2,2,2), [1:2], 3), [(zeros (2,2))]) %!assert (std ([1 2; 3 4], 0, 'all'), std ([1:4])) %!assert (std (reshape ([1:8], 2, 2, 2), 0, [1 3]), sqrt ([17/3 17/3]), eps) %!assert (std ([1 2 3;1 2 3], [], [1 2]), sqrt (0.8), eps) ## Test single input and optional arguments "all", DIM, "omitnan") %!test %! x = [-10:10]; %! y = [x;x+5;x-5]; %! assert (std (x), sqrt (38.5), 1e-14); %! assert (std (y, [], 2), sqrt ([38.5; 38.5; 38.5]), 1e-14); %! assert (std (y, 0, 2), sqrt ([38.5; 38.5; 38.5]), 1e-14); %! assert (std (y, 1, 2), ones (3,1) * sqrt (36.66666666666666), 1e-14); %! assert (std (y, "all"), sqrt (54.19354838709678), 1e-14); %! y(2,4) = NaN; %! assert (std (y, "all"), NaN); %! assert (std (y, "all", "includenan"), NaN); %! assert (std (y, "all", "omitnan"), sqrt (55.01533580116342), 1e-14); %! assert (std (y, 0, 2, "includenan"), sqrt ([38.5; NaN; 38.5]), 1e-14); %! assert (std (y, [], 2), sqrt ([38.5; NaN; 38.5]), 1e-14); %! assert (std (y, [], 2, "omitnan"), ... %! sqrt ([38.5; 37.81842105263158; 38.5]), 1e-14); ## Tests for different weight and omitnan code paths %!assert (std ([4 NaN 6], [1 2 1], "omitnan"), 1, eps) %!assert (std ([4 5 6], [1 NaN 1], "omitnan"), 1, eps) %!assert (std (magic(3), [1 NaN 3], "omitnan"), sqrt(3)*[1 2 1], eps) %!assert (std ([4 NaN 6], [1 2 1], "omitnan", "all"), 1, eps) %!assert (std ([4 NaN 6], [1 2 1], "all", "omitnan"), 1, eps) %!assert (std ([4 5 6], [1 NaN 1], "omitnan", "all"), 1, eps) %!assert (std ([4 NaN 6], [1 2 1], 2, "omitnan"), 1, eps) %!assert (std ([4 5 6], [1 NaN 1], 2, "omitnan"), 1, eps) %!assert (std (magic(3), [1 NaN 3], 1, "omitnan"), sqrt(3)*[1 2 1], eps) %!assert (std (magic(3), [1 NaN 3], 2, "omitnan"), sqrt(3)*[0.5;1;0.5], eps) %!assert (std (4*[4 5; 6 7; 8 9], [1 3], 2, 'omitnan'), sqrt(3)*[1;1;1], eps) %!assert (std ([4 NaN; 6 7; 8 9], [1 1 3], 1, 'omitnan'), [1.6 sqrt(3)/2], eps) %!assert (std (4*[4 NaN; 6 7; 8 9], [1 3], 2, 'omitnan'), sqrt(3)*[0;1;1], eps) %!assert (std (3*reshape(1:18, [3 3 2]), [1 2 3], 1, 'omitnan'), ... %! sqrt(5)*ones(1,3,2), eps) %!assert (std (reshape(1:18, [3 3 2]), [1 2 3], 2, 'omitnan'), ... %! sqrt(5)*ones(3,1,2), eps) %!assert (std (3*reshape(1:18, [3 3 2]), ones (3,3,2), [1 2], 'omitnan'), ... %! sqrt(60)*ones(1,1,2),eps) %!assert (std (3*reshape(1:18, [3 3 2]), ones (3,3,2), [1 4], 'omitnan'), ... %! sqrt(6)*ones(1,3,2),eps) %!assert (std (6*reshape(1:18, [3 3 2]), ones (3,3,2), [1:3], 'omitnan'), ... %! sqrt(969),eps) %!test %! x = reshape(1:18, [3 3 2]); %! x([2, 14]) = NaN; %! w = ones (3,3,2); %! assert (std (16*x, w, [1:3], 'omitnan'), sqrt(6519), eps); %!test %! x = reshape(1:18, [3 3 2]); %! w = ones (3,3,2); %! w([2, 14]) = NaN; %! assert (std (16*x, w, [1:3], 'omitnan'), sqrt(6519), eps); ## Test input case insensitivity %!assert (std ([1 2 3], "aLl"), 1); %!assert (std ([1 2 3], "OmitNan"), 1); %!assert (std ([1 2 3], "IncludeNan"), 1); ## Test dimension indexing with vecdim in n-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! assert (size (std (x, 0, [3 2])), [10, 1, 1, 3]); %! assert (size (std (x, 1, [1 2])), [1, 1, 6, 3]); %! assert (size (std (x, [], [1 2 4])), [1, 1, 6]); %! assert (size (std (x, 0, [1 4 3])), [1, 40]); %! assert (size (std (x, [], [1 2 3 4])), [1, 1]); ## Test matrix with vecdim, weighted, matrix weights, omitnan %!assert (std (3*magic(3)), sqrt([63 144 63]), eps) %!assert (std (3*magic(3), 'omitnan'), sqrt([63 144 63]), eps) %!assert (std (3*magic(3), 1), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), 1, 'omitnan'), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), ones(1,3), 1), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), ones(1,3), 1, 'omitnan'), sqrt([42 96 42]), eps) %!assert (std (2*magic(3), [1 1 NaN], 1, 'omitnan'), [5 4 1], eps) %!assert (std (3*magic(3), ones(3,3)), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), ones(3,3), 'omitnan'), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), [1 1 1; 1 1 1; 1 NaN 1], 'omitnan'), ... %! sqrt([42 36 42]), eps) %!assert (std (3*magic(3), ones(3,3), 1), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), ones(3,3), 1, 'omitnan'), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), [1 1 1; 1 1 1; 1 NaN 1], 1, 'omitnan'), ... %! sqrt([42 36 42]), eps) %!assert (std (3*magic(3), ones(3,3), [1 4]), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), ones(3,3), [1 4], 'omitnan'), sqrt([42 96 42]), eps) %!assert (std (3*magic(3), [1 1 1; 1 1 1; 1 NaN 1],[1 4],'omitnan'), ... %! sqrt([42 36 42]), eps) ## Test results with vecdim in n-dimensional arrays and "omitnan" %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! v = repmat (sqrt (33.38912133891213), [10, 1, 1, 3]); %! assert (std (x, 0, [3, 2]), v, 1e-14); %! v = repmat (sqrt (33.250), [10, 1, 1, 3]); %! assert (std (x, 1, [3, 2]), v, 1e-14); %! x(2,5,6,3) = NaN; %! v(2,1,1,3) = NaN; %! assert (std (x, 1, [3, 2]), v, 1e-14); %! v = repmat (sqrt (33.38912133891213), [10 1 1 3]); %! v(2,1,1,3) = NaN; %! assert (std (x, [], [3, 2]), v, 1e-14); %! v(2,1,1,3) = sqrt (33.40177912169048); %! assert (std (x, [], [3, 2], "omitnan"), v, 1e-14); ## Testing weights vectors & arrays %!assert (std (ones (2,2,2), [1:2], 3), [(zeros (2, 2))]) %!assert (std (magic (3), [1:9], "all"), 2.581988897471611, 1e-14) ## Test exceeding dimensions %!assert (std (ones (2,2), [], 3), zeros (2,2)) %!assert (std (ones (2,2,2), [], 99), zeros (2,2,2)) %!assert (std (magic (3), [], 3), zeros (3,3)) %!assert (std (magic (3), [], 1), sqrt ([7, 16, 7])) %!assert (std (magic (3), [], [1 3]), sqrt ([7, 16, 7])) %!assert (std (magic (3), [], [1 99]), sqrt ([7, 16, 7])) ## Test empty inputs %!assert (std ([]), NaN) %!assert (class (var (single ([]))), "single") %!assert (std ([],[],1), NaN(1,0)) %!assert (std ([],[],2), NaN(0,1)) %!assert (std ([],[],3), []) %!assert (class (var (single ([]), [], 1)), "single") %!assert (std (ones (1,0)), NaN) %!assert (std (ones (1,0), [], 1), NaN(1,0)) %!assert (std (ones (1,0), [], 2), NaN) %!assert (std (ones (1,0), [], 3), NaN(1,0)) %!assert (class (var (ones (1, 0, "single"), [], 1)), "single") %!assert (std (ones (0,1)), NaN) %!assert (std (ones (0,1), [], 1), NaN) %!assert (std (ones (0,1), [], 2), NaN(0,1)) %!assert (std (ones (0,1), [], 3), NaN(0,1)) %!assert (std (ones (1,3,0,2)), NaN(1,1,0,2)) %!assert (std (ones (1,3,0,2), [], 1), NaN(1,3,0,2)) %!assert (std (ones (1,3,0,2), [], 2), NaN(1,1,0,2)) %!assert (std (ones (1,3,0,2), [], 3), NaN(1,3,1,2)) %!assert (std (ones (1,3,0,2), [], 4), NaN(1,3,0)) %!test %! [~, m] = std ([]); %! assert (m, NaN); ## Test optional mean output %!test <*62395> %! [~, m] = std (13); %! assert (m, 13); %! [~, m] = std (single(13)); %! assert (m, single(13)); %! [~, m] = std ([1, 2, 3; 3 2 1], []); %! assert (m, [2 2 2]); %! [~, m] = std ([1, 2, 3; 3 2 1], [], 1); %! assert (m, [2 2 2]); %! [~, m] = std ([1, 2, 3; 3 2 1], [], 2); %! assert (m, [2 2]'); %! [~, m] = std ([1, 2, 3; 3 2 1], [], 3); %! assert (m, [1 2 3; 3 2 1]); ## Test mean output, weighted inputs, vector dims %!test <*62395> %! [~, m] = std (5,99); %! assert (m, 5); %! [~, m] = std ([1:7], [1:7]); %! assert (m, 5); %! [~, m] = std ([eye(3)], [1:3]); %! assert (m, [1/6, 1/3, 0.5], eps); %! [~, m] = std (ones (2,2,2), [1:2], 3); %! assert (m, ones (2,2)); %! [~, m] = std ([1 2; 3 4], 0, 'all'); %! assert (m, 2.5, eps); %! [~, m] = std (reshape ([1:8], 2, 2, 2), 0, [1 3]); %! assert (m, [3.5, 5.5], eps); %!test %! [v, m] = std (4 * eye (2), [1, 3]); %! assert (v, sqrt ([3, 3]), 1e-14); %! assert (m, [1, 3]); ## Test mean output, empty inputs, omitnan %!test <*62395> %! [~, m] = std ([]); %! assert (m, NaN); #%! [~, m] = std ([],[],1); #%! assert (m, NaN(1,0)); #%! [~, m] = std ([],[],2); #%! assert (m, NaN(0,1)); #%! [~, m] = std ([],[],3); #%! assert (m, []); #%! [~, m] = std (ones (1,3,0,2)); #%! assert (m, NaN(1,1,0,2)); ## Test mean output, nD array %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! [~, m] = std (x, 0, [3 2]); %! assert (m, mean (x, [3 2])); %! [~, m] = std (x, 0, [1 2]); %! assert (m, mean (x, [1 2])); %! [~, m] = std (x, 0, [1 3 4]); %! assert (m, mean (x, [1 3 4])); %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! x(2,5,6,3) = NaN; %! [~, m] = std (x, 0, [3 2], "omitnan"); %! assert (m, mean (x, [3 2], "omitnan")); ## Test Inf and NaN inputs %!test <*63203> %! [v, m] = std (Inf); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = std (NaN); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([1, Inf, 3]); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = std ([1, Inf, 3]'); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = std ([1, NaN, 3]); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([1, NaN, 3]'); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([1, Inf, 3], [], 1); %! assert (v, [0, NaN, 0]); %! assert (m, [1, Inf, 3]); %!test <*63203> %! [v, m] = std ([1, Inf, 3], [], 2); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = std ([1, Inf, 3], [], 3); %! assert (v, [0, NaN, 0]); %! assert (m, [1, Inf, 3]); %!test <*63203> %! [v, m] = std ([1, NaN, 3], [], 1); %! assert (v, [0, NaN, 0]); %! assert (m, [1, NaN, 3]); %!test <*63203> %! [v, m] = std ([1, NaN, 3], [], 2); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([1, NaN, 3], [], 3); %! assert (v, [0, NaN, 0]); %! assert (m, [1, NaN, 3]); %!test <*63203> %! [v, m] = std ([1, 2, 3; 3, Inf, 5]); %! assert (v, sqrt ([2, NaN, 2])); %! assert (m, [2, Inf, 4]); %!test <*63203> %! [v, m] = std ([1, Inf, 3; 3, Inf, 5]); %! assert (v, sqrt ([2, NaN, 2])); %! assert (m, [2, Inf, 4]); %!test <*63203> %! [v, m] = std ([1, 2, 3; 3, NaN, 5]); %! assert (v, sqrt ([2, NaN, 2])); %! assert (m, [2, NaN, 4]); %!test <*63203> %! [v, m] = std ([1, NaN, 3; 3, NaN, 5]); %! assert (v, sqrt ([2, NaN, 2])); %! assert (m, [2, NaN, 4]); %!test <*63203> %! [v, m] = std ([Inf, 2, NaN]); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([Inf, 2, NaN]'); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([NaN, 2, Inf]); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([NaN, 2, Inf]'); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([Inf, 2, NaN], [], 1); %! assert (v, [NaN, 0, NaN]); %! assert (m, [Inf, 2, NaN]); %!test <*63203> %! [v, m] = std ([Inf, 2, NaN], [], 2); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([NaN, 2, Inf], [], 1); %! assert (v, [NaN, 0, NaN]); %! assert (m, [NaN, 2, Inf]); %!test <*63203> %! [v, m] = std ([NaN, 2, Inf], [], 2); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = std ([1, 3, NaN; 3, 5, Inf]); %! assert (v, sqrt ([2, 2, NaN])); %! assert (m, [2, 4, NaN]); %!test <*63203> %! [v, m] = std ([1, 3, Inf; 3, 5, NaN]); %! assert (v, sqrt ([2, 2, NaN])); %! assert (m, [2, 4, NaN]); ## Test sparse/diagonal inputs %!test <*63291> %! [v, m] = std (2 * eye (2)); %! assert (v, sqrt ([2, 2])); %! assert (m, [1, 1]); %!test <*63291> %! [v, m] = std (4 * eye (2), [1, 3]); %! assert (v, sqrt ([3, 3])); %! assert (m, [1, 3]); %!test <*63291> %! [v, m] = std (sparse (2 * eye (2))); %! assert (full (v), sqrt ([2, 2])); %! assert (full (m), [1, 1]); %!test <*63291> %! [v, m] = std (sparse (4 * eye (2)), [1, 3]); %! assert (full (v), sqrt ([3, 3])); %! assert (full (m), [1, 3]); %!test <*63291> %! [v, m] = std (sparse (eye (2))); %! assert (issparse (v)); %! assert (issparse (m)); %!test <*63291> %! [v, m] = std (sparse (eye (2)), [1, 3]); %! assert (issparse (v)); %! assert (issparse (m)); ## Test input validation %!error std () %!error std (1, 2, "omitnan", 3) %!error std (1, 2, 3, 4) %!error std (1, 2, 3, 4, 5) %!error std (1, "foo") %!error std (1, [], "foo") %!error std ([1 2 3], 2) %!error std ([1 2], 2, "all") %!error std ([1 2],0.5, "all") %!error std (1, -1) %!error std (1, [1 -1]) %!error ... %! std ([1 2 3], [1 -1 0]) %!error std ({1:5}) %!error std ("char") %!error std (['A'; 'B']) %!error std (1, [], ones (2,2)) %!error std (1, 0, 1.5) %!error std (1, [], 0) %!error std (1, [], 1.5) %!error std ([1 2 3], [], [-1 1]) %!error ... %! std (repmat ([1:20;6:25], [5 2 6 3]), 0, [1 2 2 2]) %!error ... %! std ([1 2], eye (2)) %!error ... %! std ([1 2 3 4], [1 2; 3 4]) %!error ... %! std ([1 2 3 4], [1 2; 3 4], 1) %!error ... %! std ([1 2 3 4], [1 2; 3 4], [2 3]) %!error ... %! std (ones (2, 2), [1 2], [1 2]) %!error ... %! std ([1 2 3 4; 5 6 7 8], [1 2 1 2 1; 1 2 1 2 1], 1) %!error ... %! std (repmat ([1:20;6:25], [5 2 6 3]), repmat ([1:20;6:25], [5 2 3]), [2 3]) %!error std ([1 2 3; 2 3 4], [1 3 4]) %!error std ([1 2], [1 2 3]) %!error std (1, [1 2]) %!error std ([1 2 3; 2 3 4], [1 3 4], 1) %!error std ([1 2 3; 2 3 4], [1 3], 2) %!error std ([1 2], [1 2], 1) %!error <'all' flag cannot be used with DIM or VECDIM options> ... %! std (1, [], 1, "all") %!error ... %! std ([1 2 3; 2 3 4], [1 3], "all") %!error ... %! std (repmat ([1:20;6:25], [5 2 6 3]), repmat ([1:20;6:25], [5 2 3]), "all") statistics-release-1.7.3/inst/shadow9/var.m000066400000000000000000000743061475240274700206610ustar00rootroot00000000000000## Copyright (C) 2022-2023 Andreas Bertsatos ## Copyright (C) 2023 Nicholas Jankowski ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{v} =} var (@var{x}) ## @deftypefnx {statistics} {@var{v} =} var (@var{x}, @var{w}) ## @deftypefnx {statistics} {@var{v} =} var (@var{x}, @var{w}, "all") ## @deftypefnx {statistics} {@var{v} =} var (@var{x}, @var{w}, @var{dim}) ## @deftypefnx {statistics} {@var{v} =} var (@var{x}, @var{w}, @var{vecdim}) ## @deftypefnx {statistics} {@var{v} =} var (@dots{}, @var{nanflag}) ## @deftypefnx {statistics} {[@var{v}, @var{m}] =} var (@dots{}) ## ## Compute the variance of the elements of @var{x}. ## ## @itemize ## @item ## If @var{x} is a vector, then @code{var(@var{x})} returns the variance of the ## elements in @var{x} defined as ## @tex ## $$ {\rm var}(x) = {1\over N-1} \sum_{i=1}^N |x_i - \bar x |^2 $$ ## ## @end tex ## @ifnottex ## ## @example ## var (@var{x}) = (1 / (N-1)) * SUM_i (|@var{x}(i) - mean (@var{x})|^2) ## @end example ## ## @end ifnottex ## @noindent ## where @math{N} is the length of the @var{x} vector. ## ## @item ## If @var{x} is a matrix, then @code{var (@var{x})} returns a row vector with ## the variance of each column in @var{x}. ## ## @item ## If @var{x} is a multi-dimensional array, then @code{var (@var{x})} operates ## along the first non-singleton dimension of @var{x}. ## @end itemize ## ## @code{var (@var{x}, @var{w})} specifies a weighting scheme. When @var{w} = 0 ## (default), the variance is normalized by N-1 (population variance) where N is ## the number of observations. When @var{w} = 1, the variance is normalized by ## the number of observations (sample variance). To use the default value you ## may pass an empty input argument [] before entering other options. ## ## @var{w} can also be an array of non-negative numbers. When @var{w} is a ## vector, it must have the same length as the number of elements in the ## operating dimension of @var{x}. If @var{w} is a matrix or n-D array, or the ## operating dimension is supplied as a @var{vecdim} or "all", @var{w} must be ## the same size as @var{x}. NaN values are permitted in @var{w}, will be ## multiplied with the associated values in @var{x}, and can be excluded by the ## @var{nanflag} option. ## ## @code{var (@var{x}, [], @var{dim})} returns the variance along the operating ## dimension @var{dim} of @var{x}. For @var{dim} greater than ## @code{ndims (@var{x})} @var{v} is returned as zeros of the same size as ## @var{x} and @var{m} = @var{x}. ## ## @code{var (@var{x}, [], @var{vecdim})} returns the variance over the ## dimensions specified in the vector @var{vecdim}. For example, if @var{x} ## is a 2-by-3-by-4 array, then @code{var (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the variance of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to @code{var (@var{x}, "all")}. ## Any dimension in @var{vecdim} greater than @code{ndims (@var{x})} is ignored. ## ## @code{var (@var{x}, "all")} returns the variance of all the elements in ## @var{x}. The optional flag "all" cannot be used together with @var{dim} or ## @var{vecdim} input arguments. ## ## @code{var (@dots{}, @var{nanflag})} specifies whether to exclude NaN values ## from the calculation using any of the input argument combinations in previous ## syntaxes. The default value for @var{nanflag} is "includenan", and keeps NaN ## values in the calculation. To exclude NaN values, set the value of ## @var{nanflag} to "omitnan". ## ## @code{[@var{v}, @var{m}] = var (@dots{})} also returns the mean of the ## elements of @var{x} used to calculate the variance. If @var{v} is the ## weighted variance, then @var{m} is the weighted mean. ## ## @seealso{std, mean} ## @end deftypefn function [v, m] = var (x, varargin) if (nargin < 1 || nargin > 4) print_usage (); endif ## initialize variables all_flag = false; omitnan = false; nvarg = numel (varargin); varg_chars = cellfun ('ischar', varargin); ## Check all char arguments. if (nvarg == 3 && ! varg_chars(3)) print_usage (); endif if (any (varg_chars)) for i = varargin(varg_chars) switch (lower (i{:})) case "all" all_flag = true; case "omitnan" omitnan = true; case "includenan" omitnan = false; otherwise print_usage (); endswitch endfor varargin(varg_chars) = []; nvarg = numel (varargin); endif # FIXME: when sparse can use broadcast ops, remove sparse checks and hacks sprs_x = issparse (x); w = 0; weighted = false; # true if weight vector/array used vecdim = []; vecempty = true; vecdim_scalar_vector = [false, false]; # [false, false] for empty vecdim szx = size (x); ndx = ndims (x); ## Check numeric arguments if (! (isnumeric (x))) error ("var: X must be a numeric vector or matrix."); endif if (isa (x, "single")) outtype = "single"; else outtype = "double"; endif if (nvarg > 0) if (nvarg > 2 || any (! cellfun ('isnumeric', varargin))) print_usage (); endif ## Process weight input if (any (varargin{1} < 0)) error ("var: weights must not contain any negative values."); endif if (isscalar (varargin{1})) w = varargin{1}; if (! (w == 0 || w == 1) && ! isscalar (x)) error ("var: normalization scalar must be either 0 or 1."); endif elseif (numel (varargin{1}) > 1) weights = varargin{1}; weighted = true; endif if (nvarg > 1) ## Process dimension input vecdim = varargin{2}; if (! (vecempty = isempty (vecdim))) ## Check for empty vecdim, won't change vsv if nonzero size empty vecdim_scalar_vector = [isscalar(vecdim), isvector(vecdim)]; endif if (! (vecdim_scalar_vector(2) && all (vecdim > 0)) ... || any (rem (vecdim, 1))) error ("var: DIM must be a positive integer scalar or vector."); endif if (vecdim_scalar_vector(1) && vecdim > ndx && ! isempty (x)) ## Scalar dimension larger than ndims(x), variance of any single number ## is zero, except for inf, NaN, and empty values of x. v = zeros (szx, outtype); vn = ! isfinite (x); v(vn) = NaN; m = x; return; endif if (vecdim_scalar_vector == [0 1] && (! all (diff (sort (vecdim))))) error ("var: VECDIM must contain non-repeating positive integers."); endif endif endif ## Check for conflicting input arguments if (all_flag && ! vecempty) error ("var: 'all' flag cannot be used with DIM or VECDIM options."); endif if (weighted) if (all_flag) if (isvector (weights)) if (numel (weights) != numel (x)) error ("var: weight vector element count does not match X."); endif elseif (! (isequal (size (weights), szx))) error ("var: weight matrix or array does not match X in size."); endif elseif (vecempty) dim = find (szx > 1, 1); if length (dim) == 0 dim = 1; endif if (isvector (weights)) if (numel (weights) != szx(dim)) error (["var: weight vector length does not match operating ", ... "dimension."]); endif elseif (! isequal (size (weights), szx)) error ("var: weight matrix or array does not match X in size."); endif elseif (vecdim_scalar_vector(1)) if (isvector (weights)) if (numel (weights) != szx(vecdim)) error (["var: weight vector length does not match operating ", ... "dimension."]); endif elseif (! isequal (size (weights), szx)) error ("var: weight matrix or array does not match X in size."); endif elseif (vecdim_scalar_vector(2) && ! (isequal (size (weights), szx))) error ("var: weight matrix or array does not match X in size."); endif endif ## Force output for X being empty or scalar if (isempty (x)) if (vecempty && (ndx == 2 || all ((szx) == 0))) v = NaN (outtype); if (nargout > 1) m = NaN (outtype); endif return; endif if (vecdim_scalar_vector(1)) szx(vecdim) = 1; v = NaN (szx, outtype); if (nargout > 1) m = NaN (szx, outtype); endif return; endif endif if (isscalar (x)) if (isfinite (x)) v = zeros (outtype); else v = NaN (outtype); endif if (nargout > 1) m = x; endif return; endif if (nvarg == 0) ## Only numeric input argument, no dimensions or weights. if (all_flag) x = x(:); if (omitnan) x = x(! isnan (x)); endif n = length (x); m = sum (x) ./ n; v = sum (abs (x - m) .^ 2) ./ (n - 1 + w); if (n == 1) v = 0; endif else dim = find (szx > 1, 1); if length (dim) == 0 dim = 1; endif n = szx(dim); if (omitnan) n = sum (! isnan (x), dim); xn = isnan (x); x(xn) = 0; endif m = sum (x, dim) ./ n; dims = ones (1, ndx); dims(dim) = szx(dim); if (sprs_x) m_exp = repmat (m, dims); else m_exp = m .* ones (dims); endif if (omitnan) x(xn) = m_exp(xn); endif v = sumsq (x - m_exp, dim) ./ (n - 1 + w); if (numel (n) == 1) divby0 = n .* ones (size (v)) == 1; else divby0 = n == 1; endif v(divby0) = 0; endif elseif (nvarg == 1) ## Two numeric input arguments, w or weights given. if (all_flag) x = x(:); if (weighted) weights = weights(:); wx = weights .* x; else weights = ones (length (x), 1); wx = x; endif if (omitnan) xn = isnan (wx); wx = wx(! xn); weights = weights(! xn); x = x(! xn); endif n = length (wx); m = sum (wx) ./ sum (weights); if (weighted) v = sum (weights .* (abs (x - m) .^ 2)) ./ sum (weights); else v = sum (weights .* (abs (x - m) .^ 2)) ./ (n - 1 + w); if (n == 1) v = 0; endif endif else dim = find (szx > 1, 1); if length (dim) == 0 dim = 1; endif if (! weighted) weights = ones (szx); wx = x; else if (isvector (weights)) dims = 1:ndx; dims([1, dim]) = [dim, 1]; weights = zeros (szx) + permute (weights(:), dims); endif wx = weights .* x; endif n = size (wx, dim); if (omitnan) xn = isnan (wx); n = sum (! xn, dim); wx(xn) = 0; weights(xn) = 0; endif m = sum (wx, dim) ./ sum (weights, dim); dims = ones (1, ndims (wx)); dims(dim) = size (wx, dim); if (sprs_x) m_exp = repmat (m, dims); else m_exp = m .* ones (dims); endif if (omitnan) x(xn) = m_exp(xn); endif if (weighted) v = sum (weights .* ((x - m_exp) .^ 2), dim) ./ sum (weights, dim); else v = sumsq (x - m_exp, dim) ./ (n - 1 + w); if (numel (n) == 1) divby0 = n .* ones (size (v)) == 1; else divby0 = n == 1; endif v(divby0) = 0; endif endif elseif (nvarg == 2) ## Three numeric input arguments, both w or weights and dim or vecdim given. if (vecdim_scalar_vector(1)) if (!weighted) weights = ones (szx); wx = x; else if (isvector (weights)) dims = 1:ndx; dims([1, vecdim]) = [vecdim, 1]; weights = zeros (szx) + permute (weights(:), dims); endif wx = weights .* x; endif n = size (wx, vecdim); if (omitnan) n = sum (! isnan (wx), vecdim); xn = isnan (wx); wx(xn) = 0; weights(xn) = 0; endif m = sum (wx, vecdim) ./ sum (weights, vecdim); dims = ones (1, ndims (wx)); dims(vecdim) = size (wx, vecdim); if (sprs_x) m_exp = repmat (m, dims); else m_exp = m .* ones (dims); endif if (omitnan) x(xn) = m_exp(xn); endif if (weighted) v = sum (weights .* ((x - m_exp) .^ 2), vecdim) ... ./ sum (weights, vecdim); else v = sumsq (x - m_exp, vecdim); vn = isnan (v); v = v ./ (n - 1 + w); if (numel (n) == 1) divby0 = n .* ones (size (v)) == 1; else divby0 = n == 1; endif v(divby0) = 0; v(vn) = NaN; endif else ## Weights and nonscalar vecdim specified ## Ignore exceeding dimensions in VECDIM remdims = 1 : ndx; # all dimensions vecdim(find (vecdim > ndx)) = []; ## Calculate permutation vector remdims(vecdim) = []; # delete dimensions specified by vecdim nremd = numel (remdims); ## If all dimensions are given, it is similar to all flag if (nremd == 0) x = x(:); if (weighted) weights = weights(:); wx = weights .* x; else weights = ones (length (x), 1); wx = x; endif if (omitnan) xn = isnan (wx); wx = wx(! xn); weights = weights(! xn); x = x(! xn); endif n = length (wx); m = sum (wx) ./ sum (weights); if (weighted) v = sum (weights .* (abs (x - m) .^ 2)) ./ sum (weights); else v = sum (weights .* (abs (x - m) .^ 2)) ./ (n - 1 + w); if (n == 1) v = 0; endif endif else ## FIXME: much of the reshaping can be skipped once octave's sum can ## take a vecdim argument. ## Apply weights if (weighted) wx = weights .* x; else weights = ones (szx); wx = x; endif ## Permute to bring remaining dims forward perm = [remdims, vecdim]; wx = permute (wx, perm); weights = permute (weights, perm); x = permute (x, perm); ## Reshape to put all vecdims in final dimension szwx = size (wx); sznew = [szwx(1:nremd), prod(szwx(nremd+1:end))]; wx = reshape (wx, sznew); weights = reshape (weights, sznew); x = reshape (x, sznew); ## Calculate var on single, squashed dimension dim = nremd + 1; n = size (wx, dim); if (omitnan) xn = isnan (wx); n = sum (! xn, dim); wx(xn) = 0; weights(xn) = 0; endif m = sum (wx, dim) ./ sum (weights, dim); m_exp = zeros (sznew) + m; if (omitnan) x(xn) = m_exp(xn); endif if (weighted) v = sum (weights .* ((x - m_exp) .^ 2), dim) ./ sum (weights, dim); else v = sumsq (x - m_exp, dim) ./ (n - 1 + w); if (numel (n) == 1) divby0 = n .* ones (size (v)) == 1; else divby0 = n == 1; endif v(divby0) = 0; endif ## Inverse permute back to correct dimensions v = ipermute (v, perm); if (nargout > 1) m = ipermute (m, perm); endif endif endif endif ## Preserve class type if (nargout < 2) if strcmp (outtype, "single") v = single (v); else v = double (v); endif else if strcmp (outtype, "single") v = single (v); m = single (m); else v = double (v); m = double (m); endif endif endfunction %!assert (var (13), 0) %!assert (var (single (13)), single (0)) %!assert (var ([1,2,3]), 1) %!assert (var ([1,2,3], 1), 2/3, eps) %!assert (var ([1,2,3], [], 1), [0,0,0]) %!assert (var ([1,2,3], [], 3), [0,0,0]) %!assert (var (5, 99), 0) %!assert (var (5, 99, 1), 0) %!assert (var (5, 99, 2), 0) %!assert (var ([5 3], [99 99], 2), 1) %!assert (var ([1:7], [1:7]), 3) %!assert (var ([eye(3)], [1:3]), [5/36, 2/9, 1/4], eps) %!assert (var (ones (2,2,2), [1:2], 3), [(zeros (2,2))]) %!assert (var ([1 2; 3 4], 0, 'all'), var ([1:4])) %!assert (var (reshape ([1:8], 2, 2, 2), 0, [1 3]), [17/3 17/3], eps) %!assert (var ([1 2 3;1 2 3], [], [1 2]), 0.8, eps) ## Test single input and optional arguments "all", DIM, "omitnan") %!test %! x = [-10:10]; %! y = [x;x+5;x-5]; %! assert (var (x), 38.5); %! assert (var (y, [], 2), [38.5; 38.5; 38.5]); %! assert (var (y, 0, 2), [38.5; 38.5; 38.5]); %! assert (var (y, 1, 2), ones (3,1) * 36.66666666666666, 1e-14); %! assert (var (y, "all"), 54.19354838709678, 1e-14); %! y(2,4) = NaN; %! assert (var (y, "all"), NaN); %! assert (var (y, "all", "includenan"), NaN); %! assert (var (y, "all", "omitnan"), 55.01533580116342, 1e-14); %! assert (var (y, 0, 2, "includenan"), [38.5; NaN; 38.5]); %! assert (var (y, [], 2), [38.5; NaN; 38.5]); %! assert (var (y, [], 2, "omitnan"), [38.5; 37.81842105263158; 38.5], 1e-14); ## Tests for different weight and omitnan code paths %!assert (var ([1 NaN 3], [1 2 3], "omitnan"), 0.75, eps) %!assert (var ([1 2 3], [1 NaN 3], "omitnan"), 0.75, eps) %!assert (var (magic(3), [1 NaN 3], "omitnan"), [3 12 3], eps) %!assert (var ([1 NaN 3], [1 2 3], "omitnan", "all"), 0.75, eps) %!assert (var ([1 NaN 3], [1 2 3], "all", "omitnan"), 0.75, eps) %!assert (var ([1 2 3], [1 NaN 3], "omitnan", "all"), 0.75, eps) %!assert (var ([1 NaN 3], [1 2 3], 2, "omitnan"), 0.75, eps) %!assert (var ([1 2 3], [1 NaN 3], 2, "omitnan"), 0.75, eps) %!assert (var (magic(3), [1 NaN 3], 1, "omitnan"), [3 12 3], eps) %!assert (var (magic(3), [1 NaN 3], 2, "omitnan"), [0.75;3;0.75], eps) %!assert (var ([4 4; 4 6; 6 6], [1 3], 2, 'omitnan'), [0;0.75;0], eps) %!assert (var ([4 NaN; 4 6; 6 6], [1 2 3], 1, 'omitnan'), [1 0]) %!assert (var ([4 NaN; 4 6; 6 6], [1 3], 2, 'omitnan'), [0;0.75;0], eps) %!assert (var (3*reshape(1:18, [3 3 2]), [1 2 3], 1, 'omitnan'), ones(1,3,2)*5) %!assert (var (reshape(1:18, [3 3 2]), [1 2 3], 2, 'omitnan'), 5*ones(3,1,2)) %!assert (var (3*reshape(1:18, [3 3 2]), ones (3,3,2), [1 2], 'omitnan'), ... %! 60 * ones(1,1,2)) %!assert (var (3*reshape(1:18, [3 3 2]), ones (3,3,2), [1 4], 'omitnan'), ... %! 6 * ones(1,3,2)) %!assert (var (6*reshape(1:18, [3 3 2]), ones (3,3,2), [1:3], 'omitnan'), 969) %!test %! x = reshape(1:18, [3 3 2]); %! x([2, 14]) = NaN; %! w = ones (3,3,2); %! assert (var (16*x, w, [1:3], 'omitnan'), 6519); %!test %! x = reshape(1:18, [3 3 2]); %! w = ones (3,3,2); %! w([2, 14]) = NaN; %! assert (var (16*x, w, [1:3], 'omitnan'), 6519); ## Test input case insensitivity %!assert (var ([1 2 3], "aLl"), 1); %!assert (var ([1 2 3], "OmitNan"), 1); %!assert (var ([1 2 3], "IncludeNan"), 1); ## Test dimension indexing with vecdim in n-dimensional arrays %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! assert (size (var (x, 0, [3 2])), [10, 1, 1, 3]); %! assert (size (var (x, 1, [1 2])), [1, 1, 6, 3]); %! assert (size (var (x, [], [1 2 4])), [1, 1, 6]); %! assert (size (var (x, 0, [1 4 3])), [1, 40]); %! assert (size (var (x, [], [1 2 3 4])), [1, 1]); ## Test matrix with vecdim, weighted, matrix weights, omitnan %!assert (var (3*magic(3)), [63 144 63]) %!assert (var (3*magic(3), 'omitnan'), [63 144 63]) %!assert (var (3*magic(3), 1), [42 96 42]) %!assert (var (3*magic(3), 1, 'omitnan'), [42 96 42]) %!assert (var (3*magic(3), ones(1,3), 1), [42 96 42]) %!assert (var (3*magic(3), ones(1,3), 1, 'omitnan'), [42 96 42]) %!assert (var (2*magic(3), [1 1 NaN], 1, 'omitnan'), [25 16 1]) %!assert (var (3*magic(3), ones(3,3)), [42 96 42]) %!assert (var (3*magic(3), ones(3,3), 'omitnan'), [42 96 42]) %!assert (var (3*magic(3), [1 1 1; 1 1 1; 1 NaN 1], 'omitnan'), [42 36 42]) %!assert (var (3*magic(3), ones(3,3), 1), [42 96 42]) %!assert (var (3*magic(3), ones(3,3), 1, 'omitnan'), [42 96 42]) %!assert (var (3*magic(3), [1 1 1; 1 1 1; 1 NaN 1], 1, 'omitnan'), [42 36 42]) %!assert (var (3*magic(3), ones(3,3), [1 4]), [42 96 42]) %!assert (var (3*magic(3), ones(3,3), [1 4], 'omitnan'), [42 96 42]) %!assert (var (3*magic(3), [1 1 1; 1 1 1; 1 NaN 1],[1 4],'omitnan'), [42 36 42]) ## Test results with vecdim in n-dimensional arrays and "omitnan" %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! v = repmat (33.38912133891213, [10, 1, 1, 3]); %! assert (var (x, 0, [3, 2]), v, 1e-14); %! v = repmat (33.250, [10, 1, 1, 3]); %! assert (var (x, 1, [3, 2]), v, 1e-14); %! x(2,5,6,3) = NaN; %! v(2,1,1,3) = NaN; %! assert (var (x, 1, [3, 2]), v, 4e-14); %! v = repmat (33.38912133891213, [10 1 1 3]); %! v(2,1,1,3) = NaN; %! assert (var (x, [], [3, 2]), v, 4e-14); %! v(2,1,1,3) = 33.40177912169048; %! assert (var (x, [], [3, 2], "omitnan"), v, 4e-14); ## Testing weights vector & arrays %!assert (var (ones (2,2,2), [1:2], 3), [(zeros (2, 2))]) %!assert (var (magic (3), [1:9], "all"), 6.666666666666667, 1e-14) ## Test exceeding dimensions %!assert (var (ones (2,2), [], 3), zeros (2,2)) %!assert (var (ones (2,2,2), [], 99), zeros (2,2,2)) %!assert (var (magic (3), [], 3), zeros (3,3)) %!assert (var (magic (3), [], 1), [7, 16, 7]) %!assert (var (magic (3), [], [1 3]), [7, 16, 7]) %!assert (var (magic (3), [], [1 99]), [7, 16, 7]) ## Test empty inputs %!assert (var ([]), NaN) %!assert (class (var (single ([]))), "single") %!assert (var ([],[],1), NaN(1,0)) %!assert (var ([],[],2), NaN(0,1)) %!assert (var ([],[],3), []) %!assert (class (var (single ([]), [], 1)), "single") %!assert (var (ones (1,0)), NaN) %!assert (var (ones (1,0), [], 1), NaN(1,0)) %!assert (var (ones (1,0), [], 2), NaN) %!assert (var (ones (1,0), [], 3), NaN(1,0)) %!assert (class (var (ones (1, 0, "single"), [], 1)), "single") %!assert (var (ones (0,1)), NaN) %!assert (var (ones (0,1), [], 1), NaN) %!assert (var (ones (0,1), [], 2), NaN(0,1)) %!assert (var (ones (0,1), [], 3), NaN(0,1)) %!assert (var (ones (1,3,0,2)), NaN(1,1,0,2)) %!assert (var (ones (1,3,0,2), [], 1), NaN(1,3,0,2)) %!assert (var (ones (1,3,0,2), [], 2), NaN(1,1,0,2)) %!assert (var (ones (1,3,0,2), [], 3), NaN(1,3,1,2)) %!assert (var (ones (1,3,0,2), [], 4), NaN(1,3,0)) %!test %! [~, m] = var ([]); %! assert (m, NaN); ## Test optional mean output %!test <*62395> %! [~, m] = var (13); %! assert (m, 13); %! [~, m] = var (single(13)); %! assert (m, single(13)); %! [~, m] = var ([1, 2, 3; 3 2 1], []); %! assert (m, [2 2 2]); %! [~, m] = var ([1, 2, 3; 3 2 1], [], 1); %! assert (m, [2 2 2]); %! [~, m] = var ([1, 2, 3; 3 2 1], [], 2); %! assert (m, [2 2]'); %! [~, m] = var ([1, 2, 3; 3 2 1], [], 3); %! assert (m, [1 2 3; 3 2 1]); ## Test mean output, weighted inputs, vector dims %!test <*62395> %! [~, m] = var (5,99); %! assert (m, 5); %! [~, m] = var ([1:7], [1:7]); %! assert (m, 5); %! [~, m] = var ([eye(3)], [1:3]); %! assert (m, [1/6, 1/3, 0.5], eps); %! [~, m] = var (ones (2,2,2), [1:2], 3); %! assert (m, ones (2,2)); %! [~, m] = var ([1 2; 3 4], 0, 'all'); %! assert (m, 2.5, eps); %! [~, m] = var (reshape ([1:8], 2, 2, 2), 0, [1 3]); %! assert (m, [3.5, 5.5], eps); %!test %! [v, m] = var (4 * eye (2), [1, 3]); %! assert (v, [3, 3]); %! assert (m, [1, 3]); ## Test mean output, empty inputs, omitnan %!test <*62395> %! [~, m] = var ([]); %! assert (m, NaN); #%! [~, m] = var ([],[],1); #%! assert (m, NaN(1,0)); #%! [~, m] = var ([],[],2); #%! assert (m, NaN(0,1)); #%! [~, m] = var ([],[],3); #%! assert (m, []); #%! [~, m] = var (ones (1,3,0,2)); #%! assert (m, NaN(1,1,0,2)); ## Test mean output, nD array %!test <*62395> %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! [~, m] = var (x, 0, [3 2]); %! assert (m, mean (x, [3 2])); %! [~, m] = var (x, 0, [1 2]); %! assert (m, mean (x, [1 2])); %! [~, m] = var (x, 0, [1 3 4]); %! assert (m, mean (x, [1 3 4])); %!test %! x = repmat ([1:20;6:25], [5, 2, 6, 3]); %! x(2,5,6,3) = NaN; %! [~, m] = var (x, 0, [3 2], "omitnan"); %! assert (m, mean (x, [3 2], "omitnan")); ## Test Inf and NaN inputs %!test <*63203> %! [v, m] = var (Inf); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = var (NaN); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([1, Inf, 3]); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = var ([1, Inf, 3]'); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = var ([1, NaN, 3]); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([1, NaN, 3]'); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([1, Inf, 3], [], 1); %! assert (v, [0, NaN, 0]); %! assert (m, [1, Inf, 3]); %!test <*63203> %! [v, m] = var ([1, Inf, 3], [], 2); %! assert (v, NaN); %! assert (m, Inf); %!test <*63203> %! [v, m] = var ([1, Inf, 3], [], 3); %! assert (v, [0, NaN, 0]); %! assert (m, [1, Inf, 3]); %!test <*63203> %! [v, m] = var ([1, NaN, 3], [], 1); %! assert (v, [0, NaN, 0]); %! assert (m, [1, NaN, 3]); %!test <*63203> %! [v, m] = var ([1, NaN, 3], [], 2); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([1, NaN, 3], [], 3); %! assert (v, [0, NaN, 0]); %! assert (m, [1, NaN, 3]); %!test <*63203> %! [v, m] = var ([1, 2, 3; 3, Inf, 5]); %! assert (v, [2, NaN, 2]); %! assert (m, [2, Inf, 4]); %!test <*63203> %! [v, m] = var ([1, Inf, 3; 3, Inf, 5]); %! assert (v, [2, NaN, 2]); %! assert (m, [2, Inf, 4]); %!test <*63203> %! [v, m] = var ([1, 2, 3; 3, NaN, 5]); %! assert (v, [2, NaN, 2]); %! assert (m, [2, NaN, 4]); %!test <*63203> %! [v, m] = var ([1, NaN, 3; 3, NaN, 5]); %! assert (v, [2, NaN, 2]); %! assert (m, [2, NaN, 4]); %!test <*63203> %! [v, m] = var ([Inf, 2, NaN]); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([Inf, 2, NaN]'); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([NaN, 2, Inf]); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([NaN, 2, Inf]'); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([Inf, 2, NaN], [], 1); %! assert (v, [NaN, 0, NaN]); %! assert (m, [Inf, 2, NaN]); %!test <*63203> %! [v, m] = var ([Inf, 2, NaN], [], 2); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([NaN, 2, Inf], [], 1); %! assert (v, [NaN, 0, NaN]); %! assert (m, [NaN, 2, Inf]); %!test <*63203> %! [v, m] = var ([NaN, 2, Inf], [], 2); %! assert (v, NaN); %! assert (m, NaN); %!test <*63203> %! [v, m] = var ([1, 3, NaN; 3, 5, Inf]); %! assert (v, [2, 2, NaN]); %! assert (m, [2, 4, NaN]); %!test <*63203> %! [v, m] = var ([1, 3, Inf; 3, 5, NaN]); %! assert (v, [2, 2, NaN]); %! assert (m, [2, 4, NaN]); ## Test sparse/diagonal inputs %!test <*63291> %! [v, m] = var (2 * eye (2)); %! assert (v, [2, 2]); %! assert (m, [1, 1]); %!test <*63291> %! [v, m] = var (4 * eye (2), [1, 3]); %! assert (v, [3, 3]); %! assert (m, [1, 3]); %!test <*63291> %! [v, m] = var (sparse (2 * eye (2))); %! assert (full (v), [2, 2]); %! assert (full (m), [1, 1]); %!test <*63291> %! [v, m] = var (sparse (4 * eye (2)), [1, 3]); %! assert (full (v), [3, 3]); %! assert (full (m), [1, 3]); %!test<*63291> %! [v, m] = var (sparse (eye (2))); %! assert (issparse (v)); %! assert (issparse (m)); %!test<*63291> %! [v, m] = var (sparse (eye (2)), [1, 3]); %! assert (issparse (v)); %! assert (issparse (m)); ## Test input validation %!error var () %!error var (1, 2, "omitnan", 3) %!error var (1, 2, 3, 4) %!error var (1, 2, 3, 4, 5) %!error var (1, "foo") %!error var (1, [], "foo") %!error var ([1 2 3], 2) %!error var ([1 2], 2, "all") %!error var ([1 2],0.5, "all") %!error var (1, -1) %!error var (1, [1 -1]) %!error ... %! var ([1 2 3], [1 -1 0]) %!error var ({1:5}) %!error var ("char") %!error var (['A'; 'B']) %!error var (1, [], ones (2,2)) %!error var (1, 0, 1.5) %!error var (1, [], 0) %!error var (1, [], 1.5) %!error var ([1 2 3], [], [-1 1]) %!error ... %! var (repmat ([1:20;6:25], [5 2 6 3]), 0, [1 2 2 2]) %!error ... %! var ([1 2], eye (2)) %!error ... %! var ([1 2 3 4], [1 2; 3 4]) %!error ... %! var ([1 2 3 4], [1 2; 3 4], 1) %!error ... %! var ([1 2 3 4], [1 2; 3 4], [2 3]) %!error ... %! var (ones (2, 2), [1 2], [1 2]) %!error ... %! var ([1 2 3 4; 5 6 7 8], [1 2 1 2 1; 1 2 1 2 1], 1) %!error ... %! var (repmat ([1:20;6:25], [5 2 6 3]), repmat ([1:20;6:25], [5 2 3]), [2 3]) %!error var ([1 2 3; 2 3 4], [1 3 4]) %!error var ([1 2], [1 2 3]) %!error var (1, [1 2]) %!error var ([1 2 3; 2 3 4], [1 3 4], 1) %!error var ([1 2 3; 2 3 4], [1 3], 2) %!error var ([1 2], [1 2], 1) %!error <'all' flag cannot be used with DIM or VECDIM options> ... %! var (1, [], 1, "all") %!error ... %! var ([1 2 3; 2 3 4], [1 3], "all") %!error ... %! var (repmat ([1:20;6:25], [5 2 6 3]), repmat ([1:20;6:25], [5 2 3]), "all") statistics-release-1.7.3/inst/sigma_pts.m000066400000000000000000000077451475240274700205040ustar00rootroot00000000000000## Copyright (C) 2017 - Juan Pablo Carbajal ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pts} =} sigma_pts (@var{n}) ## @deftypefnx {statistics} {@var{pts} =} sigma_pts (@var{n}, @var{m}) ## @deftypefnx {statistics} {@var{pts} =} sigma_pts (@var{n}, @var{m}, @var{K}) ## @deftypefnx {statistics} {@var{pts} =} sigma_pts (@var{n}, @var{m}, @var{K}, @var{l}) ## ## Calculates 2*@var{n}+1 sigma points in @var{n} dimensions. ## ## Sigma points are used in the unscented transfrom to estimate ## the result of applying a given nonlinear transformation to a probability ## distribution that is characterized only in terms of a finite set of statistics. ## ## If only the dimension @var{n} is given the resulting points have zero mean ## and identity covariance matrix. ## If the mean @var{m} or the covaraince matrix @var{K} are given, then the resulting points ## will have those statistics. ## The factor @var{l} scaled the points away from the mean. It is useful to tune ## the accuracy of the unscented transfrom. ## ## There is no unique way of computing sigma points, this function implements the ## algorithm described in section 2.6 "The New Filter" pages 40-41 of ## ## Uhlmann, Jeffrey (1995). "Dynamic Map Building and Localization: New Theoretical Foundations". ## Ph.D. thesis. University of Oxford. ## ## @end deftypefn function pts = sigma_pts (n, m = [], K = [], l = 0) if isempty (K) K = eye (n); endif if isempty (m) m = zeros (1, n); endif if (n ~= length (m)) error ("Dimension and size of mean vector don't match.") endif if any(n ~= size (K)) error ("Dimension and size of covariance matrix don't match.") endif if isdefinite (K) <= 0 error ("Covariance matrix should be positive definite.") endif pts = zeros (2 * n + 1, n); pts(1,:) = m; K = sqrtm ((n + l) * K); pts(2:n+1,:) = bsxfun (@plus, m , K); pts(n+2:end,:) = bsxfun (@minus, m , K); endfunction %!demo %! K = [1 0.5; 0.5 1]; # covaraince matrix %! # calculate and build associated ellipse %! [R,S,~] = svd (K); %! theta = atan2 (R(2,1), R(1,1)); %! v = sqrt (diag (S)); %! v = v .* [cos(theta) sin(theta); -sin(theta) cos(theta)]; %! t = linspace (0, 2*pi, 100).'; %! xe = v(1,1) * cos (t) + v(2,1) * sin (t); %! ye = v(1,2) * cos (t) + v(2,2) * sin (t); %! %! figure(1); clf; hold on %! # Plot ellipse and axes %! line ([0 0; v(:,1).'],[0 0; v(:,2).']) %! plot (xe,ye,'-r'); %! %! col = 'rgb'; %! l = [-1.8 -1 1.5]; %! for li = 1:3 %! p = sigma_pts (2, [], K, l(li)); %! tmp = plot (p(2:end,1), p(2:end,2), ['x' col(li)], ... %! p(1,1), p(1,2), ['o' col(li)]); %! h(li) = tmp(1); %! endfor %! hold off %! axis image %! legend (h, arrayfun (@(x) sprintf ("l:%.2g", x), l, "unif", 0)); %!test %! p = sigma_pts (5); %! assert (mean (p), zeros(1,5), sqrt(eps)); %! assert (cov (p), eye(5), sqrt(eps)); %!test %! m = randn(1, 5); %! p = sigma_pts (5, m); %! assert (mean (p), m, sqrt(eps)); %! assert (cov (p), eye(5), sqrt(eps)); %!test %! x = linspace (0,1,5); %! K = exp (- (x.' - x).^2/ 0.5); %! p = sigma_pts (5, [], K); %! assert (mean (p), zeros(1,5), sqrt(eps)); %! assert (cov (p), K, sqrt(eps)); %!error sigma_pts(2,1); %!error sigma_pts(2,[],1); %!error sigma_pts(2,1,1); %!error sigma_pts(2,[0.5 0.5],[-1 0; 0 0]); statistics-release-1.7.3/inst/signrank.m000066400000000000000000000315511475240274700203220ustar00rootroot00000000000000## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pval} =} signrank (@var{x}) ## @deftypefnx {statistics} {@var{pval} =} signrank (@var{x}, @var{my}) ## @deftypefnx {statistics} {@var{pval} =} signrank (@var{x}, @var{my}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{pval}, @var{h}] =} signrank (@dots{}) ## @deftypefnx {statistics} {[@var{pval}, @var{h}, @var{stats}] =} signrank (@dots{}) ## ## Wilcoxon signed rank test for median. ## ## @code{@var{pval} = signrank (@var{x})} returns the @math{p}-value of a ## two-sided Wilcoxon signed rank test. It tests the null hypothesis that data ## in @var{x} come from a distribution with zero median at the 5% significance ## level under the assumption that the distribution is symmetric about its ## median. @var{x} must be a vector. ## ## If the second argument @var{my} is a scalar, the null hypothesis is that ## @var{x} has median @var{my}, whereas if @var{my} is a vector, the null ## hypothesis is that the distribution of @code{@var{x} - @var{my}} has zero ## median. ## ## @code{@var{pval} = signrank (@dots{}, @var{Name}, @var{Value})} performs the ## Wilcoxon signed rank test with additional options specified by one or more of ## the following @var{Name}, @var{Value} pair arguments: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"alpha"} @tab @tab A scalar value for the significance level of ## the test. Default is 0.05. ## ## @item @qcode{"tail"} @tab @tab A character vector specifying the alternative ## hypothesis. It can take one of the following values: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"both"} @tab For one-sample test (@var{my} is empty or a ## scalar), the data in @var{x} come from a continuous distribution with median ## different than zero or @var{my}. For two-sample test (@var{my} is a vector), ## the data in @qcode{@var{x} - @var{my}} come from a continuous distribution ## with median different than zero. ## ## @item @tab @qcode{"left"} @tab For one-sample test (@var{my} is empty or a ## scalar), the data in @var{x} come from a continuous distribution with median ## less than zero or @var{my}. For two-sample test (@var{my} is a vector), the ## data in @qcode{@var{x} - @var{my}} come from a continuous distribution with ## median less than zero. ## ## @item @tab @qcode{"right"} @tab For one-sample test (@var{my} is empty or a ## scalar), the data in @var{x} come from a continuous distribution with median ## greater than zero or @var{my}. For two-sample test (@var{my} is a vector), ## the data in @qcode{@var{x} - @var{my}} come from a continuous distribution ## with median greater than zero. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"method"} @tab @tab A character vector specifying the method for ## computing the @math{p}-value. It can take one of the following values: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"exact"} @tab Exact computation of the @math{p}-value. It ## is the default value for 15 of fewer observations when @qcode{"method"} is ## not specified. ## ## @item @tab @qcode{"approximate"} @tab Using normal approximation for ## computing the @math{p}-value. It is the default value for more than 15 ## observations when @qcode{"method"} is not specified. ## @end multitable ## ## @code{[@var{pval}, @var{h}] = signrank (@dots{})} also returns a logical ## value indicating the test decision. If @var{h} is 0, the null hypothesis is ## accepted, whereas if @var{h} is 1, the null hypothesis is rejected. ## ## @code{[@var{pval}, @var{h}, @var{stats}] = signrank (@dots{})} also returns ## the structure @var{stats} containing the following fields: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Field} @tab @tab @var{Value} ## @item @qcode{signedrank} @tab @tab Value of the sign rank test statistic. ## ## @item @qcode{zval} @tab @tab Value of the @math{z}-statistic (only computed ## when the @qcode{"method"} is @qcode{"approximate"}). ## @end multitable ## ## @seealso{tiedrank, signtest, runstest} ## @end deftypefn function [p, h, stats] = signrank (x, my, varargin) ## Check X being a vector if (! isvector (x)) error ("signrank: X must be a vector."); endif ## Add defaults alpha = 0.05; tail = "both"; if (numel (x) <= 15) method = "exact"; else method = "approximate"; endif method_present = false; ## When called with a single input argument of second argument is empty if (nargin == 1 || isempty (my)) my = zeros (size (x)); endif ## If second argument is a scalar convert to vector or check for Y being a ## vector and that X and Y have equal lengths if (isscalar (my)) my = repmat (my, size (x)); elseif (! isvector (my)) error ("signrank: Y must be either a scalar of a vector."); elseif (numel (x) != numel (my)) error ("signrank: X and Y vectors have different lengths."); endif ## Get optional input arguments if (mod (numel (varargin), 2) != 0) error ("signrank: optional arguments must be in pairs."); endif while (numel (varargin) > 0) switch (lower (varargin{1})) case "alpha" alpha = varargin{2}; case "tail" tail = varargin{2}; case "method" method = varargin{2}; method_present = true; otherwise error ("signrank: invalid Name argument."); endswitch varargin([1:2]) = []; endwhile ## Check values for optional input arguments if (! isnumeric (alpha) || isnan (alpha) || ! isscalar (alpha) ... || alpha <= 0 || alpha >= 1) error ("signrank: 'alpha' must be a numeric scalar in the range 0 to 1."); endif if (! ischar (tail)) error ("signrank: 'tail' argument must be a character vector."); elseif (sum (strcmpi (tail, {"both", "right", "left"})) != 1) error ("signrank: 'tail' value must be either 'both', right' or 'left'."); endif if (! ischar (method)) error("signrank: 'method' argument must be a character vector."); elseif (sum (strcmpi (method, {"exact", "approximate"})) != 1) error ("signrank: 'method' value must be either 'exact' or 'approximate'."); endif ## Calculate differences between X and Y vectors: remove equal values of NaNs XY_diff = x(:) - my(:); NO_diff = (XY_diff == 0); XY_diff(NO_diff | isnan (NO_diff)) = []; ## Recalculate remaining length of X vector (after equal or NaNs removal) n = length (XY_diff); ## Check for identical X and Y input arguments if (n == 0) p = 1; h = 0; stats.sign = 0; stats.zval = NaN; return; endif ## Re-evaluate method selection if (! method_present) if (n <= 15) method = "exact"; else method = "approximate"; endif endif ## Compute signed rank statistic [tie_rank, tieadj] = tiedrank (abs (XY_diff)); w = sum (tie_rank(XY_diff > 0)); stats.signedrank = w; ## Calculate stats according to selected method and tail switch (lower (method)) case "exact" w_max = n * (n + 1) / 2; ## Always compute lower tail switch_tail = false; if (w > w_max / 2) w = w_max - w; switch_tail = true; endif ## Avoid integers in tied ranks double_ties = any (tie_rank != fix (tie_rank)); if (double_ties) tie_rank = round (2 * tie_rank); w = round (2 * w); endif ## Loop through all combinations of ranks C = zeros (w + 1,1); C(1) = 1; curr = 1; tie_rank = sort (tie_rank); w_tr = tie_rank(tie_rank <= w); for tr = 1:numel (w_tr) next = min (curr + w_tr(tr), w + 1); C_hi = min (w_tr(tr), w + 1) + 1:next; C_lo = 1:length (C_hi); C(C_hi) = C(C_hi) + C(C_lo); curr = next; endfor ## Fix rank statistic if (double_ties) w = w / 2; endif ## Compute tail probability C = C / (2 ^ n); p = sum (C); switch (lower (tail)) case 'both' p = min (1, 2 * p); # two-sided case 'right' if (! switch_tail) # right tail is larger p = 1 - p + C(end); endif case 'left' if (switch_tail) # left tail is larger p = 1 - p + C(end); endif endswitch stats.zval = NaN; case "approximate" ## Compute z-value z_nom = w - n * (n + 1) / 4; z_den = sqrt ((n * (n + 1) * (2 * n + 1) - tieadj) / 24); switch (lower (tail)) case 'both' z = z_nom / z_den; p = 2 * normcdf (-abs (z)); case 'right' z = (z_nom - 0.5) / z_den; p = normcdf (-z); case 'left' z = (z_nom + 0.5) / z_den; p = normcdf (z); endswitch stats.zval = z; endswitch h = p <= alpha; endfunction ## Test output %!test %! load gradespaired.mat %! [p, h, stats] = signrank (gradespaired(:,1), ... %! gradespaired(:,2), 'tail', 'left'); %! assert (p, 0.0047, 1e-4); %! assert (h, true); %! assert (stats.zval, -2.5982, 1e-4); %! assert (stats.signedrank, 2017.5); %!test %! load ('gradespaired.mat'); %! [p, h, stats] = signrank (gradespaired(:,1), gradespaired(:,2), ... %! 'tail', 'left', 'method', 'exact'); %! assert (p, 0.0045, 1e-4); %! assert (h, true); %! assert (stats.zval, NaN); %! assert (stats.signedrank, 2017.5); %!test %! load mileage %! [p, h, stats] = signrank (mileage(:,2), 33); %! assert (p, 0.0312, 1e-4); %! assert (h, true); %! assert (stats.zval, NaN); %! assert (stats.signedrank, 21); %!test %! load mileage %! [p, h, stats] = signrank (mileage(:,2), 33, 'tail', 'right'); %! assert (p, 0.0156, 1e-4); %! assert (h, true); %! assert (stats.zval, NaN); %! assert (stats.signedrank, 21); %!test %! load mileage %! [p, h, stats] = signrank (mileage(:,2), 33, 'tail', 'right', ... %! 'alpha', 0.01, 'method', 'approximate'); %! assert (p, 0.0180, 1e-4); %! assert (h, false); %! assert (stats.zval, 2.0966, 1e-4); %! assert (stats.signedrank, 21); ## Test input validation %!error signrank (ones (2)) %!error ... %! signrank ([1, 2, 3, 4], ones (2)) %!error ... %! signrank ([1, 2, 3, 4], [1, 2, 3]) %!error ... %! signrank ([1, 2, 3, 4], [], 'tail') %!error ... %! signrank ([1, 2, 3, 4], [], 'alpha', 1.2) %!error ... %! signrank ([1, 2, 3, 4], [], 'alpha', 0) %!error ... %! signrank ([1, 2, 3, 4], [], 'alpha', -0.05) %!error ... %! signrank ([1, 2, 3, 4], [], 'alpha', "a") %!error ... %! signrank ([1, 2, 3, 4], [], 'alpha', [0.01, 0.05]) %!error ... %! signrank ([1, 2, 3, 4], [], 'tail', 0.01) %!error ... %! signrank ([1, 2, 3, 4], [], 'tail', {"both"}) %!error ... %! signrank ([1, 2, 3, 4], [], 'tail', "some") %!error ... %! signrank ([1, 2, 3, 4], [], 'method', 'exact', 'tail', "some") %!error ... %! signrank ([1, 2, 3, 4], [], 'method', 0.01) %!error ... %! signrank ([1, 2, 3, 4], [], 'method', {"exact"}) %!error ... %! signrank ([1, 2, 3, 4], [], 'method', "some") %!error ... %! signrank ([1, 2, 3, 4], [], 'tail', "both", 'method', "some") statistics-release-1.7.3/inst/signtest.m000066400000000000000000000262531475240274700203510ustar00rootroot00000000000000## Copyright (C) 2014 Tony Richardson ## Copyright (C) 2022-2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{pval} =} signtest (@var{x}) ## @deftypefnx {statistics} {@var{pval} =} signtest (@var{x}, @var{my}) ## @deftypefnx {statistics} {@var{pval} =} signtest (@var{x}, @var{my}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{pval}, @var{h}] =} signtest (@dots{}) ## @deftypefnx {statistics} {[@var{pval}, @var{h}, @var{stats}] =} signtest (@dots{}) ## ## Signed test for median. ## ## @code{@var{pval} = signtest (@var{x})} returns the @math{p}-value of a ## two-sided sign test. It tests the null hypothesis that data in @var{x} come ## from a distribution with zero median at the 5% significance level. @var{x} ## must be a vector. ## ## If the second argument @var{my} is a scalar, the null hypothesis is that ## @var{x} has median @var{my}, whereas if @var{my} is a vector, the null ## hypothesis is that the distribution of @code{@var{x} - @var{my}} has zero ## median. ## ## @code{@var{pval} = signtest (@dots{}, @var{Name}, @var{Value})} performs the ## Wilcoxon signed rank test with additional options specified by one or more of ## the following @var{Name}, @var{Value} pair arguments: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"alpha"} @tab @tab A scalar value for the significance level of ## the test. Default is 0.05. ## ## @item @qcode{"tail"} @tab @tab A character vector specifying the alternative ## hypothesis. It can take one of the following values: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"both"} @tab For one-sample test (@var{my} is empty or a ## scalar), the data in @var{x} come from a continuous distribution with median ## different than zero or @var{my}. For two-sample test (@var{my} is a vector), ## the data in @qcode{@var{x} - @var{my}} come from a continuous distribution ## with median different than zero. ## ## @item @tab @qcode{"left"} @tab For one-sample test (@var{my} is empty or a ## scalar), the data in @var{x} come from a continuous distribution with median ## less than zero or @var{my}. For two-sample test (@var{my} is a vector), the ## data in @qcode{@var{x} - @var{my}} come from a continuous distribution with ## median less than zero. ## ## @item @tab @qcode{"right"} @tab For one-sample test (@var{my} is empty or a ## scalar), the data in @var{x} come from a continuous distribution with median ## greater than zero or @var{my}. For two-sample test (@var{my} is a vector), ## the data in @qcode{@var{x} - @var{my}} come from a continuous distribution ## with median greater than zero. ## @end multitable ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Name} @tab @tab @var{Value} ## ## @item @qcode{"method"} @tab @tab A character vector specifying the method for ## computing the @math{p}-value. It can take one of the following values: ## @end multitable ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Value} @tab @var{Description} ## ## @item @tab @qcode{"exact"} @tab Exact computation of the @math{p}-value. It ## is the default value for fewer than 100 observations when @qcode{"method"} is ## not specified. ## ## @item @tab @qcode{"approximate"} @tab Using normal approximation for ## computing the @math{p}-value. It is the default value for 100 or more ## observations when @qcode{"method"} is not specified. ## @end multitable ## ## @code{[@var{pval}, @var{h}] = signtest (@dots{})} also returns a logical ## value indicating the test decision. If @var{h} is 0, the null hypothesis is ## accepted, whereas if @var{h} is 1, the null hypothesis is rejected. ## ## @code{[@var{pval}, @var{h}, @var{stats}] = signtest (@dots{})} also returns ## the structure @var{stats} containing the following fields: ## ## @multitable @columnfractions 0.18 0.02 0.8 ## @headitem @var{Field} @tab @tab @var{Value} ## @item @qcode{sign} @tab @tab Value of the sign test statistic. ## ## @item @qcode{zval} @tab @tab Value of the @math{z}-statistic (only computed ## when the @qcode{"method"} is @qcode{"approximate"}). ## @end multitable ## ## @seealso{signrank, tiedrank, runstest} ## @end deftypefn function [p, h, stats] = signtest (x, my, varargin) ## Check X being a vector if (! isvector (x)) error ("signtest: X must be a vector."); endif ## Add defaults alpha = 0.05; tail = "both"; if (numel (x) < 100) method = "exact"; else method = "approximate"; endif method_present = false; ## When called with a single input argument of second argument is empty if (nargin == 1 || isempty (my)) my = zeros (size (x)); endif ## If second argument is a scalar convert to vector or check for Y being a ## vector and that X and Y have equal lengths if (isscalar (my)) my = repmat (my, size (x)); elseif (! isvector (my)) error ("signtest: Y must be either a scalar of a vector."); elseif (numel (x) != numel (my)) error ("signtest: X and Y vectors have different lengths."); endif ## Get optional input arguments if (mod (numel (varargin), 2) != 0) error ("signtest: optional arguments must be in pairs."); endif while (numel (varargin) > 0) switch (lower (varargin{1})) case "alpha" alpha = varargin{2}; case "tail" tail = varargin{2}; case "method" method = varargin{2}; method_present = true; otherwise error ("signtest: invalid Name argument."); endswitch varargin([1:2]) = []; endwhile ## Check values for optional input arguments if (! isnumeric (alpha) || isnan (alpha) || ! isscalar (alpha) ... || alpha <= 0 || alpha >= 1) error ("signtest: 'alpha' must be a numeric scalar in the range 0 to 1."); endif if (! ischar (tail)) error ("signtest: 'tail' argument must be a character vector."); elseif (sum (strcmpi (tail, {"both", "right", "left"})) != 1) error ("signtest: 'tail' value must be either 'both', right' or 'left'."); endif if (! ischar (method)) error("signtest: 'method' argument must be a character vector."); elseif (sum (strcmpi (method, {"exact", "approximate"})) != 1) error ("signtest: 'method' value must be either 'exact' or 'approximate'."); endif ## Calculate differences between X and Y vectors: remove equal values of NaNs XY_diff = x(:) - my(:); NO_diff = (XY_diff == 0); XY_diff(NO_diff | isnan (NO_diff)) = []; ## Recalculate remaining length of X vector (after equal or NaNs removal) n = length (XY_diff); ## Check for identical X and Y input arguments if (n == 0) p = 1; h = 0; stats.sign = 0; stats.zval = NaN; return; endif ## Re-evaluate method selection if (! method_present) if (n < 100) method = "exact"; else method = "approximate"; endif endif ## Get the number of positive and negative elements from X-Y differences pos_n = length (find (XY_diff > 0)); neg_n = n - pos_n; ## Calculate stats according to selected method and tail switch (lower (method)) case "exact" switch (lower (tail)) case "both" p = 2 * binocdf (min (neg_n, pos_n), n, 0.5); p = min (1, p); case "left" p = binocdf (pos_n, n, 0.5); case "right" p = binocdf (neg_n, n, 0.5); endswitch stats.zval = NaN; case 'approximate' switch (lower (tail)) case 'both' z_value = (pos_n - neg_n - sign (pos_n - neg_n)) / sqrt (n); p = 2 * normcdf (- abs (z_value)); case 'left' z_value = (pos_n - neg_n + 1) / sqrt (n); p = normcdf (z_value); case 'right' z_value = (pos_n - neg_n - 1) / sqrt (n); p = normcdf (- z_value); endswitch stats.zval = z_value; endswitch stats.sign = pos_n; h = double (p < alpha); endfunction ## Test output %!test %! [pval, h, stats] = signtest ([-ones(1, 1000) 1], 0, "tail", "left"); %! assert (pval, 1.091701889420221e-218, 1e-14); %! assert (h, 1); %! assert (stats.zval, -31.5437631079266, 1e-14); %!test %! [pval, h, stats] = signtest ([-2 -1 0 2 1 3 1], 0); %! assert (pval, 0.6875000000000006, 1e-14); %! assert (h, 0); %! assert (stats.zval, NaN); %! assert (stats.sign, 4); %!test %! [pval, h, stats] = signtest ([-2 -1 0 2 1 3 1], 0, "method", "approximate"); %! assert (pval, 0.6830913983096086, 1e-14); %! assert (h, 0); %! assert (stats.zval, 0.4082482904638631, 1e-14); %! assert (stats.sign, 4); ## Test input validation %!error signtest (ones (2)) %!error ... %! signtest ([1, 2, 3, 4], ones (2)) %!error ... %! signtest ([1, 2, 3, 4], [1, 2, 3]) %!error ... %! signtest ([1, 2, 3, 4], [], 'tail') %!error ... %! signtest ([1, 2, 3, 4], [], 'alpha', 1.2) %!error ... %! signtest ([1, 2, 3, 4], [], 'alpha', 0) %!error ... %! signtest ([1, 2, 3, 4], [], 'alpha', -0.05) %!error ... %! signtest ([1, 2, 3, 4], [], 'alpha', "a") %!error ... %! signtest ([1, 2, 3, 4], [], 'alpha', [0.01, 0.05]) %!error ... %! signtest ([1, 2, 3, 4], [], 'tail', 0.01) %!error ... %! signtest ([1, 2, 3, 4], [], 'tail', {"both"}) %!error ... %! signtest ([1, 2, 3, 4], [], 'tail', "some") %!error ... %! signtest ([1, 2, 3, 4], [], 'method', 'exact', 'tail', "some") %!error ... %! signtest ([1, 2, 3, 4], [], 'method', 0.01) %!error ... %! signtest ([1, 2, 3, 4], [], 'method', {"exact"}) %!error ... %! signtest ([1, 2, 3, 4], [], 'method', "some") %!error ... %! signtest ([1, 2, 3, 4], [], 'tail', "both", 'method', "some") statistics-release-1.7.3/inst/silhouette.m000066400000000000000000000165361475240274700207010ustar00rootroot00000000000000## Copyright (C) 2016 Nan Zhou ## Copyright (C) 2021 Stefano Guidoni ## Copyright (C) 2024 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} silhouette (@var{X}, @var{clust}) ## @deftypefnx {statistics} {[@var{si}, @var{h}] =} silhouette (@var{X}, @var{clust}) ## @deftypefnx {statistics} {[@var{si}, @var{h}] =} silhouette (@dots{}, @var{Metric}, @var{MetricArg}) ## ## Compute the silhouette values of clustered data and show them on a plot. ## ## @var{X} is a n-by-p matrix of n data points in a p-dimensional space. Each ## datapoint is assigned to a cluster using @var{clust}, a vector of n elements, ## one cluster assignment for each data point. ## ## Each silhouette value of @var{si}, a vector of size n, is a measure of the ## likelihood that a data point is accurately classified to the right cluster. ## Defining "a" as the mean distance between a point and the other points from ## its cluster, and "b" as the mean distance between that point and the points ## from other clusters, the silhouette value of the i-th point is: ## ## @tex ## \def\frac#1#2{{\begingroup#1\endgroup\over#2}} ## $$ S_i = \frac{b_i - a_i}{max(a_1,b_i)} $$ ## @end tex ## @ifnottex ## @verbatim ## bi - ai ## Si = ------------ ## max(ai,bi) ## @end verbatim ## @end ifnottex ## ## Each element of @var{si} ranges from -1, minimum likelihood of a correct ## classification, to 1, maximum likelihood. ## ## Optional input value @var{Metric} is the metric used to compute the distances ## between data points. Since @code{silhouette} uses @code{pdist} to compute ## these distances, @var{Metric} is similar to the @var{Distance} input argument ## of @code{pdist} and it can be: ## @itemize @bullet ## @item A known distance metric defined as a string: @qcode{euclidean}, ## @qcode{squaredeuclidean} (default), @qcode{seuclidean}, @qcode{mahalanobis}, ## @qcode{cityblock}, @qcode{minkowski}, @qcode{chebychev}, @qcode{cosine}, ## @qcode{correlation}, @qcode{hamming}, @qcode{jaccard}, or @qcode{spearman}. ## ## @item A vector as those created by @code{pdist}. In this case @var{X} does ## nothing. ## ## @item A function handle that is passed to @code{pdist} with @var{MetricArg} ## as optional inputs. ## @end itemize ## ## Optional return value @var{h} is a handle to the silhouette plot. ## ## @strong{Reference} ## Peter J. Rousseeuw, Silhouettes: a Graphical Aid to the Interpretation and ## Validation of Cluster Analysis. 1987. doi:10.1016/0377-0427(87)90125-7 ## @end deftypefn ## ## @seealso{dendrogram, evalclusters, kmeans, linkage, pdist} function [si, h] = silhouette (X, clust, metric = "squaredeuclidean", varargin) ## check the input parameters if (nargin < 2) print_usage (); endif ## Check for last argument being 'DoNotPlot' to prevent from opening a figure ## Undocumented feature to avoid issues with faiing tests. It is only used by ## SilhouetteEvaluation class. DisplayPlot = true; if (numel (varargin) > 0) if (ischar (varargin{end})) if (strcmp (varargin{end}, "DoNotPlot")) DisplayPlot = false; varargin{end} = []; endif endif endif n = size (clust, 1); ## check size if (! isempty (X)) if (size (X, 1) != n) error ("First dimension of X <%d> doesn't match that of clust <%d>",... size (X, 1), n); endif endif ## check metric if (ischar (metric)) metric = lower (metric); switch (metric) case "sqeuclidean" metric = "squaredeuclidean"; case {"euclidean", "squaredeuclidean", "seuclidean", "mahalanobis", ... "cityblock", "minkowski", "chebychev", "cosine", "correlation", ... "hamming", "jaccard", "spearman"} ; otherwise error ("silhouette: invalid metric '%s'", metric); endswitch elseif (isnumeric (metric) && isvector (metric)) ## X can be omitted when using this distMatrix = squareform (metric); if (size (distMatrix, 1) != n) error ("First dimension of X <%d> doesn't match that of clust <%d>",... size (distMatrix, 1), n); endif endif ## main si = zeros(n, 1); clusterIDs = unique (clust); # eg [1; 2; 3; 4] m = length (clusterIDs); ## if only one cluster is defined, the silhouette value is not defined if (m == 1) si = NaN * ones (n, 1); return; endif ## distance matrix showing the distance for any two rows of X if (! exist ('distMatrix', 'var')) distMatrix = squareform (pdist (X, metric, varargin{:})); endif ## calculate values of si one by one for iii = 1 : length (si) ## allocate values to clusters groupedValues = {}; for jjj = 1 : m groupedValues{clusterIDs(jjj)} = [distMatrix(iii, ... clust == clusterIDs(jjj))]; endfor ## end allocation ## calculate a(i) ## average distance of iii to all other objects in the same cluster if (length (groupedValues{clust(iii)}) == 1) si(iii) = 1; continue; else a_i = (sum (groupedValues{clust(iii)})) / ... (length (groupedValues{clust(iii)}) - 1); endif ## end a(i) ## calculate b(i) clusterIDs_new = clusterIDs; ## remove the cluster iii in clusterIDs_new(find (clusterIDs_new == clust(iii))) = []; ## average distance of iii to all objects of another cluster a_iii_2others = zeros (length (clusterIDs_new), 1); for jjj = 1 : length (clusterIDs_new) a_iii_2others(jjj) = mean (groupedValues{clusterIDs_new(jjj)}); endfor b_i = min (a_iii_2others); ## end b(i) ## calculate s(i) si(iii) = (b_i - a_i) / (max ([a_i; b_i])); ## end s(i) endfor ## plot ## a poor man silhouette graph if (DisplayPlot) vBarsc = zeros (m, 1); vPadding = [0; 0; 0; 0]; Bars = vPadding; for i = 1 : m vBar = si(find (clust == clusterIDs(i))); vBarsc(i) = length (Bars) + (length (vBar) / 2); Bars = [Bars; (sort (vBar, "descend")); vPadding]; endfor figure(); h = barh (Bars, "hist", "facecolor", [0 0.4471 0.7412]); xlabel ("Silhouette Value"); ylabel ("Cluster"); set (gca, "ytick", vBarsc, "yticklabel", clusterIDs); ylim ([0 (length (Bars))]); axis ("ij"); endif endfunction %!demo %! load fisheriris; %! X = meas(:,3:4); %! cidcs = kmeans (X, 3, "Replicates", 5); %! silhouette (X, cidcs); %! y_labels(cidcs([1 51 101])) = unique (species); %! set (gca, "yticklabel", y_labels); %! title ("Fisher's iris data"); ## Test input validation %!error silhouette (); %!error silhouette ([1 2; 1 1]); %!error silhouette ([1 2; 1 1], [1 2 3]'); %!error silhouette ([1 2; 1 1], [1 2]', "xxx"); statistics-release-1.7.3/inst/slicesample.m000066400000000000000000000220271475240274700210050ustar00rootroot00000000000000## Copyright (C) 1995-2022 The Octave Project Developers ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{smpl}, @var{neval}] =} slicesample (@var{start}, @var{nsamples}, @var{property}, @var{value}, @dots{}) ## ## Draws @var{nsamples} samples from a target stationary distribution @var{pdf} ## using slice sampling of Radford M. Neal. ## ## Input: ## @itemize ## @item ## @var{start} is a 1 by @var{dim} vector of the starting point of the ## Markov chain. Each column corresponds to a different dimension. ## ## @item ## @var{nsamples} is the number of samples, the length of the Markov chain. ## @end itemize ## ## Next, several property-value pairs can or must be specified, they are: ## ## (Required properties) One of: ## ## @itemize ## @item ## @var{"pdf"}: the value is a function handle of the target stationary ## distribution to be sampled. The function should accept different locations ## in each row and each column corresponds to a different dimension. ## ## or ## ## @item ## @var{logpdf}: the value is a function handle of the log of the target ## stationary distribution to be sampled. The function should accept different ## locations in each row and each column corresponds to a different dimension. ## @end itemize ## ## The following input property/pair values may be needed depending on the ## desired outut: ## ## @itemize ## @item ## "burnin" @var{burnin} the number of points to discard at the beginning, the default ## is 0. ## ## @item ## "thin" @var{thin} omitts @var{m}-1 of every @var{m} points in the generated ## Markov chain. The default is 1. ## ## @item ## "width" @var{width} the maximum Manhattan distance between two samples. ## The default is 10. ## @end itemize ## ## Outputs: ## @itemize ## ## @item ## @var{smpl} is a @var{nsamples} by @var{dim} matrix of random ## values drawn from @var{pdf} where the rows are different random values, the ## columns correspond to the dimensions of @var{pdf}. ## ## @item ## @var{neval} is the number of function evaluations per sample. ## @end itemize ## Example : Sampling from a normal distribution ## ## @example ## @group ## start = 1; ## nsamples = 1e3; ## pdf = @@(x) exp (-.5 * x .^ 2) / (pi ^ .5 * 2 ^ .5); ## [smpl, accept] = slicesample (start, nsamples, "pdf", pdf, "thin", 4); ## histfit (smpl); ## @end group ## @end example ## ## @seealso{rand, mhsample, randsample} ## @end deftypefn function [smpl, neval] = slicesample (start, nsamples, varargin) if (nargin < 4) error ("slicesample: function called with too few input arguments."); endif sizestart = size (start); pdf = []; logpdf = []; width = 10; burnin = 0; thin = 1; for k = 1:2:length (varargin) if (ischar (varargin{k})) switch lower (varargin{k}) case "pdf" if (isa (varargin{k+1}, "function_handle")) pdf = varargin{k+1}; else error ("slicesample: pdf must be a function handle."); endif case "logpdf" if (isa (varargin{k+1}, "function_handle")) pdf = varargin{k+1}; else error ("slicesample: logpdf must be a function handle."); endif case "width" if (numel (varargin{k+1}) == 1 || numel (varargin{k+1}) == sizestart(2)) width = varargin{k+1}(:).'; else error ("slicesample: width must be a scalar or 1 by dim vector."); endif case "burnin" if (varargin{k+1}>=0) burnin = varargin{k+1}; else error ("slicesample: burnin must be greater than or equal to 0."); endif case "thin" if (varargin{k+1}>=1) thin = varargin{k+1}; else error ("slicesample: thin must be greater than or equal to 1."); endif otherwise warning (["slicesample: Ignoring unknown option " varargin{k}]); endswitch else error (["slicesample: " varargin{k} " is not a valid property."]); endif endfor if (! isempty (pdf) && isempty (logpdf)) logpdf = @(x) rloge (pdf (x)); elseif (isempty (pdf) && isempty (logpdf)) error ("slicesample: pdf or logpdf must be input."); endif dim = sizestart(2); smpl = zeros (nsamples, dim); if (all (sizestart == [1 dim])) smpl(1, :) = start; else error ("slicesample: start must be a 1 by dim vector."); endif maxit = 100; neval = 0; fgraterthan = @(x, fxc) logpdf (x) >= fxc; ti = burnin + nsamples * thin; rndexp = rande (ti, 1); crand = rand (ti, dim); prand = rand (ti, dim); xc = smpl(1, :); for i = 1:ti neval++; sliceheight = logpdf (xc) - rndexp(i); c = width .* crand(i, :); lb = xc - c; ub = xc + width - c; #Only for single variable as bounds can not be found with point when dim > 1 if (dim == 1) for k=1:maxit neval++; if (! fgraterthan (lb, sliceheight)) break endif lb -= width; end if (k == maxit) warning ("slicesample: Step out exceeded maximum iterations"); endif for k = 1:maxit neval++; if (! fgraterthan (ub, sliceheight)) break endif ub += width; end if (k == maxit) warning ("slicesample: Step out exceeded maximum iterations"); endif end xp = (ub - lb) .* prand(i, :) + lb; for k=1:maxit neval++; isgt = fgraterthan (xp,sliceheight); if (all (isgt)) break endif lc = ! isgt & xp < xc; uc = ! isgt & xp > xc; lb(lc) = xp(lc); ub(uc) = xp(uc); xp = (ub - lb) .* rand (1, dim) + lb; end if (k == maxit) warning ("slicesample: Step in exceeded maximum iterations"); endif xc = xp; if (i > burnin) indx = (i - burnin) / thin; if rem (indx, 1) == 0 smpl(indx, :) = xc; end end end neval = neval / (nsamples * thin + burnin); endfunction function y = rloge (x) y = -inf (size (x)); xg0 = x > 0; y(xg0) = log (x(xg0)); endfunction %!demo %! ## Define function to sample %! d = 2; %! mu = [-1; 2]; %! rand ("seed", 5) # for reproducibility %! Sigma = rand (d); %! Sigma = (Sigma + Sigma'); %! Sigma += eye (d)*abs (eigs (Sigma, 1, "sa")) * 1.1; %! pdf = @(x)(2*pi)^(-d/2)*det(Sigma)^-.5*exp(-.5*sum((x.'-mu).*(Sigma\(x.'-mu)),1)); %! %! ## Inputs %! start = ones (1,2); %! nsamples = 500; %! K = 500; %! m = 10; %! rande ("seed", 4); rand ("seed", 5) # for reproducibility %! [smpl, accept] = slicesample (start, nsamples, "pdf", pdf, "burnin", K, "thin", m, "width", [20, 30]); %! figure; %! hold on; %! plot (smpl(:,1), smpl(:,2), 'x'); %! [x, y] = meshgrid (linspace (-6,4), linspace(-3,7)); %! z = reshape (pdf ([x(:), y(:)]), size(x)); %! mesh (x, y, z, "facecolor", "None"); %! %! ## Using sample points to find the volume of half a sphere with radius of .5 %! f = @(x) ((.25-(x(:,1)+1).^2-(x(:,2)-2).^2).^.5.*(((x(:,1)+1).^2+(x(:,2)-2).^2)<.25)).'; %! int = mean (f (smpl) ./ pdf (smpl)); %! errest = std (f (smpl) ./ pdf (smpl)) / nsamples^.5; %! trueerr = abs (2/3*pi*.25^(3/2)-int); %! fprintf ("Monte Carlo integral estimate int f(x) dx = %f\n", int); %! fprintf ("Monte Carlo integral error estimate %f\n", errest); %! fprintf ("The actual error %f\n", trueerr); %! mesh (x,y,reshape (f([x(:), y(:)]), size(x)), "facecolor", "None"); %!demo %! ## Integrate truncated normal distribution to find normilization constant %! pdf = @(x) exp (-.5*x.^2)/(pi^.5*2^.5); %! nsamples = 1e3; %! rande ("seed", 4); rand ("seed", 5) # for reproducibility %! [smpl, accept] = slicesample (1, nsamples, "pdf", pdf, "thin", 4); %! f = @(x) exp (-.5 * x .^ 2) .* (x >= -2 & x <= 2); %! x = linspace (-3, 3, 1000); %! area (x, f(x)); %! xlabel ("x"); %! ylabel ("f(x)"); %! int = mean (f (smpl) ./ pdf (smpl)); %! errest = std (f (smpl) ./ pdf (smpl)) / nsamples ^ 0.5; %! trueerr = abs (erf (2 ^ 0.5) * 2 ^ 0.5 * pi ^ 0.5 - int); %! fprintf("Monte Carlo integral estimate int f(x) dx = %f\n", int); %! fprintf("Monte Carlo integral error estimate %f\n", errest); %! fprintf("The actual error %f\n", trueerr); ## Test output %!test %! start = 0.5; %! nsamples = 1e3; %! pdf = @(x) exp (-.5*(x-1).^2)/(2*pi)^.5; %! [smpl, accept] = slicesample (start, nsamples, "pdf", pdf, "thin", 2, "burnin", 0, "width", 5); %! assert (mean (smpl, 1), 1, .15); %! assert (var (smpl, 1), 1, .25); ## Test input validation %!error slicesample (); %!error slicesample (1); %!error slicesample (1, 1); statistics-release-1.7.3/inst/squareform.m000066400000000000000000000103031475240274700206620ustar00rootroot00000000000000## Copyright (C) 2015 Carnë Draug ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{z} =} squareform (@var{y}) ## @deftypefnx {statistics} {@var{y} =} squareform (@var{z}) ## @deftypefnx {statistics} {@var{z} =} squareform (@var{y}, @qcode{"tovector"}) ## @deftypefnx {statistics} {@var{y} =} squareform (@var{z}, @qcode{"tomatrix"}) ## ## Interchange between distance matrix and distance vector formats. ## ## Converts between an hollow (diagonal filled with zeros), square, and ## symmetric matrix and a vector with of the lower triangular part. ## ## Its target application is the conversion of the vector returned by ## @code{pdist} into a distance matrix. It performs the opposite operation ## if input is a matrix. ## ## If @var{x} is a vector, its number of elements must fit into the ## triangular part of a matrix (main diagonal excluded). In other words, ## @code{numel (@var{x}) = @var{n} * (@var{n} - 1) / 2} for some integer ## @var{n}. The resulting matrix will be @var{n} by @var{n}. ## ## If @var{x} is a distance matrix, it must be square and the diagonal entries ## of @var{x} must all be zeros. @code{squareform} will generate a warning if ## @var{x} is not symmetric. ## ## The second argument is used to specify the output type in case there ## is a single element. It will defaults to @qcode{"tomatrix"} otherwise. ## ## @seealso{pdist} ## @end deftypefn function y = squareform (x, method) if (nargin < 1 || nargin > 2) print_usage (); elseif (! isnumeric (x) || ! ismatrix (x)) error ("squareform: Y or Z must be a numeric matrix or vector."); endif if (nargin == 1) ## This is ambiguous when numel (x) == 1, but that's the whole reason ## why the "method" option exists. if (isvector (x)) method = "tomatrix"; else method = "tovector"; endif endif switch (tolower (method)) case "tovector" if (! issquare (x)) error ("squareform: Z is not a square matrix."); elseif (any (diag (x) != 0)) error ("squareform: Z is not a hollow matrix."); elseif (! issymmetric(x)) warning ("squareform:symmetric", "squareform: Z is not a symmetric matrix"); endif y = vec (tril (x, -1, "pack"), 2); case "tomatrix" ## the dimensions of y are the solution to the quadratic formula for: ## length (x) = (sy - 1) * (sy / 2) sy = (1 + sqrt (1 + 8 * numel (x))) / 2; if (fix (sy) != sy) error ("squareform: the numel of Y cannot form a square matrix."); endif y = zeros (sy, class (x)); y(tril (true (sy), -1)) = x; # fill lower triangular part y += y.'; # and then the upper triangular part otherwise error ("squareform: invalid METHOD '%s'.", method); endswitch endfunction %!shared v, m %! v = 1:6; %! m = [0 1 2 3;1 0 4 5;2 4 0 6;3 5 6 0]; ## make sure that it can go both directions automatically %!assert (squareform (v), m) %!assert (squareform (squareform (v)), v) %!assert (squareform (m), v) ## treat row and column vectors equally %!assert (squareform (v'), m) ## handle 1 element input properly %!assert (squareform (1), [0 1;1 0]) %!assert (squareform (1, "tomatrix"), [0 1; 1 0]) %!assert (squareform (0, "tovector"), zeros (1, 0)) %!warning squareform ([0 1 2; 3 0 4; 5 6 0]); ## confirm that it respects input class %!test %! for c = {@single, @double, @uint8, @uint32, @uint64} %! f = c{1}; %! assert (squareform (f (v)), f (m)) %! assert (squareform (f (m)), f (v)) %! endfor statistics-release-1.7.3/inst/standardizeMissing.m000066400000000000000000000164431475240274700223530ustar00rootroot00000000000000## Copyright (C) 1995-2023 The Octave Project Developers ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{B} =} standardizeMissing (@var{A}, @var{indicator}) ## ## Replace data values specified by @var{indicator} in @var{A} by the ## standard 'missing' data value for that data type. ## ## @var{A} can be a numeric scalar or array, a character vector or array, or ## a cell array of character vectors (a.k.a. string cells). ## ## @var{indicator} can be a scalar or an array containing values to be ## replaced by the 'missing' value for the class of @var{A}, and should have ## a data type matching @var{A}. ## ## 'missing' values are defined as : ## ## @itemize ## @item ## @qcode{NaN}: @code{single}, @code{double} ## ## @item ## @qcode{" "} (white space): @code{char} ## ## @item ## @qcode{@{""@}} (empty string in cell): string cells. ## @end itemize ## ## Compatibility Notes: ## @itemize ## @item ## Octave's implementation of @code{standardizeMissing} ## does not restrict @var{indicator} of type @qcode{char} to row vectors. ## ## @item ## All numerical and logical inputs for @var{A} and @var{indicator} may ## be specified in any combination. The output will be the same class as ## @var{A}, with the @var{indicator} converted to that data type for ## comparison. Only @code{single} and @code{double} have defined 'missing' ## values, so @var{A} of other data types will always output ## @var{B} = @var{A}. ## @end itemize ## ## @end deftypefn ## ## @seealso{fillmissing, ismissing, rmmissing} function A = standardizeMissing (A, indicator) if (nargin != 2) print_usage (); endif input_class = class(A); do_nothing_flag = false; ## Set missing_val. ## Compatibility requirements: ## Numeric/logical: Only double and single have 'missing' values defined, ## so other numeric and logical pass through unchanged. ## Other: only char & cellstr have 'missing' values defined, others produce ## error. ## ## TODO: as implemented in Octave, add table, string, timetable, ## categorical, datetime, and duration to class checks and BISTs ## ## TODO: when 'missing' class is implemented, these switch blocks can all ## be removed and the final assignment updated to call missing instead of ## missing_val if (isnumeric(A) || islogical(A)) switch (input_class) case "double" missing_val = NaN ("double"); case "single" missing_val = NaN ("single"); otherwise do_nothing_flag = true; endswitch else switch (input_class) case "char" missing_val = " "; case "cell" if iscellstr(A) missing_val = {""}; else error ("stardardizeMissing: only cells of strings are supported."); endif otherwise error ("standardizeMissing: unsupported data type %s.", input_class); endswitch endif if (! do_nothing_flag) ## if A is an array of cell strings and indicator just a string, ## convert indicator to a cell string with one element if (iscellstr (A) && ischar (indicator) && ! iscellstr (indicator)) indicator = {indicator}; endif if ((isnumeric (A) && ! (isnumeric (indicator) || islogical (indicator))) || (ischar (A) && ! ischar (indicator)) || (iscellstr (A) && ! (iscellstr (indicator)))) error (strcat (["standardizeMissing: 'indicator' and 'A' must"], ... [" have the same data type."])); endif A(ismember (A, indicator)) = missing_val; endif endfunction ## numeric tests %!assert (standardizeMissing (1, 1), NaN) %!assert (standardizeMissing (1, 0), 1) %!assert (standardizeMissing (eye(2), 1), [NaN 0;0 NaN]) %!assert (standardizeMissing ([1:3;4:6], [2 3; 4 5]), [1, NaN, NaN; NaN, NaN, 6]) %!assert (standardizeMissing (cat (3,1,2,3,4), 3), cat (3,1,2,NaN,4)) ## char and cellstr tests %!assert (standardizeMissing ('foo', 'a'), 'foo') %!assert (standardizeMissing ('foo', 'f'), ' oo') %!assert (standardizeMissing ('foo', 'o'), 'f ') %!assert (standardizeMissing ('foo', 'oo'), 'f ') %!assert (standardizeMissing ({'foo'}, 'f'), {'foo'}) %!assert (standardizeMissing ({'foo'}, {'f'}), {'foo'}) %!assert (standardizeMissing ({'foo'}, 'test'), {'foo'}) %!assert (standardizeMissing ({'foo'}, {'test'}), {'foo'}) %!assert (standardizeMissing ({'foo'}, 'foo'), {''}) %!assert (standardizeMissing ({'foo'}, {'foo'}), {''}) ## char and cellstr array tests %!assert (standardizeMissing (['foo';'bar'], 'oar'), ['f ';'b ']) %!assert (standardizeMissing (['foo';'bar'], ['o';'a';'r']), ['f ';'b ']) %!assert (standardizeMissing (['foo';'bar'], ['o ';'ar']), ['f ';'b ']) %!assert (standardizeMissing ({'foo','bar'}, 'foo'), {'','bar'}) %!assert (standardizeMissing ({'foo','bar'}, 'f'), {'foo','bar'}) %!assert (standardizeMissing ({'foo','bar'}, {'foo', 'a'}), {'','bar'}) %!assert (standardizeMissing ({'foo'}, {'f', 'oo'}), {'foo'}) %!assert (standardizeMissing ({'foo','bar'}, {'foo'}), {'','bar'}) %!assert (standardizeMissing ({'foo','bar'}, {'foo', 'a'}), {'','bar'}) ## numeric type preservation tests %!assert (standardizeMissing (double (1), single (1)), double (NaN)) %!assert (standardizeMissing (single (1), single (1)), single (NaN)) %!assert (standardizeMissing (single (1), double (1)), single (NaN)) %!assert (standardizeMissing (single (1), true), single (NaN)) %!assert (standardizeMissing (double (1), int32(1)), double (NaN)) ## Passttrough tests %!assert (standardizeMissing (true, true), true) %!assert (standardizeMissing (true, 1), true) %!assert (standardizeMissing (int32 (1), int32 (1)), int32 (1)) %!assert (standardizeMissing (int32 (1), 1), int32 (1)) %!assert (standardizeMissing (uint32 (1), uint32 (1)), uint32 (1)) %!assert (standardizeMissing (uint32 (1), 1), uint32 (1)) ## Test input validation %!error standardizeMissing (); %!error standardizeMissing (1); %!error standardizeMissing (1,2,3); %!error standardizeMissing ({'abc', 1}, 1); %!error standardizeMissing (struct ('a','b'), 1); %!error <'indicator' and 'A' must have > standardizeMissing ([1 2 3], {1}); %!error <'indicator' and 'A' must have > standardizeMissing ([1 2 3], 'a'); %!error <'indicator' and 'A' must have > standardizeMissing ([1 2 3], struct ('a', 1)); %!error <'indicator' and 'A' must have > standardizeMissing ('foo', 1); %!error <'indicator' and 'A' must have > standardizeMissing ('foo', {1}); %!error <'indicator' and 'A' must have > standardizeMissing ('foo', {'f'}); %!error <'indicator' and 'A' must have > standardizeMissing ('foo', struct ('a', 1)); %!error <'indicator' and 'A' must have > standardizeMissing ({'foo'}, 1); %!error <'indicator' and 'A' must have > standardizeMissing ({'foo'}, 1); statistics-release-1.7.3/inst/stepwisefit.m000066400000000000000000000137661475240274700210640ustar00rootroot00000000000000## Copyright (C) 2013-2021 Nir Krakauer ## Copyright (C) 2014 Mikael Kurula ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{X_use}, @var{b}, @var{bint}, @var{r}, @var{rint}, @var{stats}] =} stepwisefit (@var{y}, @var{X}, @var{penter} = 0.05, @var{premove} = 0.1, @var{method} = "corr") ## ## Linear regression with stepwise variable selection. ## ## @subheading Arguments ## ## @itemize @bullet ## @item ## @var{y} is an @var{n} by 1 vector of data to fit. ## @item ## @var{X} is an @var{n} by @var{k} matrix containing the values of @var{k} potential predictors. No constant term should be included (one will always be added to the regression automatically). ## @item ## @var{penter} is the maximum p-value to enter a new variable into the regression (default: 0.05). ## @item ## @var{premove} is the minimum p-value to remove a variable from the regression (default: 0.1). ## @item ## @var{method} sets how predictors are selected at each step, either based on their correlation with the residuals ("corr", default) ## or on the p values of their regression coefficients when they are successively added ("p"). ## @end itemize ## ## @subheading Return values ## ## @itemize @bullet ## @item ## @var{X_use} contains the indices of the predictors included in the final regression model. The predictors are listed in the order they were added, so typically the first ones listed are the most significant. ## @item ## @var{b}, @var{bint}, @var{r}, @var{rint}, @var{stats} are the results of @code{[b, bint, r, rint, stats] = regress(y, [ones(size(y)) X(:, X_use)], penter);} ## @end itemize ## @subheading References ## ## @enumerate ## @item ## N. R. Draper and H. Smith (1966). @cite{Applied Regression Analysis}. Wiley. Chapter 6. ## ## @end enumerate ## @seealso{regress} ## @end deftypefn function [X_use, b, bint, r, rint, stats] = stepwisefit(y, X, penter = 0.05, premove = 0.1, method = "corr") if nargin >= 3 && isempty(penter) penter = 0.05; endif if nargin >= 4 && isempty(premove) premove = 0.1; endif #remove any rows with missing entries notnans = !any (isnan ([y X]) , 2); y = y(notnans); X = X(notnans,:); n = numel(y); #number of data points k = size(X, 2); #number of predictors X_use = []; v = 0; #number of predictor variables in regression model iter = 0; max_iters = 100; #maximum number of interations to do r = y; while 1 iter++; #decide which variable to add to regression, if any added = false; if numel(X_use) < k X_inds = zeros(k, 1, "logical"); X_inds(X_use) = 1; switch lower (method) case {"corr"} [~, i_to_add] = max(abs(corr(X(:, ~X_inds), r))); #try adding the variable with the highest correlation to the residual from current regression i_to_add = (1:k)(~X_inds)(i_to_add); #index within the original predictor set [b_new, bint_new, r_new, rint_new, stats_new] = regress(y, [ones(n, 1) X(:, [X_use i_to_add])], penter); case {"p"} z_vals=zeros(k,1); for j=1:k if ~X_inds(j) [b_j, bint_j, ~,~ ,~] = regress(y, [ones(n, 1) X(:, [X_use j])], penter); z_vals(j) = abs(b_j(end)) / (bint_j(end, 2) - b_j(end)); endif endfor [~, i_to_add] = max(z_vals); #try adding the variable with the largest z-value (smallest partial p-value) [b_new, bint_new, r_new, rint_new, stats_new] = regress(y, [ones(n, 1) X(:, [X_use i_to_add])], penter); otherwise error("stepwisefit: invalid value for method") endswitch z_new = abs(b_new(end)) / (bint_new(end, 2) - b_new(end)); if z_new > 1 #accept new variable added = true; X_use = [X_use i_to_add]; b = b_new; bint = bint_new; r = r_new; rint = rint_new; stats = stats_new; v = v + 1; endif endif #decide which variable to drop from regression, if any dropped = false; if v > 0 t_ratio = tinv(1 - premove/2, n - v - 1) / tinv(1 - penter/2, n - v - 1); #estimate the ratio between the z score corresponding to premove to that corresponding to penter [z_min, i_min] = min(abs(b(2:end)) ./ (bint(2:end, 2) - b(2:end))); if z_min < t_ratio #drop a variable dropped = true; X_use(i_min) = []; [b, bint, r, rint, stats] = regress(y, [ones(n, 1) X(:, X_use)], penter); v = v - 1; endif endif #terminate if no change in the list of regression variables if ~added && ~dropped break endif if iter >= max_iters warning('stepwisefit: maximum iteration count exceeded before convergence') break endif endwhile endfunction %!test %! % Sample data from Draper and Smith (n = 13, k = 4) %! X = [7 1 11 11 7 11 3 1 2 21 1 11 10; ... %! 26 29 56 31 52 55 71 31 54 47 40 66 68; ... %! 6 15 8 8 6 9 17 22 18 4 23 9 8; ... %! 60 52 20 47 33 22 6 44 22 26 34 12 12]'; %! y = [78.5 74.3 104.3 87.6 95.9 109.2 102.7 72.5 93.1 115.9 83.8 113.3 109.4]'; %! [X_use, b, bint, r, rint, stats] = stepwisefit(y, X); %! assert(X_use, [4 1]) %! assert(b, regress(y, [ones(size(y)) X(:, X_use)], 0.05)) %! [X_use, b, bint, r, rint, stats] = stepwisefit(y, X, 0.05, 0.1, "corr"); %! assert(X_use, [4 1]) %! assert(b, regress(y, [ones(size(y)) X(:, X_use)], 0.05)) %! [X_use, b, bint, r, rint, stats] = stepwisefit(y, X, [], [], "p"); %! assert(X_use, [4 1]) %! assert(b, regress(y, [ones(size(y)) X(:, X_use)], 0.05)) statistics-release-1.7.3/inst/tabulate.m000066400000000000000000000127131475240274700203060ustar00rootroot00000000000000## Copyright (C) 2003 Alberto Terruzzi ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} tabulate (@var{x}) ## @deftypefnx {statistics} {@var{table} =} tabulate (@var{x}) ## ## Calculate a frequency table. ## ## @code{tabulate (x)} displays a frequency table of the data in the vector ## @var{x}. For each unique value in @var{x}, the tabulate function shows the ## number of instances and percentage of that value in @var{x}. ## ## @code{@var{table} = tabulate (@var{x})} returns the frequency table, ## @var{table}, as a numeric matrix when @var{x} is numeric and as a cell array ## otherwise. When an output argument is requested, @code{tabulate} does not ## print the frequency table in the command window. ## ## If @var{x} is numeric, any missing values (@qcode{NaNs}) are ignored. ## ## If all the elements of @var{x} are positive integers, then the frequency ## table includes 0 counts for the integers between 1 and @qcode{max (@var{x})} ## that do not appear in @var{x}. ## ## @seealso{bar, pareto} ## @end deftypefn function table = tabulate (x) ## Check input for being either numeric or a cell array if (! (isnumeric (x) && isvector (x)) && ! (iscellstr (x) && isvector (x)) && ! ischar (x)) error (strcat (["tabulate: X must be either a numeric vector, a"], ... [" vector cell array of strings, or a character matrix."])); endif ## Remove missing values (NaNs) if numeric if (isnumeric (x)) x(isnan (x)) = []; endif ## Handle positive integers separately if (isnumeric (x) && all (x == fix (x)) && all (x > 0)); [count, value] = hist (x, (1:max (x))); posint = true; else [g, gn, gl] = grp2idx (x); [count, value] = hist (g, (1:length (gn))); posint = false; endif ## Calculate percentages percent = 100 * count ./ sum (count); ## Display results is no output argument if (nargout == 0) if (posint) fprintf (" Value Count Percent\n"); fprintf (" %5d %5d %6.2f%%\n", value', count', percent'); else valw = max (cellfun ("length", gn)); valw = max ([5, min([50, valw])]); header = sprintf (" %%%ds %%5s %%6s\n", valw); result = sprintf (" %%%ds %%5d %%6.2f%%%%\n", valw); fprintf (header, "Value", "Count", "Percent"); for i = 1:length (gn) fprintf (result, gn{i}, count(i), percent(i)); endfor endif ## Create output table else if (posint) table = [value', count', percent']; elseif (isnumeric (x)) table = [gl, count', percent']; else table = [gn, num2cell([count', percent'])]; endif endif endfunction %!demo %! ## Generate a frequency table for a vector of data in a cell array %! load patients %! %! ## Display the first seven entries of the Gender variable %! gender = Gender(1:7) %! %! ## Compute the equency table that shows the number and %! ## percentage of Male and Female patients %! tabulate (Gender) %!demo %! ## Create a frequency table for a vector of positive integers %! load patients %! %! ## Display the first seven entries of the Gender variable %! height = Height(1:7) %! %! ## Create a frequency table that shows, in its second and third columns, %! ## the number and percentage of patients with a particular height. %! table = tabulate (Height); %! %! ## Display the first and last seven entries of the frequency table %! first = table(1:7,:) %! %! last = table(end-6:end,:) %!demo %! ## Create a frequency table from a character array %! load carsmall %! %! ## Tabulate the data in the Origin variable, which shows the %! ## country of origin of each car in the data set %! tabulate (Origin) %!demo %! ## Create a frequency table from a numeric vector with NaN values %! load carsmall %! %! ## The carsmall dataset contains measurements of 100 cars %! total_cars = length (MPG) %! ## For six cars, the MPG value is missing %! missingMPG = length (MPG(isnan (MPG))) %! %! ## Create a frequency table using MPG %! tabulate (MPG) %! table = tabulate (MPG); %! %! ## Only 94 cars were used %! valid_cars = sum (table(:,2)) %!test %! load patients %! table = tabulate (Gender); %! assert (table{1,1}, "Male"); %! assert (table{2,1}, "Female"); %! assert (table{1,2}, 47); %! assert (table{2,2}, 53); %!test %! load patients %! table = tabulate (Height); %! assert (table(end-4,:), [68, 15, 15]); %! assert (table(end-3,:), [69, 8, 8]); %! assert (table(end-2,:), [70, 11, 11]); %! assert (table(end-1,:), [71, 10, 10]); %! assert (table(end,:), [72, 4, 4]); %!error tabulate (ones (3)) %!error tabulate ({1, 2, 3, 4}) %!error ... %! tabulate ({"a", "b"; "a", "c"}) statistics-release-1.7.3/inst/tiedrank.m000066400000000000000000000130251475240274700203030ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{r}, @var{tieadj}]} = tiedrank (@var{x}) ## @deftypefnx {statistics} {[@var{r}, @var{tieadj}]} = tiedrank (@var{x}, @var{tieflag}) ## @deftypefnx {statistics} {[@var{r}, @var{tieadj}]} = tiedrank (@var{x}, @var{tieflag}, @var{bidir}) ## ## Compute rank adjusted for ties. ## ## @code{[@var{r}, @var{tieadj}] = tiedrank (@var{x})} computes the ranks of the ## values in vector @var{x}. If any values in @var{x} are tied, @code{tiedrank} ## computes their average rank. The return value @var{tieadj} is an adjustment ## for ties required by the nonparametric tests @code{signrank} and ## @code{ranksum}, and for the computation of Spearman's rank correlation. ## ## @code{[@var{r}, @var{tieadj}] = tiedrank (@var{x}, 1)} computes the ranks of ## the values in the vector @var{x}. @var{tieadj} is a vector of three ## adjustments for ties required in the computation of Kendall's tau. ## @code{tiedrank (@var{x}, 0)} is the same as @code{tiedrank (@var{x})}. ## ## @code{[@var{r}, @var{tieadj}] = tiedrank (@var{x}, 0, 1)} computes the ranks ## from each end, so that the smallest and largest values get rank 1, the next ## smallest and largest get rank 2, etc. These ranks are used in the ## Ansari-Bradley test. ## ## @end deftypefn function [r, tieadj] = tiedrank (x, tieflag, bidir) ## Check input arguments and add defauls if (nargin < 1 || nargin > 3) print_usage (); endif if (! isvector (x)) error ("tiedrank: X must be a vector."); endif if (nargin < 2) tieflag = false; elseif (! isscalar (tieflag) || ! (isnumeric (tieflag) || isbool (tieflag))) error ("tiedrank: TIEFLAG must be a numeric or boolean scalar."); endif if (nargin < 3) bidir = false; elseif (! isscalar (bidir) || ! (isnumeric (bidir) || isbool (bidir))) error ("tiedrank: BIDIR must be a numeric or boolean scalar."); endif ## Sort X and leave NaNs at the end of vector [sx, idx] = sort (x(:)); NaNs = sum (isnan (x)); xLen = length (x) - NaNs; ## Count ranks from low end if (! bidir) ranks = [1:xLen, NaN(1,NaNs)]'; ## Count ranks from both ends else ## For even number of samples if (mod (xLen, 2) == 0) ranks = [(1:xLen/2), (xLen/2:-1:1), NaN(1,NaNs)]'; ## For odd number of samples else ranks = [(1:(xLen+1)/2), ((xLen-1)/2:-1:1), NaN(1,NaNs)]'; endif endif ## Define number of adjustments if (! tieflag) tieadj = 0; else tieadj = [0; 0; 0]; endif ## Check precision of X if (isa (x, "single")) ranks = single (ranks); tieadj = single (tieadj); endif ## Adjust for ties ties = sx(1:xLen-1) >= sx(2:xLen); tieloc = [find(ties); xLen+2]; maxTies = length (tieloc); tiecount = 1; while (tiecount < maxTies) tiestart = tieloc(tiecount); ntied = 2; while (tieloc(tiecount + 1) == tieloc(tiecount) + 1) tiecount = tiecount + 1; ntied = ntied + 1; endwhile if (! tieflag) tieadj = tieadj + ntied * (ntied - 1) * (ntied + 1) / 2; else n2minusn = ntied * (ntied - 1); tieadj = tieadj + [n2minusn/2; n2minusn*(ntied-2); n2minusn*(2*ntied+5)]; endif ## Compute mean of tied ranks ranks(tiestart:tiestart+ntied-1) = ... sum (ranks(tiestart:tiestart+ntied-1)) / ntied; tiecount = tiecount + 1; endwhile ## Reshape ranks including NaN where required. r(idx) = ranks; r = reshape (r, size (x)); endfunction ## testing against mileage data and results from Matlab %!test %! [r,tieadj] = tiedrank ([10, 20, 30, 40, 20]); %! assert (r, [1, 2.5, 4, 5, 2.5]); %! assert (tieadj, 3); %!test %! [r,tieadj] = tiedrank ([10; 20; 30; 40; 20]); %! assert (r, [1; 2.5; 4; 5; 2.5]); %! assert (tieadj, 3); %!test %! [r,tieadj] = tiedrank ([10, 20, 30, 40, 20], 1); %! assert (r, [1, 2.5, 4, 5, 2.5]); %! assert (tieadj, [1; 0; 18]); %!test %! [r,tieadj] = tiedrank ([10, 20, 30, 40, 20], 0, 1); %! assert (r, [1, 2.5, 2, 1, 2.5]); %! assert (tieadj, 3); %!test %! [r,tieadj] = tiedrank ([10, 20, 30, 40, 20], 1, 1); %! assert (r, [1, 2.5, 2, 1, 2.5]); %! assert (tieadj, [1; 0; 18]); ## Test input validation %!error tiedrank (ones (2)) %!error ... %! tiedrank ([1, 2, 3, 4, 5], [1, 1]) %!error ... %! tiedrank ([1, 2, 3, 4, 5], "A") %!error ... %! tiedrank ([1, 2, 3, 4, 5], [true, true]) %!error ... %! tiedrank ([1, 2, 3, 4, 5], 0, [1, 1]) %!error ... %! tiedrank ([1, 2, 3, 4, 5], 0, "A") %!error ... %! tiedrank ([1, 2, 3, 4, 5], 0, [true, true]) statistics-release-1.7.3/inst/trimmean.m000066400000000000000000000271271475240274700203260ustar00rootroot00000000000000## Copyright (C) 2001 Paul Kienzle ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{m} =} trimmean (@var{x}, @var{p}) ## @deftypefnx {statistics} {@var{m} =} trimmean (@var{x}, @var{p}, @var{flag}) ## @deftypefnx {statistics} {@var{m} =} trimmean (@dots{}, @qcode{"all"}) ## @deftypefnx {statistics} {@var{m} =} trimmean (@dots{}, @var{dim}) ## @deftypefnx {statistics} {@var{m} =} trimmean (@dots{}, @var{dim}) ## ## Compute the trimmed mean. ## ## The trimmed mean of @var{x} is defined as the mean of @var{x} excluding the ## highest and lowest @math{k} data values of @var{x}, calculated as ## @qcode{@var{k} = n * (@var{p} / 100) / 2)}, where @var{n} is the sample size. ## ## @code{@var{m} = trimmean (@var{x}, @var{p})} returns the mean of @var{x} ## after removing the outliers in @var{x} defined by @var{p} percent. ## @itemize ## @item If @var{x} is a vector, then @code{trimmean (@var{x}, @var{p})} is the ## mean of all the values of @var{x}, computed after removing the outliers. ## @item If @var{x} is a matrix, then @code{trimmean (@var{x}, @var{p})} is a ## row vector of column means, computed after removing the outliers. ## @item If @var{x} is a multidimensional array, then @code{trimmean} operates ## along the first nonsingleton dimension of @var{x}. ## @end itemize ## ## To specify the operating dimension(s) when @var{x} is a matrix or a ## multidimensional array, use the @var{dim} or @var{vecdim} input argument. ## ## @code{trimmean} treats @qcode{NaN} values in @var{x} as missing values and ## removes them. ## ## @code{@var{m} = trimmean (@var{x}, @var{p}, @var{flag})} specifies how to ## trim when @math{k}, i.e. half the number of outliers, is not an integer. ## @var{flag} can be specified as one of the following values: ## @multitable @columnfractions 0.2 0.05 0.75 ## @headitem Value @tab @tab Description ## @item @qcode{"round"} @tab @tab Round @math{k} to the nearest integer. This ## is the default. ## @item @qcode{"floor"} @tab @tab Round @math{k} down to the next smaller ## integer. ## @item @qcode{"weighted"} @tab @tab If @math{k = i + f}, where @math{i} is an ## integer and @math{f} is a fraction, compute a weighted mean with weight ## @math{(1 – f)} for the @math{(i + 1)}-th and @math{(n – i)}-th values, and ## full weight for the values between them. ## @end multitable ## ## @code{@var{m} = trimmean (@dots{}, @qcode{"all"})} returns the trimmed mean ## of all the values in @var{x} using any of the input argument combinations in ## the previous syntaxes. ## ## @code{@var{m} = trimmean (@dots{}, @var{dim})} returns the trimmed mean along ## the operating dimension @var{dim} specified as a positive integer scalar. If ## not specified, then the default value is the first nonsingleton dimension of ## @var{x}, i.e. whose size does not equal 1. If @var{dim} is greater than ## @qcode{ndims (@var{X})} or if @qcode{size (@var{x}, @var{dim})} is 1, then ## @code{trimmean} returns @var{x}. ## ## @code{@var{m} = trimmean (@dots{}, @var{vecdim})} returns the trimmed mean ## over the dimensions specified in the vector @var{vecdim}. For example, if ## @var{x} is a 2-by-3-by-4 array, then @code{mean (@var{x}, [1 2])} returns a ## 1-by-1-by-4 array. Each element of the output array is the mean of the ## elements on the corresponding page of @var{x}. If @var{vecdim} indexes all ## dimensions of @var{x}, then it is equivalent to @code{mean (@var{x}, "all")}. ## Any dimension in @var{vecdim} greater than @code{ndims (@var{x})} is ignored. ## ## @seealso{mean} ## @end deftypefn function m = trimmean (x, p, varargin) if (nargin < 2 || nargin > 4) print_usage; endif if (p < 0 || p >= 100) error ("trimmean: invalid percent."); endif ## Parse extra arguments if (nargin < 3) flag = []; dim = []; elseif (nargin < 4) if (ischar (varargin{1}) && ! strcmpi (varargin{1}, "all")) flag = varargin{1}; dim = []; elseif (isnumeric (varargin{1}) || strcmpi (varargin{1}, "all")) flag = []; dim = varargin{1}; endif else flag = varargin{1}; dim = varargin{2}; endif ## Get size of X szx = size (x); ndx = numel (szx); ## Handle special case X = [] if (isempty (x)) m = NaN (class (x)); return endif ## Check FLAG if (isempty (flag)) flag = "round"; endif if (! any (strcmpi (flag, {"round", "floor", "weighted"}))) error ("trimmean: invalid FLAG argument."); endif ## Check DIM if (isempty (dim)) (dim = find (szx != 1, 1)) || (dim = 1); endif if (strcmpi (dim, "all")) x = x(:); dim = 1; endif if (! (isvector (dim) && all (dim > 0) && all (rem (dim, 1) == 0))) error ("trimmean: DIM must be a positive integer scalar or vector."); endif vecdim_flag = false; if (numel (dim) > 1) dim = sort (dim); if (! all (diff (dim))) error ("trimmean: VECDIM must contain non-repeating positive integers."); endif vecdim_flag = true; endif ## If DIM is a scalar greater than ndims, return X if (isscalar (dim) && dim > ndx) m = x; return endif ## If DIM is a scalar and size (x, dim) == 1, return X if (isscalar (dim) && size (x, dim) == 1) m = x; return endif ## If DIM is a vector, ignore any value > ndims (x) if (numel (dim) > 1) dim(dim > ndx) = []; endif ## Permute dim to simplify all operations along dim1. At func. end ipermute. if (numel (dim) > 1 || (dim != 1 && ! isvector (x))) perm = 1:ndx; if (! vecdim_flag) ## Move dim to dim 1 perm([1, dim]) = [dim, 1]; x = permute (x, perm); szx([1, dim]) = szx([dim, 1]); dim = 1; else ## Move vecdims to front perm(dim) = []; perm = [dim, perm]; x = permute (x, perm); ## Reshape all vecdims into dim1 num_dim = prod (szx(dim)); szx(dim) = []; szx = [num_dim, ones(1, numel(dim)-1), szx]; x = reshape (x, szx); dim = 1; endif perm_flag = true; else perm_flag = false; endif ## Create output matrix sizem = size (x); sizem(dim) = 1; ## Sort X along 1st dimensions x = sort (x); ## No missing data, all columns have the same length if (! any (isnan (x(:)))) if (isempty (x)) n = 0; else n = size (x, 1); end m = trim (x, n, p, flag, sizem); m = reshape (m, sizem); ## With missing data, each column is computed separately else m = NaN (sizem, class (x)); for j = 1:prod (sizem(2:end)) n = find (! isnan (x(:,j)), 1, "last"); m(j) = trim (x(:,j), n, p, flag, [1, 1]); endfor endif ## Inverse permute back to correct dimensions (if necessary) if (perm_flag) m = ipermute (m, perm); endif endfunction ## Help function for handling different flags function m = trim (x, n, p, flag, sizem) switch (lower (flag)) case "round" k = n * p / 200; k0 = round (k - eps (k)); if (! isempty (n) && n > 0 && k0 < n / 2) m = mean (x((k0+1):(n-k0),:), 1); else m = NaN (sizem, class (x)); endif case "floor" k0 = floor (n * p / 200); if (! isempty (n) && n > 0 && k0 < n / 2) m = mean (x((k0+1):(n-k0),:), 1); else m = NaN (sizem, class (x)); endif case "weighted" k = n * p / 200; k0 = floor (k); fr = 1 + k0 - k; if (! isempty (n) && n > 0 && (k0 < n / 2 || fr > 0)) m = (sum (x((k0+2):(n-k0-1),:),1) + fr * x(k0+1,:) + fr * x(n-k0,:)) ... / (max (0, n - 2 * k0 - 2) + 2 * fr); else m = NaN (sizem, class (x)); endif endswitch endfunction ## Test output %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! assert (trimmean (x, 10, "all"), 19.4722, 1e-4); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! out = trimmean (x, 10, [1, 2]); %! assert (out(1,1,1), 10.3889, 1e-4); %! assert (out(1,1,2), 29.6111, 1e-4); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! assert (trimmean (x, 10, "all"), 19.3824, 1e-4); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! out = trimmean (x, 10, 1); %! assert (out(:,:,1), [-17.6, 8, 13, 18]); %! assert (out(:,:,2), [23, 28, 33, 10.6]); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, 1); %! assert (out(:,:,1), [-23, 8, 13, 18]); %! assert (out(:,:,2), [23, 28, 33, 3.75]); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! out = trimmean (x, 10, 2); %! assert (out(:,:,1), [8.5; 9.5; -15.25; 11.5; 12.5]); %! assert (out(:,:,2), [28.5; -4.75; 30.5; 31.5; 32.5]); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, 2); %! assert (out(:,:,1), [8.5; 9.5; -15.25; 14; 12.5]); %! assert (out(:,:,2), [28.5; -4.75; 28; 31.5; 32.5]); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! out = trimmean (x, 10, [1, 2, 3]); %! assert (out, trimmean (x, 10, "all")); ## Test N-D array with NaNs %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, [1, 2]); %! assert (out(1,1,1), 10.7647, 1e-4); %! assert (out(1,1,2), 29.1176, 1e-4); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, [1, 3]); %! assert (out, [2.5556, 18, 23, 11.6667], 1e-4); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, [2, 3]); %! assert (out, [18.5; 2.3750; 3.2857; 24; 22.5], 1e-4); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, [1, 2, 3]); %! assert (out, trimmean (x, 10, "all")); %!test %! x = reshape (1:40, [5, 4, 2]); %! x([3, 37]) = -100; %! x([4, 38]) = NaN; %! out = trimmean (x, 10, [2, 3, 5]); %! assert (out, [18.5; 2.3750; 3.2857; 24; 22.5], 1e-4); ## Test special cases %!assert (trimmean (reshape (1:40, [5, 4, 2]), 10, 4), reshape(1:40, [5, 4, 2])) %!assert (trimmean ([], 10), NaN) %!assert (trimmean ([1;2;3;4;5], 10, 2), [1;2;3;4;5]) ## Test input validation %!error trimmean (1) %!error trimmean (1,2,3,4,5) %!error trimmean ([1 2 3 4], -10) %!error trimmean ([1 2 3 4], 100) %!error trimmean ([1 2 3 4], 10, "flag") %!error trimmean ([1 2 3 4], 10, "flag", 1) %!error ... %! trimmean ([1 2 3 4], 10, -1) %!error ... %! trimmean ([1 2 3 4], 10, "floor", -1) %!error ... %! trimmean (reshape (1:40, [5, 4, 2]), 10, [-1, 2]) %!error ... %! trimmean (reshape (1:40, [5, 4, 2]), 10, [1, 2, 2]) statistics-release-1.7.3/inst/ttest.m000066400000000000000000000154061475240274700176520ustar00rootroot00000000000000## Copyright (C) 2014 Tony Richardson ## Copyright (C) 2022 Andrew Penn ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} ttest (@var{x}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} ttest (@var{x}, @var{m}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} ttest (@var{x}, @var{y}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} ttest (@var{x}, @var{m}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} ttest (@var{x}, @var{y}, @var{Name}, @var{Value}) ## ## Test for mean of a normal sample with unknown variance. ## ## Perform a t-test of the null hypothesis @code{mean (@var{x}) == ## @var{m}} for a sample @var{x} from a normal distribution with unknown ## mean and unknown standard deviation. Under the null, the test statistic ## @var{t} has a Student's t distribution. The default value of ## @var{m} is 0. ## ## If the second argument @var{y} is a vector, a paired-t test of the ## hypothesis @code{mean (@var{x}) = mean (@var{y})} is performed. If @var{x} ## and @var{y} are vectors, they must have the same size and dimensions. ## ## @var{x} (and @var{y}) can also be matrices. For matrices, @qcode{ttest} ## performs separate t-tests along each column, and returns a vector of results. ## @var{x} and @var{y} must have the same number of columns. The Type I error ## rate of the resulting vector of @var{pval} can be controlled by entering ## @var{pval} as input to the function @qcode{multcompare}. ## ## @qcode{ttest} treats NaNs as missing values, and ignores them. ## ## Name-Value pair arguments can be used to set various options. ## @qcode{"alpha"} can be used to specify the significance level ## of the test (the default value is 0.05). @qcode{"tail"}, can be used ## to select the desired alternative hypotheses. If the value is ## @qcode{"both"} (default) the null is tested against the two-sided ## alternative @code{mean (@var{x}) != @var{m}}. ## If it is @qcode{"right"} the one-sided alternative @code{mean (@var{x}) ## > @var{m}} is considered. Similarly for @qcode{"left"}, the one-sided ## alternative @code{mean (@var{x}) < @var{m}} is considered. ## When argument @var{x} is a matrix, @qcode{"dim"} can be used to select ## the dimension over which to perform the test. (The default is the ## first non-singleton dimension). ## ## If @var{h} is 1 the null hypothesis is rejected, meaning that the tested ## sample does not come from a Student's t distribution. If @var{h} is 0, then ## the null hypothesis cannot be rejected and it can be assumed that @var{x} ## follows a Student's t distribution. The p-value of the test is returned in ## @var{pval}. A 100(1-alpha)% confidence interval is returned in @var{ci}. ## ## @var{stats} is a structure containing the value of the test statistic ## (@var{tstat}), the degrees of freedom (@var{df}) and the sample's standard ## deviation (@var{sd}). ## ## @seealso{hotelling_ttest, ttest2, hotelling_ttest2} ## @end deftypefn function [h, p, ci, stats] = ttest (x, my, varargin) ## Set default arguments my_default = 0; alpha = 0.05; tail = "both"; ## Find the first non-singleton dimension of x dim = min (find (size (x) != 1)); if (isempty (dim)) dim = 1; endif if (nargin == 1) my = my_default; endif i = 1; while (i <= length (varargin)) switch lower (varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; case "tail" i = i + 1; tail = varargin{i}; case "dim" i = i + 1; dim = varargin{i}; otherwise error ("ttest: Invalid Name argument."); endswitch i = i + 1; endwhile if (! isa (tail, "char")) error ("ttest: tail argument must be a string."); endif if (any (and (! isscalar (my), size (x) != size (my)))) error ("ttest: Arrays in paired test must be the same size."); endif ## Set default values if arguments are present but empty if (isempty (my)) my = my_default; endif ## This adjustment allows everything else to remain the ## same for both the one-sample t test and paired tests. x = x - my; if (! isscalar (my)) my = 0; endif ## Calculate the test statistic value (tval) n = sum (!isnan (x), dim); x_bar = mean (x, dim, "omitnan"); stats.tstat = []; stats.df = n - 1; stats.sd = std (x, 0, dim, "omitnan"); x_bar_std = stats.sd ./ sqrt(n); tval = (x_bar) ./ x_bar_std; stats.tstat = tval; ## Based on the "tail" argument determine the P-value, the critical values, ## and the confidence interval. switch lower (tail) case "both" p = 2 * (1 - tcdf (abs (tval), n - 1)); tcrit = - tinv (alpha / 2, n - 1); ci = [x_bar-tcrit.*x_bar_std; x_bar+tcrit.*x_bar_std] + my; case "left" p = tcdf (tval, n - 1); tcrit = - tinv (alpha, n - 1); ci = [-inf*ones(size(x_bar)); my+x_bar+tcrit.*x_bar_std]; case "right" p = 1 - tcdf (tval, n - 1); tcrit = - tinv (alpha, n - 1); ci = [my+x_bar-tcrit.*x_bar_std; inf*ones(size(x_bar))]; otherwise error ("ttest: Invalid value for tail argument."); endswitch ## Reshape the ci array to match MATLAB shaping if (isscalar (x_bar) && dim == 2) ci = ci(:)'; elseif (size (x_bar, 2) < size (x_bar, 1)) ci = reshape (ci(:), length (x_bar), 2); endif ## Determine the test outcome ## MATLAB returns this a double instead of a logical array h = double (p < alpha); endfunction %!test %! x = 8:0.1:12; %! [h, pval, ci] = ttest (x, 10); %! assert (h, 0) %! assert (pval, 1, 10*eps) %! assert (ci, [9.6219 10.3781], 1E-5) %! [h, pval, ci0] = ttest (x, 0); %! assert (h, 1) %! assert (pval, 0) %! assert (ci0, ci, 2e-15) %! [h, pval, ci] = ttest (x, 10, "tail", "right", "dim", 2, "alpha", 0.05); %! assert (h, 0) %! assert (pval, 0.5, 10*eps) %! assert (ci, [9.68498 Inf], 1E-5) %!error ttest ([8:0.1:12], 10, "tail", "invalid"); %!error ttest ([8:0.1:12], 10, "tail", 25); statistics-release-1.7.3/inst/ttest2.m000066400000000000000000000153421475240274700177330ustar00rootroot00000000000000## Copyright (C) 2014 Tony Richardson ## Copyright (C) 2022 Andrew Penn ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}]} = ttest2 (@var{x}, @var{y}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}]} = ttest2 (@var{x}, @var{y}, @var{Name}, @var{Value}) ## ## Perform a t-test to compare the means of two groups of data under the null ## hypothesis that the groups are drawn from distributions with the same mean. ## ## @var{x} and @var{y} can be vectors or matrices. For matrices, @qcode{ttest2} ## performs separate t-tests along each column, and returns a vector of results. ## @var{x} and @var{y} must have the same number of columns. The Type I error ## rate of the resulting vector of @var{pval} can be controlled by entering ## @var{pval} as input to the function @qcode{multcompare}. ## ## @qcode{ttest2} treats NaNs as missing values, and ignores them. ## ## For a nested t-test, use @qcode{anova2}. ## ## The argument @qcode{"alpha"} can be used to specify the significance level ## of the test (the default value is 0.05). The string argument @qcode{"tail"}, ## can be used to select the desired alternative hypotheses. If @qcode{"tail"} ## is @qcode{"both"} (default) the null is tested against the two-sided ## alternative @code{mean (@var{x}) != @var{m}}. If @qcode{"tail"} is ## @qcode{"right"} the one-sided alternative @code{mean (@var{x}) > @var{m}} is ## considered. Similarly for @qcode{"left"}, the one-sided alternative ## @code{mean (@var{x}) < @var{m}} is considered. ## ## When @qcode{"vartype"} is @qcode{"equal"} the variances are assumed to be ## equal (this is the default). When @qcode{"vartype"} is @qcode{"unequal"} the ## variances are not assumed equal. ## ## When argument @var{x} and @var{y} are matrices the @qcode{"dim"} argument can ## be used to select the dimension over which to perform the test. ## (The default is the first non-singleton dimension.) ## ## If @var{h} is 0 the null hypothesis is accepted, if it is 1 the null ## hypothesis is rejected. The p-value of the test is returned in @var{pval}. ## A 100(1-alpha)% confidence interval is returned in @var{ci}. @var{stats} ## is a structure containing the value of the test statistic (@var{tstat}), ## the degrees of freedom (@var{df}) and the sample standard deviation ## (@var{sd}). ## ## @seealso{hotelling_ttest2, anova1, hotelling_ttest, ttest} ## @end deftypefn function [h, p, ci, stats] = ttest2 (x, y, varargin) ## Set defaults alpha = 0.05; tail = "both"; vartype = "equal"; ## Find the first non-singleton dimension of x dim = min (find (size (x) != 1)); if (isempty (dim)) dim = 1; endif ## Evaluate optional input arguments i = 1; while ( i <= length(varargin) ) switch lower(varargin{i}) case "alpha" i = i + 1; alpha = varargin{i}; case "tail" i = i + 1; tail = varargin{i}; case "vartype" i = i + 1; vartype = varargin{i}; case "dim" i = i + 1; dim = varargin{i}; otherwise error ("ttest2: Invalid Name argument."); endswitch i = i + 1; endwhile ## Error checking if (! isa (tail, "char")) error ("ttest2: tail argument must be a string."); endif if (size (x, abs (dim - 3)) != size (y, abs (dim - 3))) error ("ttest2: The data in a 2-sample t-test must be commensurate") endif ## Calculate mean, variance and size of each sample m = sum (!isnan (x), dim); n = sum (!isnan (y), dim); x_bar = mean (x, dim, "omitnan") - mean (y, dim, "omitnan"); s1_var = var (x, 0, dim, "omitnan"); s2_var = var (y, 0, dim, "omitnan"); ## Perform test-specific calculations switch lower (vartype) case "equal" stats.tstat = []; stats.df = (m + n - 2); sp_var = ((m - 1) .* s1_var + (n - 1) .* s2_var) ./ stats.df; stats.sd = sqrt (sp_var); x_bar_std = sqrt (sp_var .* (1 ./ m + 1 ./ n)); n_sd = 1; case "unequal" stats.tstat = []; se1 = sqrt (s1_var ./ m); se2 = sqrt (s2_var ./ n); sp_var = s1_var ./ m + s2_var ./ n; stats.df = ((se1 .^ 2 + se2 .^ 2) .^ 2 ./ ... (se1 .^ 4 ./ (m - 1) + se2 .^ 4 ./ (n - 1))); stats.sd = [sqrt(s1_var); sqrt(s2_var)]; x_bar_std = sqrt (sp_var); n_sd = 2; otherwise error ("ttest2: Invalid value for vartype argument."); end stats.tstat = x_bar ./ x_bar_std; ## Based on the "tail" argument determine the P-value, the critical values, ## and the confidence interval. switch lower(tail) case "both" p = 2 * (1 - tcdf (abs (stats.tstat), stats.df)); tcrit = - tinv (alpha / 2, stats.df); ci = [x_bar-tcrit.*x_bar_std; x_bar+tcrit.*x_bar_std]; case "left" p = tcdf (stats.tstat, stats.df); tcrit = - tinv (alpha, stats.df); ci = [-inf*ones(size(x_bar)); x_bar+tcrit.*x_bar_std]; case "right" p = 1 - tcdf (stats.tstat, stats.df); tcrit = - tinv (alpha, stats.df); ci = [x_bar-tcrit.*x_bar_std; inf*ones(size(x_bar))]; otherwise error ("ttest2: Invalid value for tail argument."); endswitch ## Reshape the ci array to match MATLAB shaping if (isscalar (x_bar) && dim == 2) ci = ci(:)'; stats.sd = stats.sd(:)'; elseif (size (x_bar, 2) < size (x_bar, 1)) ci = reshape (ci(:), length (x_bar), 2); stats.sd = reshape (stats.sd(:), length (x_bar), n_sd); endif ## Determine the test outcome ## MATLAB returns this a double instead of a logical array h = double (p < alpha); endfunction %!test %! a = 1:5; %! b = 6:10; %! b(5) = NaN; %! [h,p,ci,stats] = ttest2 (a,b); %! assert (h, 1); %! assert (p, 0.002535996080258229, 1e-14); %! assert (ci, [-6.822014919225481, -2.17798508077452], 1e-14); %! assert (stats.tstat, -4.582575694955839, 1e-14); %! assert (stats.df, 7); %! assert (stats.sd, 1.4638501094228, 1e-13); %!error ttest2 ([8:0.1:12], [8:0.1:12], "tail", "invalid"); %!error ttest2 ([8:0.1:12], [8:0.1:12], "tail", 25); statistics-release-1.7.3/inst/vartest.m000066400000000000000000000201201475240274700201640ustar00rootroot00000000000000## Copyright (C) 2014 Tony Richardson ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} vartest (@var{x}, @var{v}) ## @deftypefnx {statistics} {@var{h} =} vartest (@var{x}, @var{v}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} vartest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}] =} vartest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} vartest (@dots{}) ## ## One-sample test of variance. ## ## @code{@var{h} = vartest (@var{x}, @var{v})} performs a chi-square test of the ## hypothesis that the data in the vector @var{x} come from a normal ## distribution with variance @var{v}, against the alternative that @var{x} ## comes from a normal distribution with a different variance. The result is ## @var{h} = 0 if the null hypothesis ("variance is V") cannot be rejected at ## the 5% significance level, or @var{h} = 1 if the null hypothesis can be ## rejected at the 5% level. ## ## @var{x} may also be a matrix or an N-D array. For matrices, @code{vartest} ## performs separate tests along each column of @var{x}, and returns a vector of ## results. For N-D arrays, @code{vartest} works along the first non-singleton ## dimension of @var{x}. @var{v} must be a scalar. ## ## @code{vartest} treats NaNs as missing values, and ignores them. ## ## @code{[@var{h}, @var{pval}] = vartest (@dots{})} returns the p-value. That ## is the probability of observing the given result, or one more extreme, by ## chance if the null hypothesisis true. ## ## @code{[@var{h}, @var{pval}, @var{ci}] = vartest (@dots{})} returns a ## 100 * (1 - @var{alpha})% confidence interval for the true variance. ## ## @code{[@var{h}, @var{pval}, @var{ci}, @var{stats}] = vartest (@dots{})} ## returns a structure with the following fields: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{chisqstat} @tab the value of the test statistic ## @item @tab @qcode{df} @tab the degrees of freedom of the test ## @end multitable ## ## @code{[@dots{}] = vartest (@dots{}, @var{name}, @var{value}), @dots{}} ## specifies one or more of the following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"alpha"} @tab the significance level. Default is 0.05. ## ## @item @tab @qcode{"dim"} @tab dimension to work along a matrix or an N-D ## array. ## ## @item @tab @qcode{"tail"} @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.1 0.15 0.75 ## @item @tab @qcode{"both"} @tab variance is not @var{v} (two-tailed, default) ## @item @tab @qcode{"left"} @tab variance is less than @var{v} (left-tailed) ## @item @tab @qcode{"right"} @tab variance is greater than @var{v} ## (right-tailed) ## @end multitable ## ## @seealso{ttest, ztest, kstest} ## @end deftypefn function [h, pval, ci, stats] = vartest (x, v, varargin) ## Validate input arguments if (nargin < 2) error ("vartest: too few input arguments."); endif if (! isscalar (v) || ! isnumeric(v) || ! isreal(v) || v < 0) error ("vartest: invalid value for variance."); endif ## Add defaults alpha = 0.05; tail = "both"; dim = []; if (nargin > 2 && mod (numel (varargin(:)), 2) == 0) for idx = 3:2:nargin name = varargin{idx-2}; value = varargin{idx-1}; switch (lower (name)) case "alpha" alpha = value; if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("vartest: invalid value for alpha."); endif case "tail" tail = value; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("vartest: invalid value for tail."); endif case "dim" dim = value; if (! isscalar (dim) || ! ismember (dim, 1:ndims (x))) error ("vartest: invalid value for operating dimension."); endif otherwise error ("vartest: invalid name for optional arguments."); endswitch endfor elseif (nargin > 2 && mod (numel (varargin(:)), 2) != 0) error ("vartest: optional arguments must be in name/value pairs."); endif ## Figure out which dimension mean will work along if (isempty (dim)) dim = find (size (x) != 1, 1); endif ## Replace all NaNs with zeros is_nan = isnan (x); x_dims = ndims (x); x(is_nan) = 0; ## Find sample size for each group (if more than one) if (any (is_nan(:))) sz = sum (! is_nan, dim); else sz = size (x, dim); endif ## Find degrees of freedom for each group (if more than one) df = max (sz - 1, 0); ## Calculate mean for each group (if more than one) x_mean = sum (x, dim) ./ max (1, sz); ## Center data if (isscalar (x_mean)) x_centered = x - x_mean; else rep = ones (1, x_dims); rep(dim) = size (x, dim); x_centered = x - repmat (x_mean, rep); endif ## Replace all NaNs with zeros x_centered(is_nan) = 0; ## Calculate chi-square statistic sumsq = sum (abs (x_centered) .^ 2, dim); if (v > 0) chisqstat = sumsq ./ v; else chisqstat = Inf (size (sumsq)); chisqstat(sumsq == 0) = NaN; endif ## Calculate p-value for the test and confidence intervals (if requested) if (strcmpi (tail, "both")) pval = chi2cdf (chisqstat, df); pval = 2 * min (pval, 1 - pval); if (nargout > 2) ci = cat (dim, sumsq ./ chi2inv (1 - alpha / 2, df), ... sumsq ./ chi2inv (alpha / 2, df)); endif elseif (strcmpi (tail, "right")) pval = chi2cdf (chisqstat, df); if (nargout > 2) ci = cat (dim, sumsq ./ chi2inv (1 - alpha, df), Inf (size (pval))); endif elseif (strcmpi (tail, "left")) pval = chi2cdf (chisqstat, df); if (nargout > 2) ci = cat (dim, zeros (size (pval)), sumsq ./ chi2inv (alpha, df)); endif endif ## Determine the test outcome h = double (pval < alpha); h(isnan (pval)) = NaN; ## Create stats output structure (if requested) if (nargout > 3) stats = struct ("chisqstat", chisqstat, "df", df); endif endfunction ## Test input validation %!error vartest (); %!error vartest ([1, 2, 3, 4], -0.5); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", 0); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", 1.2); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", "val"); %!error ... %! vartest ([1, 2, 3, 4], 1, "tail", "val"); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", 0.01, "tail", "val"); %!error ... %! vartest ([1, 2, 3, 4], 1, "dim", 3); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", 0.01, "tail", "both", "dim", 3); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", 0.01, "tail", "both", "badoption", 3); %!error ... %! vartest ([1, 2, 3, 4], 1, "alpha", 0.01, "tail"); ## Test results %!test %! load carsmall %! [h, pval, ci] = vartest (MPG, 7^2); %! assert (h, 1); %! assert (pval, 0.04335086742174443, 1e-14); %! assert (ci, [49.397; 88.039], 1e-3); statistics-release-1.7.3/inst/vartest2.m000066400000000000000000000222751475240274700202630ustar00rootroot00000000000000## Copyright (C) 2014 Tony Richardson ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} vartest2 (@var{x}, @var{y}) ## @deftypefnx {statistics} {@var{h} =} vartest2 (@var{x}, @var{y}, @var{name}, @var{value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} vartest2 (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}] =} vartest2 (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{stats}] =} vartest2 (@dots{}) ## ## Two-sample F test for equal variances. ## ## @code{@var{h} = vartest2 (@var{x}, @var{y})} performs an F test of the ## hypothesis that the independent data in vectors @var{x} and @var{y} come from ## normal distributions with equal variance, against the alternative that they ## come from normal distributions with different variances. The result is ## @var{h} = 0 if the null hypothesis ("variance are equal") cannot be rejected ## at the 5% significance level, or @var{h} = 1 if the null hypothesis can be ## rejected at the 5% level. ## ## @var{x} and @var{y} may also be matrices or N-D arrays. For matrices, ## @code{vartest2} performs separate tests along each column and returns a ## vector of results. For N-D arrays, @code{vartest2} works along the first ## non-singleton dimension and @var{x} and @var{y} must have the same size along ## all the remaining dimensions. ## ## @code{vartest} treats NaNs as missing values, and ignores them. ## ## @code{[@var{h}, @var{pval}] = vartest (@dots{})} returns the p-value. That ## is the probability of observing the given result, or one more extreme, by ## chance if the null hypothesisis true. ## ## @code{[@var{h}, @var{pval}, @var{ci}] = vartest (@dots{})} returns a ## @math{100 * (1 - @var{alpha})%} confidence interval for the true ratio ## var(X)/var(Y). ## ## @code{[@var{h}, @var{pval}, @var{ci}, @var{stats}] = vartest (@dots{})} ## returns a structure with the following fields: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{fstat} @tab the value of the test statistic ## @item @tab @qcode{df1} @tab the numerator degrees of freedom of the test ## @item @tab @qcode{df2} @tab the denominator degrees of freedom of the test ## @end multitable ## ## @code{[@dots{}] = vartest (@dots{}, @var{name}, @var{value}), @dots{}} ## specifies one or more of the following name/value pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab Name @tab Value ## @item @tab @qcode{"alpha"} @tab the significance level. Default is 0.05. ## ## @item @tab @qcode{"dim"} @tab dimension to work along a matrix or an N-D ## array. ## ## @item @tab @qcode{"tail"} @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.1 0.15 0.75 ## @item @tab @qcode{"both"} @tab variance is not @var{v} (two-tailed, default) ## @item @tab @qcode{"left"} @tab variance is less than @var{v} (left-tailed) ## @item @tab @qcode{"right"} @tab variance is greater than @var{v} ## (right-tailed) ## @end multitable ## ## @seealso{ttest2, kstest2, bartlett_test, levene_test} ## @end deftypefn function [h, pval, ci, stats] = vartest2 (x, y, varargin) ## Validate input arguments if (nargin < 2) error ("vartest2: too few input arguments."); endif if (isscalar (x) || isscalar(y)) error ("vartest2: X and Y must be vectors or matrices or N-D arrays."); endif ## If X and Y are vectors make them the same orientation if (isvector (x) && isvector (y)) if (size (x, 1) == 1) y = y(:)'; else y = y(:); endif endif ## Add defaults alpha = 0.05; tail = "both"; dim = []; if (nargin > 2 && mod (numel (varargin(:)), 2) == 0) for idx = 3:2:nargin name = varargin{idx-2}; value = varargin{idx-1}; switch (lower (name)) case "alpha" alpha = value; if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("vartest2: invalid value for alpha."); endif case "tail" tail = value; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("vartest2: invalid value for tail."); endif case "dim" dim = value; if (! isscalar (dim) || ! ismember (dim, 1:ndims (x))) error ("vartest2: invalid value for operating dimension."); endif otherwise error ("vartest2: invalid name for optional arguments."); endswitch endfor elseif (nargin > 2 && mod (numel (varargin(:)), 2) != 0) error ("vartest2: optional arguments must be in name/value pairs."); endif ## Figure out which dimension mean will work along if (isempty (dim)) dim = find (size (x) != 1, 1); endif ## Check that all non-working dimensions of X and Y are of equal size x_size = size (x); y_size = size (y); x_size(dim) = 1; y_size(dim) = 1; if (! isequal (x_size, y_size)) error ("vartestt2: input size mismatch."); endif ## Compute statistics for each sample [df1, x_var] = getstats(x,dim); [df2, y_var] = getstats(y,dim); ## Compute F statistic F = NaN (size (x_var)); t1 = (y_var > 0); F(t1) = x_var(t1) ./ y_var(t1); t2 = (x_var > 0) & ! t1; F(t2) = Inf; ## Calculate p-value for the test and confidence intervals (if requested) if (strcmpi (tail, "both")) pval = 2 * min (fcdf (F, df1, df2), 1 - fcdf (F, df1, df2)); if (nargout > 2) ci = cat (dim, F .* finv (alpha / 2, df2, df1), ... F ./ finv (alpha / 2, df1, df2)); endif elseif (strcmpi (tail, "right")) pval = 1 - fcdf (F, df1, df2); if (nargout > 2) ci = cat (dim, F .* finv (alpha, df2, df1), Inf (size (F))); endif elseif (strcmpi (tail, "left")) pval = fcdf (F, df1, df2); if (nargout > 2) ci = cat (dim, zeros (size (F)), F ./ finv (alpha, df1, df2)); endif endif ## Determine the test outcome h = double (pval < alpha); h(isnan (pval)) = NaN; ## Create stats output structure (if requested) if (nargout > 3) stats = struct ("fstat", F, "df1", df1, "df2", df2); endif endfunction ## Compute statistics for one sample function [df, data_var] = getstats (data, dim) ## Calculate sample size and df by ignoring NaNs is_nan = isnan (data); n_data = sum (! is_nan, dim); df = max (n_data - 1, 0); ## Calculate mean data(is_nan) = 0; m_data = sum (data, dim) ./ max (1, n_data); ## Calculate variance if (isscalar (m_data)) c_data = data - m_data; else rep = ones (1, ndims (data)); rep(dim) = size (data, dim); c_data = data - repmat (m_data, rep); end c_data(is_nan) = 0; data_var = sum (abs (c_data) .^ 2,dim); t = (df > 0); data_var(t) = data_var(t) ./ df(t); data_var(! t) = NaN; ## Make df a scalar if possible if (numel (df) > 1 && all (df(:) == df(1))) df = df(1); end endfunction ## Test input validation %!error vartest2 (); %!error vartest2 (ones (20,1)); %!error ... %! vartest2 (rand (20,1), 5); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "alpha", 0); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "alpha", 1.2); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "alpha", "some"); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "alpha", [0.05, 0.001]); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "tail", [0.05, 0.001]); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "tail", "some"); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "dim", 3); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "alpha", 0.001, "dim", 3); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "some", 3); %!error ... %! vartest2 (rand (20,1), rand (25,1)*2, "some"); ## Test results %!test %! load carsmall %! [h, pval, ci, stat] = vartest2 (MPG(Model_Year==82), MPG(Model_Year==76)); %! assert (h, 0); %! assert (pval, 0.6288022362718455, 1e-13); %! assert (ci, [0.4139; 1.7193], 1e-4); %! assert (stat.fstat, 0.8384, 1e-4); %! assert (stat.df1, 30); %! assert (stat.df2, 33); statistics-release-1.7.3/inst/vartestn.m000066400000000000000000000404461475240274700203570ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {} vartestn (@var{x}) ## @deftypefnx {statistics} {} vartestn (@var{x}, @var{group}) ## @deftypefnx {statistics} {} vartestn (@dots{}, @var{name}, @var{value}) ## @deftypefnx {statistics} {@var{p} =} vartestn (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{stats}] =} vartestn (@dots{}) ## @deftypefnx {statistics} {[@var{p}, @var{stats}] =} vartestn (@dots{}, @var{name}, @var{value}) ## ## Test for equal variances across multiple groups. ## ## @code{@var{h} = vartestn (@var{x})} performs Bartlett's test for equal ## variances for the columns of the matrix @var{x}. This is a test of the null ## hypothesis that the columns of @var{x} come from normal distributions with ## the same variance, against the alternative that they come from normal ## distributions with different variances. The result is displayed in a summary ## table of statistics as well as a box plot of the groups. ## ## @code{vartestn (@var{x}, @var{group})} requires a vector @var{x}, and a ## @var{group} argument that is a categorical variable, vector, string array, or ## cell array of strings with one row for each element of @var{x}. Values of ## @var{x} corresponding to the same value of @var{group} are placed in the same ## group. ## ## @code{vartestn} treats NaNs as missing values, and ignores them. ## ## @code{@var{p} = vartestn (@dots{})} returns the probability of observing the ## given result, or one more extreme, by chance under the null hypothesis that ## all groups have equal variances. Small values of @var{p} cast doubt on the ## validity of the null hypothesis. ## ## @code{[@var{p}, @var{stats}] = vartestn (@dots{})} returns a structure with ## the following fields: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab @qcode{chistat} @tab -- the value of the test statistic ## @item @tab @qcode{df} @tab -- the degrees of freedom of the test ## @end multitable ## ## ## @code{[@var{p}, @var{stats}] = vartestn (@dots{}, @var{name}, @var{value})} ## specifies one or more of the following @var{name}/@var{value} pairs: ## ## @multitable @columnfractions 0.20 0.8 ## @item @qcode{"display"} @tab @qcode{"on"} to display a boxplot and table, or ## @qcode{"off"} to omit these displays. Default @qcode{"on"}. ## ## @item @qcode{"testtype"} @tab One of the following strings to control the ## type of test to perform ## @end multitable ## ## @multitable @columnfractions 0.03 0.25 0.72 ## @item @tab @qcode{"Bartlett"} @tab Bartlett's test (default). ## ## @item @tab @qcode{"LeveneQuadratic"} @tab Levene's test computed by ## performing anova on the squared deviations of the data values from their ## group means. ## ## @item @tab @qcode{"LeveneAbsolute"} @tab Levene's test computed by performing ## anova on the absolute deviations of the data values from their group means. ## ## @item @tab @qcode{"BrownForsythe"} @tab Brown-Forsythe test computed by ## performing anova on the absolute deviations of the data values from the group ## medians. ## ## @item @tab @qcode{"OBrien"} @tab O'Brien's modification of Levene's test with ## @math{W=0.5}. ## @end multitable ## ## The classical Bartlett's test is sensitive to the assumption that the ## distribution in each group is normal. The other test types are more robust ## to non-normal distributions, especially ones prone to outliers. For these ## tests, the STATS output structure has a field named @qcode{fstat} containing ## the test statistic, and @qcode{df1} and @qcode{df2} containing its numerator ## and denominator degrees of freedom. ## ## @seealso{vartest, vartest2, anova1, bartlett_test, levene_test} ## @end deftypefn function [p, stats] = vartestn (x, group, varargin) ## Validate input arguments if (nargin < 1) error ("vartestn: too few input arguments."); endif if (isscalar (x)) error ("vartestn: X must be a vector or a matrix."); endif if (nargin < 2) group = []; endif if (nargin > 1 && any (strcmpi (group, {"display", "testtype"}))) varargin = [{group} varargin]; group = []; endif if (isvector (x) && (nargin < 2 || isempty (group ))) error ("vartestn: if X is a vector then a group vector is required."); endif ## Add defaults plotdata = true; testtype = "Bartlett"; if (numel (varargin(:)) > 0 && mod (numel (varargin(:)), 2) == 0) for idx = 1:2:numel (varargin(:)) name = varargin{idx}; value = varargin{idx+1}; switch (lower (name)) case "display" plotdata = value; if (! any (strcmpi (plotdata, {"on", "off"}))) error ("vartestn: invalid value for display."); endif if (strcmpi (plotdata, "on")) plotdata = true; else plotdata = false; endif case "testtype" testtype = value; if (! any (strcmpi (testtype, {"Bartlett", "LeveneAbsolute", ... "LeveneQuadratic", "BrownForsythe", "OBrien"}))) error ("vartestn: invalid value for testtype."); endif otherwise error ("vartestn: invalid name for optional arguments."); endswitch endfor elseif (numel (varargin(:)) > 0 && mod (numel (varargin(:)), 2) != 0) error ("vartestn: optional arguments must be in name/value pairs."); endif ## Convert group to cell array from character array, make it a column if (! isempty (group) && ischar (group)) group = cellstr (group); endif if (size (group, 1) == 1) group = group'; endif ## If x is a matrix, convert it to column vector and create a ## corresponging column vector for groups if (length (x) < prod (size (x))) [n, m] = size (x); x = x(:); gi = reshape (repmat ((1:m), n, 1), n*m, 1); if (length (group) == 0) ## no group names are provided group = gi; elseif (size (group, 1) == m) ## group names exist and match columns group = group(gi,:); else error ("vartestn: columns in X and GROUP length do not match."); endif endif ## Check that x and group are the same size if (! all (numel (x) == numel (group))) error ("vartestn: GROUP must be a vector with the same number of rows as x."); endif ## Identify NaN values (if any) and remove them from X along with ## their corresponding values from group vector nonan = ! isnan (x); x = x(nonan); group = group(nonan, :); ## Convert group to indices and separate names [group_id, group_names] = grp2idx (group); group_id = group_id(:); ## Compute group summary statistics [group_mean, group_ster, group_size] = grpstats (x, group_id, ... {"mean", "sem", "numel"}); ## Compute group degreed of freedom and variances group_DF = group_size - 1; groupVAR = group_size .* group_ster .^ 2; sum_DF = sum (group_DF); ## Caculate pooled variance if (sum_DF > 0) pooledVAR = sum (group_DF .* groupVAR) / sum_DF; else pooledVAR = NaN; end ## Get number of groups k = length (group_DF); ## Test for equal variance according to specified testtype switch (lower (testtype)) case "bartlett" ## Calculate degrees of freedom Bdf = max(0, sum (group_DF > 0) - 1); ## Get valid groups msgroups = group_DF > 0; ## For valid groups if (Bdf > 0 && sum_DF > 0) B = log (pooledVAR) * sum (group_DF) - ... sum (group_DF(msgroups) .* log (groupVAR(msgroups))); C = 1 + (sum (1 ./ group_DF(msgroups)) - 1 / sum (group_DF)) / (3 * Bdf); F = B / C; else F = NaN; endif ## Compute p-value p = 1 - chi2cdf (F, Bdf); testname = "Bartlett's statistic "; if (nargout > 1) stats = struct("chisqstat", F, "df", Bdf); endif case {"leveneabsolute", "levenequadratic"} ## Remove single-sample groups ssgroups = find (group_size < 2); msgroups = ! ismember (group_id, ssgroups); ## Center each group with mean x_center = x(msgroups) - group_mean(group_id(msgroups)); ## Get number of valid groups (group size > 1) n_groups = length (group_size) - length (ssgroups); ## Perform one-way anova and extract results from the anova table if (n_groups > 1) if (strcmpi (testtype, "LeveneAbsolute")) [p, atab] = anova1 (abs (x_center), group_id(msgroups), "off"); testname = "Levene's statistic (absolute) "; else [p, atab] = anova1 (x_center .^ 2, group_id(msgroups), "off"); testname = "Levene's statistic (quadratic) "; endif ## Get F statistic and both degrees of freedom F = atab{2,5}; Bdf = [atab{2,3}, atab{3,3}]; else p = NaN; F = NaN; Bdf = [0, (length (x_center) - n_groups)]; endif if (nargout > 1) stats = struct("fstat", F, "df", Bdf); endif case "brownforsythe" ## Remove single-sample groups ssgroups = find (group_size < 2); msgroups = ! ismember (group_id, ssgroups); ## Calculate group medians group_md = grpstats (x, group_id, "median"); ## Center each group with median xcbf = x(msgroups) - group_md(group_id(msgroups)); ## Get number of valid groups (group size > 1) n_groups = length(group_size) - length(ssgroups); ## Perform one-way anova and extract results from the anova table if (n_groups > 1) [p, atab] = anova1 (abs (xcbf), group_id(msgroups), "off"); ## Get F statistic and both degrees of freedom F = atab{2,5}; Bdf = [atab{2,3}, atab{3,3}]; else p = NaN; F = NaN; Bdf = [0, (length (xcbf) - n_groups)]; end testname = "Brown-Forsythe statistic "; if (nargout > 1) stats = struct("fstat", F, "df", Bdf); endif case "obrien" ## Remove single-sample groups ssgroups = find (group_size < 2); msgroups = ! ismember (group_id, ssgroups); ## Center each group with mean x_center = x(msgroups) - group_mean(group_id(msgroups)); ## Calculate OBrien Z_ij xcs = x_center.^2; W = 0.5; xcw = ((W + group_size(group_id(msgroups)) - 2) .* ... group_size(group_id(msgroups)) .* xcs - W .* ... (group_size(group_id(msgroups)) - 1) .* ... groupVAR(group_id(msgroups))) ./ ... ((group_size(group_id(msgroups)) - 1) .* ... (group_size(group_id(msgroups)) - 2)); ## Get number of valid groups (group size > 1) n_groups = length(group_size) - length(ssgroups); ## Perform one-way anova and extract results from the anova table if (n_groups > 1) [p, atab] = anova1 (xcw, group_id(msgroups), "off"); ## Get F statistic and both degrees of freedom F = atab{2,5}; Bdf = [atab{2,3}, atab{3,3}]; else p = NaN; F = NaN; Bdf = [0, length(xcw)-n_groups]; end testname = "OBrien statistic "; if (nargout > 1) stats = struct("fstat", F, "df", Bdf); endif endswitch ## Print Group Summary Table (unless opted out) if (nargout == 0 || plotdata) groupSTD = sqrt (groupVAR); printf ("\n Group Summary Table\n\n"); printf ("Group Count Mean Std Dev\n"); printf ("------------------------------------------------------------\n"); for i = 1:k printf ("%-20s %10i %9.4f %1.6f\n", ... group_names{i}, group_size(i), group_mean(i), groupSTD(i)); endfor printf ("Pooled Groups %10i %9.4f %1.6f\n", ... sum (group_size), mean (group_mean), mean (groupSTD)); printf ("Pooled valid Groups %10i %9.4f %1.6f\n\n", ... sum (group_size(group_id(msgroups))), ... mean (group_mean(group_id(msgroups))), ... mean (groupSTD(group_id(msgroups)))); printf ("%s %7.5f\n", testname, F); if (numel (Bdf) == 1) printf ("Degrees of Freedom %10i\n", Bdf); else printf ("Degrees of Freedom %10i, %3i\n", Bdf(1), Bdf(2)); endif printf ("p-value %1.6f\n\n", p); endif ## Plot data using BOXPLOT (unless opted out) if (plotdata) boxplot (x, group_id, "Notch", "on", "Labels", group_names); endif endfunction %!demo %! ## Test the null hypothesis that the variances are equal across the five %! ## columns of data in the students’ exam grades matrix, grades. %! %! load examgrades %! vartestn (grades) %!demo %! ## Test the null hypothesis that the variances in miles per gallon (MPG) are %! ## equal across different model years. %! %! load carsmall %! vartestn (MPG, Model_Year) %!demo %! ## Use Levene’s test to test the null hypothesis that the variances in miles %! ## per gallon (MPG) are equal across different model years. %! %! load carsmall %! p = vartestn (MPG, Model_Year, "TestType", "LeveneAbsolute") %!demo %! ## Test the null hypothesis that the variances are equal across the five %! ## columns of data in the students’ exam grades matrix, grades, using the %! ## Brown-Forsythe test. Suppress the display of the summary table of %! ## statistics and the box plot. %! %! load examgrades %! [p, stats] = vartestn (grades, "TestType", "BrownForsythe", "Display", "off") ## Test input validation %!error vartestn (); %!error vartestn (1); %!error ... %! vartestn ([1, 2, 3, 4, 5, 6, 7]); %!error ... %! vartestn ([1, 2, 3, 4, 5, 6, 7], []); %!error ... %! vartestn ([1, 2, 3, 4, 5, 6, 7], "TestType", "LeveneAbsolute"); %!error ... %! vartestn ([1, 2, 3, 4, 5, 6, 7], [], "TestType", "LeveneAbsolute"); %!error ... %! vartestn ([1, 2, 3, 4, 5, 6, 7], [1, 1, 1, 2, 2, 2, 2], "Display", "some"); %!error ... %! vartestn (ones (50,3), "Display", "some"); %!error ... %! vartestn (ones (50,3), "Display", "off", "testtype", "some"); %!error ... %! vartestn (ones (50,3), [], "som"); %!error ... %! vartestn (ones (50,3), [], "some", "some"); %!error ... %! vartestn (ones (50,3), [1, 2], "Display", "off"); ## Test results %!test %! load examgrades %! [p, stat] = vartestn (grades, "Display", "off"); %! assert (p, 7.908647337018238e-08, 1e-14); %! assert (stat.chisqstat, 38.7332, 1e-4); %! assert (stat.df, 4); %!test %! load examgrades %! [p, stat] = vartestn (grades, "Display", "off", "TestType", "LeveneAbsolute"); %! assert (p, 9.523239714592791e-07, 1e-14); %! assert (stat.fstat, 8.5953, 1e-4); %! assert (stat.df, [4, 595]); %!test %! load examgrades %! [p, stat] = vartestn (grades, "Display", "off", "TestType", "LeveneQuadratic"); %! assert (p, 7.219514351897161e-07, 1e-14); %! assert (stat.fstat, 8.7503, 1e-4); %! assert (stat.df, [4, 595]); %!test %! load examgrades %! [p, stat] = vartestn (grades, "Display", "off", "TestType", "BrownForsythe"); %! assert (p, 1.312093241723211e-06, 1e-14); %! assert (stat.fstat, 8.4160, 1e-4); %! assert (stat.df, [4, 595]); %!test %! load examgrades %! [p, stat] = vartestn (grades, "Display", "off", "TestType", "OBrien"); %! assert (p, 8.235660885480556e-07, 1e-14); %! assert (stat.fstat, 8.6766, 1e-4); %! assert (stat.df, [4, 595]); statistics-release-1.7.3/inst/violin.m000066400000000000000000000267631475240274700200170ustar00rootroot00000000000000## Copyright (C) 2016 - Juan Pablo Carbajal ## Copyright (C) 2022-2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This progrm is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} violin (@var{x}) ## @deftypefnx {statistics} {@var{h} =} violin (@var{x}) ## @deftypefnx {statistics} {@var{h} =} violin (@dots{}, @var{property}, @var{value}, @dots{}) ## @deftypefnx {statistics} {@var{h} =} violin (@var{hax}, @dots{}) ## @deftypefnx {statistics} {@var{h} =} violin (@dots{}, @code{"horizontal"}) ## ## Produce a Violin plot of the data @var{x}. ## ## The input data @var{x} can be a N-by-m array containg N observations of m ## variables. It can also be a cell with m elements, for the case in which the ## variables are not uniformly sampled. ## ## The following @var{property} can be set using @var{property}/@var{value} pairs ## (default values in parenthesis). ## The value of the property can be a scalar indicating that it applies ## to all the variables in the data. ## It can also be a cell/array, indicating the property for each variable. ## In this case it should have m columns (as many as variables). ## ## @table @asis ## ## @item Color ## (@asis{"y"}) Indicates the filling color of the violins. ## ## @item Nbins ## (50) Internally, the function calls @command{hist} to compute the histogram ## of the data. This property indicates how many bins to use. ## See @command{help hist} for more details. ## ## @item SmoothFactor ## (4) The fuction performs simple kernel density estimation and automatically ## finds the bandwith of the kernel function that best approximates the ## histogram using optimization (@command{sqp}). ## The result is in general very noisy. To smooth the result the bandwidth is ## multiplied by the value of this property. The higher the value the smoother ## the violings, but values too high might remove features from the data ## distribution. ## ## @item Bandwidth ## (NA) If this property is given a value other than NA, it sets the bandwith of ## the kernel function. No optimization is peformed and the property ## @asis{SmoothFactor} is ignored. ## ## @item Width ## (0.5) Sets the maximum width of the violins. Violins are centered at integer ## axis values. The distance between two violin middle axis is 1. Setting a ## value higher thna 1 in this property will cause the violins to overlap. ## @end table ## ## If the string @asis{"Horizontal"} is among the input arguments, the violin ## plot is rendered along the x axis with the variables in the y axis. ## ## The returned structure @var{h} has handles to the plot elements, allowing ## customization of the visualization using set/get functions. ## ## Example: ## ## @example ## title ("Grade 3 heights"); ## axis ([0,3]); ## set (gca, "xtick", 1:2, "xticklabel", @{"girls"; "boys"@}); ## h = violin (@{randn(100,1)*5+140, randn(130,1)*8+135@}, "Nbins", 10); ## set (h.violin, "linewidth", 2) ## @end example ## ## @seealso{boxplot, hist} ## @end deftypefn function h = violin (ax, varargin) old_hold = ishold (); # First argument is not an axis if (~ishandle (ax) || ~isscalar (ax)) x = ax; ax = gca (); else x = varargin{1}; varargin(1) = []; endif ###################### ## Parse parameters ## parser = inputParser (); parser.CaseSensitive = false; parser.FunctionName = 'violin'; parser.addParamValue ('Nbins', 50); parser.addParamValue ('SmoothFactor', 4); parser.addParamValue ('Bandwidth', NA); parser.addParamValue ('Width', 0.5); parser.addParamValue ('Color', "y"); parser.addSwitch ('Horizontal'); parser.parse (varargin{:}); res = parser.Results; c = res.Color; # Color of violins if (ischar (c)) c = c(:); endif nb = res.Nbins; # Number of bins in histogram sf = res.SmoothFactor; # Smoothing factor for kernel estimation r0 = res.Bandwidth; # User value for KDE bandwith to prevent optimization is_horiz = res.Horizontal; # Whether the plot must be rotated width = res.Width; # Width of the violins clear parser res ###################### ## Make everything a cell for code simplicity if (~iscell (x)) [N Nc] = size (x); x = mat2cell (x, N, ones (1, Nc)); else Nc = numel (x); endif try [nb, c, sf, r0, width] = to_cell (nb, c, sf, r0, width, Nc); catch err if strcmp (err.identifier, "to_cell:element_idx") n = str2num (err.message); txt = {"Nbins", "Color", "SmoothFactor", "Bandwidth", "Width"}; error ("Octave:invalid-input-arg", ... ["options should be scalars or call/array with as many values as" ... " numbers of variables in the data (wrong size of %s)."], txt{n}); else rethrow (lasterror()) endif end ## Build violins [px py mx] = cellfun (@(y,n,s,r)build_polygon(y, n, s, r), ... x, nb, sf, r0, "unif", 0); Nc = 1:numel (px); Ncc = mat2cell (Nc, 1, ones (1, Nc(end))); ## get hold state old_hold = ishold (); ## Draw plain violins tmp = cellfun (@(x,y,n,u, w)patch(ax, (w * x + n)(:), y(:) ,u'), ... px, py, Ncc, c, width); h.violin = tmp; hold on ## Overlay mean value tmp = cellfun (@(z,y)plot(ax, z, y,'.k', "markersize", 6), Ncc, mx); h.mean = tmp; ## Overlay median Mx = cellfun (@median, x, "unif", 0); tmp = cellfun (@(z,y)plot(ax, z, y, 'ok'), Ncc, Mx); h.median = tmp; ## Overlay 1nd and 3th quartiles LUBU = cellfun (@(x,y)abs(quantile(x,[0.25 0.75])-y), x, Mx, "unif", 0); tmp = cellfun (@(x,y,z)errorbar(ax, x, y, z(1),z(2)), Ncc, Mx, LUBU)(:); ## Flatten errorbar output handles tmp2 = allchild (tmp); if (~iscell (tmp2)) tmp2 = mat2cell (tmp2, ones(length (tmp2), 1), 1); endif tmp = mat2cell (tmp, ones (length (tmp), 1), 1); tmp = cellfun (@vertcat, tmp, tmp2, "unif", 0); h.quartile = cell2mat (tmp); hold off ## Rotate the plot if it is horizontal if (is_horiz) structfun (@swap_axes, h); set (ax, "ytick", Nc); else set (ax, "xtick", Nc); endif if (nargout < 1); clear h; endif ## restore hold state if (old_hold) hold on endif endfunction function y = stdnormal_pdf (x) y = (2 * pi)^(- 1/2) * exp (- x .^ 2 / 2); endfunction function k = kde(x,r) k = mean (stdnormal_pdf (x / r)) / r; k /= max (k); endfunction function [px py mx] = build_polygon (x, nb, sf, r) N = size (x, 1); mx = mean (x); sx = std (x); X = (x - mx ) / sx; [count bin] = hist (X, nb); count /= max (count); Y = X - bin; if isna (r) r0 = 1.06 * N^(1/5); r = sqp (r0, @(r)sumsq (kde(Y,r) - count), [], [], 1e-3, 1e2); else sf = 1; endif sig = sf * r; ## Create violin polygon ## smooth tails: extend to 1.83 sigmas, i.e. ~99% of data. xx = linspace (0, 1.83 * sig, 5); bin = [bin(1)-fliplr(xx) bin bin(end)+xx]; py = [bin; fliplr(bin)].' * sx + mx; v = kde (X-bin, sig).'; px = [v -flipud(v)]; endfunction function tf = swap_axes (h) tmp = mat2cell (h(:), ones (length (h),1), 1); tmpy = cellfun(@(x)get(x, "ydata"), tmp, "unif", 0); tmpx = cellfun(@(x)get(x, "xdata"), tmp, "unif", 0); cellfun (@(h,x,y)set (h, "xdata", y, "ydata", x), tmp, tmpx, tmpy); tf = true; endfunction function varargout = to_cell (varargin) m = varargin{end}; varargin(end) = []; for i = 1:numel(varargin) x = varargin{i}; if (isscalar (x)) x = repmat (x, m, 1); endif if (iscell (x)) if (numel(x) ~= m) # no dimension equals m error ("to_cell:element_idx", "%d\n",i); endif varargout{i} = x; continue endif sz = size (x); d = find (sz == m); if (isempty (d)) # no dimension equals m error ("to_cell:element_idx", "%d\n",i); elseif (length (d) == 2) ## both dims are m, choose 1st elseif (d == 1) # 2nd dimension is m --> transpose x = x.'; sz = fliplr (sz); endif varargout{i} = mat2cell (x, sz(1), ones (m,1)); endfor endfunction %!demo %! clf %! x = zeros (9e2, 10); %! for i=1:10 %! x(:,i) = (0.1 * randn (3e2, 3) * (randn (3,1) + 1) + 2 * randn (1,3))(:); %! endfor %! h = violin (x, "color", "c"); %! axis tight %! set (h.violin, "linewidth", 2); %! set (gca, "xgrid", "on"); %! xlabel ("Variables") %! ylabel ("Values") %!demo %! clf %! data = {randn(100,1)*5+140, randn(130,1)*8+135}; %! subplot (1,2,1) %! title ("Grade 3 heights - vertical"); %! set (gca, "xtick", 1:2, "xticklabel", {"girls"; "boys"}); %! violin (data, "Nbins", 10); %! axis tight %! %! subplot(1,2,2) %! title ("Grade 3 heights - horizontal"); %! set (gca, "ytick", 1:2, "yticklabel", {"girls"; "boys"}); %! violin (data, "horizontal", "Nbins", 10); %! axis tight %!demo %! clf %! data = exprnd (0.1, 500,4); %! violin (data, "nbins", {5,10,50,100}); %! axis ([0 5 0 max(data(:))]) %!demo %! clf %! data = exprnd (0.1, 500,4); %! violin (data, "color", jet(4)); %! axis ([0 5 0 max(data(:))]) %!demo %! clf %! data = repmat(exprnd (0.1, 500,1), 1, 4); %! violin (data, "width", linspace (0.1,0.5,4)); %! axis ([0 5 0 max(data(:))]) %!demo %! clf %! data = repmat(exprnd (0.1, 500,1), 1, 4); %! violin (data, "nbins", [5,10,50,100], "smoothfactor", [4 4 8 10]); %! axis ([0 5 0 max(data(:))]) ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! data = exprnd (0.1, 500,4); %! violin (data, "color", jet(4)); %! axis ([0 5 0 max(data(:))]) %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! data = {randn(100,1)*5+140, randn(130,1)*8+135}; %! subplot (1,2,1) %! title ("Grade 3 heights - vertical"); %! set (gca, "xtick", 1:2, "xticklabel", {"girls"; "boys"}); %! violin (data, "Nbins", 10); %! axis tight %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! data = {randn(100,1)*5+140, randn(130,1)*8+135}; %! subplot (1,2,1) %! title ("Grade 3 heights - vertical"); %! set (gca, "xtick", 1:2, "xticklabel", {"girls"; "boys"}); %! violin (data, "Nbins", 10); %! axis tight %! subplot(1,2,2) %! title ("Grade 3 heights - horizontal"); %! set (gca, "ytick", 1:2, "yticklabel", {"girls"; "boys"}); %! violin (data, "horizontal", "Nbins", 10); %! axis tight %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! data = repmat(exprnd (0.1, 500,1), 1, 4); %! violin (data, "nbins", [5,10,50,100], "smoothfactor", [4 4 8 10]); %! axis ([0 5 0 max(data(:))]) %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect %!test %! hf = figure ("visible", "off"); %! unwind_protect %! data = repmat(exprnd (0.1, 500,1), 1, 4); %! violin (data, "width", linspace (0.1,0.5,4)); %! axis ([0 5 0 max(data(:))]) %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect statistics-release-1.7.3/inst/wblplot.m000066400000000000000000000271741475240274700201770ustar00rootroot00000000000000## Copyright (C) 2014 Bj{\"o}rn Vennberg ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {} wblplot (@var{data}, @dots{}) ## @deftypefnx {statistics} {@var{handle} =} wblplot (@var{data}, @dots{}) ## @deftypefnx {statistics} {[@var{handle}, @var{param}] =} wblplot (@var{data}) ## @deftypefnx {statistics} {[@var{handle}, @var{param}] =} wblplot (@var{data}, @var{censor}) ## @deftypefnx {statistics} {[@var{handle}, @var{param}] =} wblplot (@var{data}, @var{censor}, @var{freq}) ## @deftypefnx {statistics} {[@var{handle}, @var{param}] =} wblplot (@var{data}, @var{censor}, @var{freq}, @var{confint}) ## @deftypefnx {statistics} {[@var{handle}, @var{param}] =} wblplot (@var{data}, @var{censor}, @var{freq}, @var{confint}, @var{fancygrid}) ## @deftypefnx {statistics} {[@var{handle}, @var{param}] =} wblplot (@var{data}, @var{censor}, @var{freq}, @var{confint}, @var{fancygrid}, @var{showlegend}) ## ## Plot a column vector @var{data} on a Weibull probability plot using rank ## regression. ## ## @var{censor}: optional parameter is a column vector of same size as ## @var{data} with 1 for right censored data and 0 for exact observation. ## Pass [] when no censor data are available. ## ## @var{freq}: optional vector same size as @var{data} with the number of ## occurences for corresponding data. ## Pass [] when no frequency data are available. ## ## @var{confint}: optional confidence limits for ploting upper and lower ## confidence bands using beta binomial confidence bounds. If a single ## value is given this will be used such as LOW = a and HIGH = 1 - a. ## Pass [] if confidence bounds is not requested. ## ## @var{fancygrid}: optional parameter which if set to anything but 1 will turn ## off the fancy gridlines. ## ## @var{showlegend}: optional parameter that when set to zero(0) turns off the ## legend. ## ## If one output argument is given, a @var{handle} for the data marker and ## plotlines is returned, which can be used for further modification of line and ## marker style. ## ## If a second output argument is specified, a @var{param} vector with scale, ## shape and correlation factor is returned. ## ## @seealso{normplot, wblpdf} ## @end deftypefn function [handle, param] = wblplot (data, censor = [], freq = [], ... confint = [], fancygrid = 1, showlegend = 1) [mm, nn] = size (data); if (mm > 1 && nn > 1) error ("wblplot: can only handle a single data vector") elseif (mm == 1 && nn > 1) data = data(:); mm = nn; endif if (any (data <= 0)) error ("wblplot: data vector must be positive and non zero") endif if (isempty (freq)) freq = ones (mm, 1); N = mm; else [mmf nnf] = size (freq); if ((mmf == mm && nnf == 1) || (mmf == 1 && nnf == mm)) freq = freq(:); N = sum (freq); ## Total number of samples if (any (freq <= 0)) error ("wblplot: frequency vector must be positive non zero integers") endif else error ("wblplot: frequency must be vector of same length as data") endif endif if (isempty (censor)) censor = zeros(mm,1); else [mmc, nnc] = size(censor); if ((mmc == mm && nnc == 1) || (mmc == 1 && nnc == mm)) censor = censor(:); else error ("wblplot: censor must be a vector of same length as data") endif ## Make sure censored data is sorted corectly so that no censored samples ## are processed before failures if they have the same time. if (any (censor > 0)) ind = find (censor > 0); ind2 = find (data(1:end-1) == data(2:end)); if ((! isempty (ind)) && (! isempty (ind2))) if (any (ind == ind2)) tmp = censor(ind2); censor(ind2) = censor(ind2 + 1); censor(ind2+1) = tmp; tmp = freq(ind2); freq(ind2) = freq(ind2 + 1); freq(ind2 + 1) = tmp; endif endif endif endif ## Determine the order number wbdat = zeros (length (find (censor == 0)), 3); Op = 0; Oi = 0; c = N; nf = 0; for k = 1 : mm if (censor(k, 1) == 0) nf = nf + 1; wbdat(nf, 1) = data(k, 1); for s = 1 : freq(k, 1); Oi = Op + ((N + 1) - Op) / (1 + c); Op = Oi; c = c - 1; endfor wbdat(nf, 3) = Oi; else c = c - freq(k, 1); endif endfor ## Compute median rank a = wbdat(:, 3) ./ (N - wbdat(:, 3) + 1); f = finv(0.5, 2 * (N - wbdat(:, 3) + 1), 2 * wbdat(:, 3)); wbdat(:, 2) = a ./ (f+a); datx = log (wbdat(:,1)); daty = log (log (1 ./ (1 - wbdat(:,2)))); ## Rank regression poly = polyfit (datx, daty, 1); ## Shape factor beta_rry = poly(1); ## Scale factor eta_rry = exp (-(poly(2) / beta_rry)); ## Determine min-max values of view port aa = ceil (log10 (max (wbdat(:,1)))); bb = log10 (max (wbdat(:,1))); if ((aa - bb) < 0.2) aa = ceil (log10 (max (wbdat(:,1)))) + 1; endif xmax = 10 ^ aa; if ((log10 (min (wbdat(:,1))) - floor (log10 (min (wbdat(:,1))))) < 0.2) xmin = 10 ^ (floor (log10 (min (wbdat(:,1)))) - 1); else xmin = 10 ^ floor (log10 (min (wbdat(:,1)))); endif if (min (wbdat(:,2)) > 0.20) ymin = log (log (1 / (1 - 0.1))); elseif (min (wbdat(:,2)) > 0.02) ymin = log (log (1 / (1 - 0.01))); elseif (min (wbdat(:,2)) > 0.002) ymin = log (log (1 / (1 - 0.001))); else ymin = log (log (1 / (1 - 0.0001))); endif ymax= log (log (1 / (1 - 0.999))); x = [0;0]; y = [0;0]; label = char('0.10', '1.00', '10.00', '99.00'); prob = [0.001 0.01 0.1 0.99]; tick = log (log (1 ./ (1 - prob))); xbf = [xmin; xmax]; ybf = polyval (poly, log (xbf)); newplot(); x(1, 1) = xmin; x(2, 1) = xmax; if (fancygrid == 1) for k = 1 : 4 ## Y major grids x(1, 1) = xmin; x(2, 1) = xmax*10; y(1, 1) = log (log (1 / (1 - 10 ^ (-k)))); y(2, 1) = y(1, 1); ymajorgrid(k) = line (x, y, 'LineStyle', '-', 'Marker', 'none', ... 'Color', [1 0.75 0.75], 'LineWidth', 0.1); endfor ## Y Minor grids 2 - 9 x(1, 1) = xmin; x(2, 1) = xmax * 10; for m = 1 : 4 for k = 1 : 8 y(1, 1) = log (log (1 / (1 - ((k + 1) / (10 ^ m))))); y(2, 1) = y(1, 1); yminorgrid(k) = line (x, y, 'LineStyle', '-', 'Marker', 'none', ... 'Color', [0.75 1 0.75], 'LineWidth', 0.1); endfor endfor ## X-axis grid y(1, 1) = ymin; y(2, 1) = ymax; for m = log10 (xmin) : log10 (xmax) x(1, 1) = 10 ^ m; x(2, 1) = x(1, 1); y(1, 1) = ymin; y(2, 1) = ymax; xmajorgrid(k) = line (x, y, 'LineStyle', '-', 'Marker', 'none', ... 'Color', [1 0.75 0.75]); for k = 1 : 8 ## X Minor grids - 2 - 9 x(1, 1) = (k + 1) * (10 ^ m); x(2, 1) = (k + 1) * (10 ^ m); xminorgrid(k) = line (x, y, 'LineStyle', '-', 'Marker', 'none', ... 'Color', [0.75 1 0.75], 'LineWidth', 0.1); endfor endfor endif set (gca, 'XScale', 'log'); set (gca, 'YTick', tick, 'YTickLabel', label); xlabel ('Data', 'FontSize', 12); ylabel ('Unreliability, F(t)=1-R(t)', 'FontSize', 12); title ('Weibull Probability Plot', 'FontSize', 12); set (gcf, 'Color', [0.9, 0.9, 0.9]); set (gcf, 'name', 'WblPlot'); hold on h = plot (wbdat(:,1), daty, 'o'); set (h, 'markerfacecolor', [0, 0, 1]); set (h, 'markersize', 8); h2 = line (xbf, ybf, 'LineStyle', '-', 'Marker', 'none', ... 'Color', [0.25 0.25 1], 'LineWidth', 1); ## If requested plot beta binomial confidens bounds if (! isempty (confint)) cb_high = []; cb_low = []; if (length (confint) == 1) if (confint > 0.5) cb_high = confint; cb_low = 1 - confint; else cb_high = 1 - confint; cb_low = confint; endif else cb_high = confint(2); cb_low = confint(1); endif conf = zeros (N + 4, 3); betainv = 1 / beta_rry; N2 = [1:N]'; N2 = [0.3; 0.7; N2; N2(end) + 0.5; N2(end) + 0.8]; ## Extend the ends a bit ypos = medianranks (0.5, N, N2); conf(:, 1) = eta_rry * log (1 ./ (1 - ypos)) .^ betainv; conf(:, 2) = medianranks (cb_low, N, N2); conf(:, 3) = medianranks (cb_high, N, N2); confy = log (log (1 ./ (1 - conf(:,2:3)))); confu = [conf(:,1) confy]; if (conf(1,1) > xmin) ## It looks better to extend the lines. p1 = polyfit (log (conf(1:2,1)), confy(1:2,1), 1); y1 = polyval (p1, log (xmin)); p2 = polyfit (log (conf(1:2,1)), confy(1:2,2), 1); y2 = polyval (p2, log (xmin)); confu = [xmin y1 y2; confu]; endif if (conf(end,1) < xmax) p3 = polyfit (log (conf(end-1:end,1)), confy(end-1:end,1), 1); y3 = polyval (p3, log (xmax)); p4 = polyfit (log (conf(end-1:end,1)), confy(end-1:end,2), 1); y4 = polyval (p4, log (xmax)); confu = [confu; xmax, y3, y4]; endif h3 = plot (confu(:,1), confu(:,2:3), 'LineStyle', '-' ,'Marker', 'none', ... 'Color', [1 0.25 0.25], 'LineWidth', 1); endif ## Correlation coefficient rsq = corr (datx, daty); if (showlegend == 1) s1 = sprintf (' RRY\n \\beta=%.3f \n \\eta=%.2f \n \\rho=%.4f', ... beta_rry, eta_rry, rsq); if (! isempty (confint)) s2 = sprintf ('CB_H=%.2f', cb_high); s3 = sprintf ('CB_L=%.2f', cb_low); legend ([h; h2; h3], "Data", s1, s2, s3, "location", "northeastoutside"); else legend ([h; h2], "Data", s1, "location", "northeastoutside"); endif legend ("boxoff"); endif axis ([xmin, xmax, ymin, (log (log (1 / (1 - 0.99))))]); hold off if (nargout >= 2) param = [eta_rry, beta_rry, rsq]; if (! isempty (confint)) handle = [h; h2; h3]; else handle = [h; h2]; endif endif if (nargout == 1) if (! isempty (confint)) handle = [h; h2; h3]; else handle = [h; h2]; endif endif endfunction function ret = medianranks (alpha, n, ii) a = ii ./ (n - ii + 1); f = finv (alpha, 2 * (n - ii + 1), 2 * ii); ret = a ./ (f + a); endfunction %!demo %! x = [16 34 53 75 93 120]; %! wblplot (x); %!demo %! x = [2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67]'; %! c = [0 1 0 1 0 1 1 1 0 0 1 0 1 0 1 1 0 1 1]'; %! [h, p] = wblplot (x, c); %! p %!demo %! x = [16, 34, 53, 75, 93, 120, 150, 191, 240 ,339]; %! [h, p] = wblplot (x, [], [], 0.05); %! p %! ## Benchmark Reliasoft eta = 146.2545 beta 1.1973 rho = 0.9999 %!demo %! x = [46 64 83 105 123 150 150]; %! c = [0 0 0 0 0 0 1]; %! f = [1 1 1 1 1 1 4]; %! wblplot (x, c, f, 0.05); %!demo %! x = [46 64 83 105 123 150 150]; %! c = [0 0 0 0 0 0 1]; %! f = [1 1 1 1 1 1 4]; %! ## Subtract 30.92 from x to simulate a 3 parameter wbl with gamma = 30.92 %! wblplot (x - 30.92, c, f, 0.05); ## Test plotting %!test %! hf = figure ("visible", "off"); %! unwind_protect %! x = [16, 34, 53, 75, 93, 120, 150, 191, 240 ,339]; %! [h, p] = wblplot (x, [], [], 0.05); %! assert (numel (h), 4) %! assert (p(1), 146.2545, 1E-4) %! assert (p(2), 1.1973, 1E-4) %! assert (p(3), 0.9999, 5E-5) %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect statistics-release-1.7.3/inst/x2fx.m000066400000000000000000000207171475240274700173770ustar00rootroot00000000000000## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {statistics} {[@var{d}, @var{model}, @var{termstart}, @var{termend}] =} x2fx (@var{x}) ## @deftypefnx {statistics} {[@var{d}, @var{model}, @var{termstart}, @var{termend}] =} x2fx (@var{x}, @var{model}) ## @deftypefnx {statistics} {[@var{d}, @var{model}, @var{termstart}, @var{termend}] =} x2fx (@var{x}, @var{model}, @var{categ}) ## @deftypefnx {statistics} {[@var{d}, @var{model}, @var{termstart}, @var{termend}] =} x2fx (@var{x}, @var{model}, @var{categ}, @var{catlevels}) ## ## Convert predictors to design matrix. ## ## @code{@var{d} = x2fx (@var{x}, @var{model})} converts a matrix of predictors ## @var{x} to a design matrix @var{d} for regression analysis. Distinct ## predictor variables should appear in different columns of @var{x}. ## ## The optional input @var{model} controls the regression model. By default, ## @code{x2fx} returns the design matrix for a linear additive model with a ## constant term. @var{model} can be any one of the following strings: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @item @tab "linear" @tab Constant and linear terms (the default) ## @item @tab "interaction" @tab Constant, linear, and interaction terms ## @item @tab "quadratic" @tab Constant, linear, interaction, and squared terms ## @item @tab "purequadratic" @tab Constant, linear, and squared terms ## @end multitable ## ## If @var{x} has n columns, the order of the columns of @var{d} for a full ## quadratic model is: ## ## @itemize ## @item ## The constant term. ## @item ## The linear terms (the columns of X, in order 1,2,...,n). ## @item ## The interaction terms (pairwise products of columns of @var{x}, in order ## (1,2), (1,3), ..., (1,n), (2,3), ..., (n-1,n). ## @item ## The squared terms (in the order 1,2,...,n). ## @end itemize ## ## Other models use a subset of these terms, in the same order. ## ## Alternatively, MODEL can be a matrix specifying polynomial terms of arbitrary ## order. In this case, MODEL should have one column for each column in X and ## one r for each term in the model. The entries in any r of MODEL are powers ## for the corresponding columns of @var{x}. For example, if @var{x} has ## columns X1, X2, and X3, then a row [0 1 2] in @var{model} would specify the ## term (X1.^0).*(X2.^1).*(X3.^2). A row of all zeros in @var{model} specifies ## a constant term, which you can omit. ## ## @code{@var{d} = x2fx (@var{x}, @var{model}, @var{categ})} treats columns with ## numbers listed in the vector @var{categ} as categorical variables. Terms ## involving categorical variables produce dummy variable columns in @var{d}. ## Dummy variables are computed under the assumption that possible categorical ## levels are completely enumerated by the unique values that appear in the ## corresponding column of @var{x}. ## ## @code{@var{d} = x2fx (@var{x}, @var{model}, @var{categ}, @var{catlevels})} ## accepts a vector @var{catlevels} the same length as @var{categ}, specifying ## the number of levels in each categorical variable. In this case, values in ## the corresponding column of @var{x} must be integers in the range from 1 to ## the specified number of levels. Not all of the levels need to appear in ## @var{x}. ## ## @end deftypefn function [D, model, termstart, termend] = x2fx (x, model, categ, catlevels) ## Get matrix size [m,n] = size(x); ## Get data class if (isa (x, "single")) data_class = 'single'; else data_class = 'double'; endif ## Check for input arguments if (nargin < 2 || isempty (model)) model = 'linear'; endif if (nargin < 3) categ = []; endif if (nargin < 4) catlevels = []; endif ## Convert models parsed as strings to numerical matrix if (ischar (model)) if (strcmpi (model, "linear") || strcmpi (model, "additive")) interactions = false; quadratic = false; elseif (strcmpi (model, "interactions")) interactions = true; quadratic = false; elseif (strcmpi (model, "quadratic")) interactions = true; quadratic = true; elseif (strcmpi (model, "purequadratic")) interactions = false; quadratic = true; else D = feval (model, x); termstart = []; termend = []; return endif I = eye(n); ## Construct interactions part if (interactions && n > 1) [r, c] = find (tril (ones (n) ,-1)); nt = length(r); intpart = zeros(nt,n); intpart(sub2ind (size (intpart),(1:nt)', r)) = 1; intpart(sub2ind (size (intpart),(1:nt)', c)) = 1; else intpart = zeros(0,n); endif ## Construct quadratic part if (quadratic) quadpart = 2 * I; quadpart(categ,:) = []; else quadpart = zeros(0,n); endif model = [zeros(1,n); I]; model = [model; intpart; quadpart]; endif ## Process each categorical variable catmember = ismember (1:n, categ); var_DF = ones(1,n); if (isempty (catlevels)) ## Get values of each categorical variable and replace them with integers for idx=1:length(categ) categ_idx = categ(idx); [Y, I, J] = unique (x(:,categ_idx)); var_DF(categ_idx) = length (Y) - 1; x(:,categ_idx) = J; endfor else ## Ensure all categorical variables take valid values var_DF(categ) = catlevels - 1; for idx = 1:length (categ) categ_idx = categ(idx); if (any (! ismember (x(:,categ_idx), 1:catlevels(idx)))) error("x2fx: wrong value %f in category %d.", ... catlevels(idx), categ_idx); endif endfor endif ## Get size of model martix [r, c] = size (model); ## Check for equal number of columns between x and model if (c != n) error("x2fx: wrong number of columns between x and model"); endif ## Check model category column for values greater than 1 if (any (model(:,categ) > 1, 1)) error("x2fx: wrong values in model's category column"); endif ## Allocate space for the dummy variables for all terms termdf = prod (max (1, (model > 0) .* repmat (var_DF, r, 1)), 2); termend = cumsum (termdf); termstart = termend - termdf + 1; D = zeros (m, termend(end), data_class); allrows = (1:m)'; for idx = 1:r cols = termstart(idx):termend(idx); pwrs = model(idx,:); t = pwrs > 0; C = 1; if (any (t)) if (any (pwrs(! catmember))) pwrs_cat = pwrs .* !catmember; C = ones (size (x, 1), 1); collist = find (pwrs_cat > 0); for idx = 1:length (collist) categ_idx = collist(idx); C = C .* x(:,categ_idx) .^ pwrs_cat(categ_idx); endfor endif if (any (pwrs(catmember) > 0)) Z = zeros (m, termdf(idx)); collist = find (pwrs > 0 & catmember); xcol = x(:,collist(1)); keep = (xcol <= var_DF(collist(1))); colnum = xcol; cumdf = 1; for idx=2:length(collist) cumdf = cumdf * var_DF(collist(idx-1)); xcol = x(:,collist(idx)); keep = keep & (xcol <= var_DF(collist(idx))); colnum = colnum + cumdf * (xcol - 1); endfor if (length (C) > 1) C = C(keep); endif Z(sub2ind(size(Z),allrows(keep),colnum(keep))) = C; C = Z; endif endif D(:,cols) = C; endfor endfunction %!test %! X = [1, 10; 2, 20; 3, 10; 4, 20; 5, 15; 6, 15]; %! D = x2fx(X,'quadratic'); %! assert (D(1,:) , [1, 1, 10, 10, 1, 100]); %! assert (D(2,:) , [1, 2, 20, 40, 4, 400]); %!test %! X = [1, 10; 2, 20; 3, 10; 4, 20; 5, 15; 6, 15]; %! model = [0, 0; 1, 0; 0, 1; 1, 1; 2, 0]; %! D = x2fx(X,model); %! assert (D(1,:) , [1, 1, 10, 10, 1]); %! assert (D(2,:) , [1, 2, 20, 40, 4]); %! assert (D(4,:) , [1, 4, 20, 80, 16]); %!error x2fx ([1, 10; 2, 20; 3, 10], [0; 1]); %!error x2fx ([1, 10, 15; 2, 20, 40; 3, 10, 25], [0, 0; 1, 0; 0, 1; 1, 1; 2, 0]); %!error x2fx ([1, 10; 2, 20; 3, 10], "whatever"); statistics-release-1.7.3/inst/ztest.m000066400000000000000000000167021475240274700176600ustar00rootroot00000000000000## Copyright (C) 2014 Tony Richardson ## Copyright (C) 2022 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} ztest (@var{x}, @var{m}, @var{sigma}) ## @deftypefnx {statistics} {@var{h} =} ztest (@var{x}, @var{m}, @var{sigma}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} ztest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}] =} ztest (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{ci}, @var{zvalue}] =} ztest (@dots{}) ## ## One-sample Z-test. ## ## @code{@var{h} = ztest (@var{x}, @var{v})} performs a Z-test of the hypothesis ## that the data in the vector @var{x} come from a normal distribution with mean ## @var{m}, against the alternative that @var{x} comes from a normal ## distribution with a different mean @var{m}. The result is @var{h} = 0 if the ## null hypothesis ("mean is M") cannot be rejected at the 5% significance ## level, or @var{h} = 1 if the null hypothesis can be rejected at the 5% level. ## ## @var{x} may also be a matrix or an N-D array. For matrices, @code{ztest} ## performs separate tests along each column of @var{x}, and returns a vector of ## results. For N-D arrays, @code{ztest} works along the first non-singleton ## dimension of @var{x}. @var{m} and @var{sigma} must be a scalars. ## ## @code{ztest} treats NaNs as missing values, and ignores them. ## ## @code{[@var{h}, @var{pval}] = ztest (@dots{})} returns the p-value. That ## is the probability of observing the given result, or one more extreme, by ## chance if the null hypothesisis true. ## ## @code{[@var{h}, @var{pval}, @var{ci}] = ztest (@dots{})} returns a ## 100 * (1 - @var{alpha})% confidence interval for the true mean. ## ## @code{[@var{h}, @var{pval}, @var{ci}, @var{zvalue}] = ztest (@dots{})} ## returns the value of the test statistic. ## ## @code{[@dots{}] = ztest (@dots{}, @var{Name}, @var{Value}, @dots{})} ## specifies one or more of the following @var{Name}/@var{Value} pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Name} @tab @var{Value} ## @item @tab "alpha" @tab the significance level. Default is 0.05. ## ## @item @tab "dim" @tab dimension to work along a matrix or an N-D array. ## ## @item @tab "tail" @tab a string specifying the alternative hypothesis: ## @end multitable ## @multitable @columnfractions 0.1 0.15 0.75 ## @item @tab "both" @tab "mean is not @var{m}" (two-tailed, default) ## @item @tab "left" @tab "mean is less than @var{m}" (left-tailed) ## @item @tab "right" @tab "mean is greater than @var{m}" (right-tailed) ## @end multitable ## ## @seealso{ttest, vartest, signtest, kstest} ## @end deftypefn function [h, pval, ci, zvalue] = ztest (x, m, sigma, varargin) ## Validate input arguments if (nargin < 3) error ("ztest: too few input arguments."); endif if (! isscalar (m) || ! isnumeric(m) || ! isreal(m)) error ("ztest: invalid value for mean."); endif if (! isscalar (sigma) || ! isnumeric(sigma) || ! isreal(sigma) || sigma < 0) error ("ztest: invalid value for standard deviation."); endif ## Add defaults alpha = 0.05; tail = "both"; dim = []; if (nargin > 3) for idx = 4:2:nargin name = varargin{idx-3}; value = varargin{idx-2}; switch (lower (name)) case "alpha" alpha = value; if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("ztest: invalid VALUE for alpha."); endif case "tail" tail = value; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("ztest: invalid VALUE for tail."); endif case "dim" dim = value; if (! isscalar (dim) || ! ismember (dim, 1:ndims (x))) error ("ztest: invalid VALUE for operating dimension."); endif otherwise error ("ztest: invalid NAME for optional arguments."); endswitch endfor endif ## Figure out which dimension mean will work along if (isempty (dim)) dim = find (size (x) != 1, 1); endif ## Replace all NaNs with zeros is_nan = isnan (x); ## Find sample size for each group (if more than one) if (any (is_nan(:))) sz = sum (! is_nan, dim); else sz = size (x, dim); endif ## Calculate mean, strandard error and z-value for each group x_mean = sum (x(! is_nan), dim) ./ max (1, sz); stderr = sigma ./ sqrt (sz); zvalue = (x_mean - m) ./ stderr; ## Calculate p-value for the test and confidence intervals (if requested) if (strcmpi (tail, "both")) pval = 2 * normcdf (- abs (zvalue), 0, 1); if (nargout > 2) crit = norminv (1 - alpha / 2, 0, 1) .* stderr; ci = cat (dim, x_mean - crit, x_mean + crit); endif elseif (strcmpi (tail, "right")) p = normcdf (- zvalue,0,1); if (nargout > 2) crit = norminv (1 - alpha, 0, 1) .* stderr; ci = cat (dim, x_mean - crit, Inf (size (p))); endif elseif (strcmpi (tail, "left")) p = normcdf (zvalue, 0, 1); if (nargout > 2) crit = norminv (1 - alpha, 0, 1) .* stderr; ci = cat (dim, - Inf (size (p)), x_mean + crit); endif endif ## Determine the test outcome h = double (pval < alpha); h(isnan (pval)) = NaN; endfunction ## Test input validation %!error ztest (); %!error ... %! ztest ([1, 2, 3, 4], 2, -0.5); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "alpha", 0); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "alpha", 1.2); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "alpha", "val"); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "tail", "val"); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "alpha", 0.01, "tail", "val"); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "dim", 3); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "alpha", 0.01, "tail", "both", "dim", 3); %!error ... %! ztest ([1, 2, 3, 4], 1, 2, "alpha", 0.01, "tail", "both", "badoption", 3); ## Test results %!test %! load carsmall %! [h, pval, ci] = ztest (MPG, mean (MPG, "omitnan"), std (MPG, "omitnan")); %! assert (h, 0); %! assert (pval, 1, 1e-14); %! assert (ci, [22.094; 25.343], 1e-3); %!test %! load carsmall %! [h, pval, ci] = ztest (MPG, 26, 8); %! assert (h, 1); %! assert (pval, 0.00568359158544743, 1e-14); %! assert (ci, [22.101; 25.335], 1e-3); %!test %! load carsmall %! [h, pval, ci] = ztest (MPG, 26, 4); %! assert (h, 1); %! assert (pval, 3.184168011941316e-08, 1e-14); %! assert (ci, [22.909; 24.527], 1e-3); statistics-release-1.7.3/inst/ztest2.m000066400000000000000000000132151475240274700177360ustar00rootroot00000000000000## Copyright (C) 1996-2017 Kurt Hornik ## Copyright (C) 2023 Andreas Bertsatos ## ## This file is part of the statistics package for GNU Octave. ## ## This program is free software: you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation, either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {statistics} {@var{h} =} ztest2 (@var{x1}, @var{n1}, @var{x2}, @var{n2}) ## @deftypefnx {statistics} {@var{h} =} ztest2 (@var{x1}, @var{n1}, @var{x2}, @var{n2}, @var{Name}, @var{Value}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}] =} ztest2 (@dots{}) ## @deftypefnx {statistics} {[@var{h}, @var{pval}, @var{zvalue}] =} ztest2 (@dots{}) ## ## Two proportions Z-test. ## ## If @var{x1} and @var{n1} are the counts of successes and trials in one ## sample, and @var{x2} and @var{n2} those in a second one, test the null ## hypothesis that the success probabilities @math{p1} and @math{p2} are the ## same. The result is @var{h} = 0 if the null hypothesis cannot be rejected at ## the 5% significance level, or @var{h} = 1 if the null hypothesis can be ## rejected at the 5% level. ## ## Under the null, the test statistic @var{zvalue} approximately follows a ## standard normal distribution. ## ## The size of @var{h}, @var{pval}, and @var{zvalue} is the common size of ## @var{x}, @var{n1}, @var{x2}, and @var{n2}, which must be scalars or of common ## size. A scalar input functions as a constant matrix of the same size as the ## other inputs. ## ## @code{[@var{h}, @var{pval}] = ztest2 (@dots{})} returns the p-value. That ## is the probability of observing the given result, or one more extreme, by ## chance if the null hypothesisis true. ## ## @code{[@var{h}, @var{pval}, @var{zvalue}] = ztest2 (@dots{})} returns the ## value of the test statistic. ## ## @code{[@dots{}] = ztest2 (@dots{}, @var{Name}, @var{Value}, @dots{})} ## specifies one or more of the following @var{Name}/@var{Value} pairs: ## ## @multitable @columnfractions 0.05 0.2 0.75 ## @headitem @tab @var{Name} @tab @var{Value} ## @item @tab @qcode{"alpha"} @tab the significance level. Default is 0.05. ## ## @item @tab @qcode{"tail"} @tab a string specifying the alternative hypothesis ## @end multitable ## @multitable @columnfractions 0.1 0.25 0.65 ## @item @tab @qcode{"both"} @tab @math{p1} is not @math{p2} ## (two-tailed, default) ## @item @tab @qcode{"left"} @tab @math{p1} is less than @math{p2} ## (left-tailed) ## @item @tab @qcode{"right"} @tab @math{p1} is greater than @math{p2} ## (right-tailed) ## @end multitable ## ## @seealso{chi2test, fishertest} ## @end deftypefn function [h, pval, zvalue] = ztest2 (x1, n1, x2, n2, varargin) if (nargin < 4) print_usage (); endif if (! isscalar (x1) || ! isscalar (n1) || ! isscalar (x2) || ! isscalar (n2)) [retval, x1, n1, x2, n2] = common_size (x1, n1, x2, n2); if (retval > 0) error ("ztest2: X1, N1, X2, and N2 must be of common size or scalars."); endif endif if (iscomplex (x1) || iscomplex (n1) || iscomplex (x2) || iscomplex(n2)) error ("ztest2: X1, N1, X2, and N2 must not be complex."); endif ## Add defaults and parse optional arguments alpha = 0.05; tail = "both"; if (nargin > 4) params = numel (varargin); if ((params / 2) != fix (params / 2)) error ("ztest2: optional arguments must be in NAME-VALUE pairs.") endif for idx = 1:2:params name = varargin{idx}; value = varargin{idx+1}; switch (lower (name)) case "alpha" alpha = value; if (! isscalar (alpha) || ! isnumeric (alpha) || ... alpha <= 0 || alpha >= 1) error ("ztest2: invalid VALUE for alpha."); endif case "tail" tail = value; if (! any (strcmpi (tail, {"both", "left", "right"}))) error ("ztest2: invalid VALUE for tail."); endif otherwise error ("ztest2: invalid NAME for optional arguments."); endswitch endfor endif p1 = x1 ./ n1; p2 = x2 ./ n2; pc = (x1 + x2) ./ (n1 + n2); zvalue = (p1 - p2) ./ sqrt (pc .* (1 - pc) .* (1 ./ n1 + 1 ./ n2)); cdf = normcdf (zvalue); if (strcmpi (tail, "both")) pval = 2 * min (cdf, 1 - cdf); elseif (strcmpi (tail, "right")) pval = 1 - cdf; elseif (strcmpi (tail, "left")) pval = cdf; endif ## Determine the test outcome h = double (pval < alpha); h(isnan (pval)) = NaN; endfunction ## Test input validation %!error ztest2 (); %!error ztest2 (1); %!error ztest2 (1, 2); %!error ztest2 (1, 2, 3); %!error ... %! ztest2 (1, 2, 3, 4, "alpha") %!error ... %! ztest2 (1, 2, 3, 4, "alpha", 0); %!error ... %! ztest2 (1, 2, 3, 4, "alpha", 1.2); %!error ... %! ztest2 (1, 2, 3, 4, "alpha", "val"); %!error ... %! ztest2 (1, 2, 3, 4, "tail", "val"); %!error ... %! ztest2 (1, 2, 3, 4, "alpha", 0.01, "tail", "val"); %!error ... %! ztest2 (1, 2, 3, 4, "alpha", 0.01, "tail", "both", "badoption", 3); statistics-release-1.7.3/io.github.gnu_octave.statistics.metainfo.xml000066400000000000000000000022271475240274700261060ustar00rootroot00000000000000 io.github.gnu_octave.statistics org.octave.Octave FSFAP GPL-3.0-or-later Statistics The Statistics package for GNU Octave Octave Statistics

The Statistics package for GNU Octave is a collection of functions for statistical analysis.

https://github.com/gnu-octave/statistics/issues https://gnu-octave.github.io/statistics Octave Community octave-maintainers@gnu.org
statistics-release-1.7.3/src/000077500000000000000000000000001475240274700161355ustar00rootroot00000000000000statistics-release-1.7.3/src/Makefile000066400000000000000000000005311475240274700175740ustar00rootroot00000000000000# Makefile for compiling required oct files all: $(MKOCTFILE) editDistance.cc $(MKOCTFILE) libsvmread.cc $(MKOCTFILE) libsvmwrite.cc $(MKOCTFILE) svmpredict.cc svm.cpp svm_model_octave.cc $(MKOCTFILE) svmtrain.cc svm.cpp svm_model_octave.cc $(MKOCTFILE) fcnntrain.cc $(MKOCTFILE) fcnnpredict.cc statistics-release-1.7.3/src/editDistance.cc000066400000000000000000000557641475240274700210650ustar00rootroot00000000000000/* Copyright (C) 2024 Andreas Bertsatos This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include using namespace std; struct UniqueVecOut { ColumnVector IA; ColumnVector IC; Cell IA_c; }; // Function for computing the minimum of three integer values int minimum (int a, int b, int c) { int min = a; if (b < min) min = b; if (c < min) min = c; return min; } // Function for computing the Levenshtein distance between two strings int LevensDistStr (const string& s1, const string& s2) { const size_t rows = s1.length(); const size_t cols = s2.length(); vector curr(cols+1, 0); int prev; // Prepopulate 1st column for (size_t j = 0; j <= cols; j++) { curr[j] = j; } // Compute all other elements in distance matrix for (size_t i = 1; i <= rows; i++) { prev = curr[0]; curr[0] = i; for (size_t j = 1; j <= cols; j++) { int temp = curr[j]; if (s1[i - 1] == s2[j - 1]) { curr[j] = prev; } else { curr[j] = 1 + minimum (prev, curr[j - 1], curr[j]); } prev = temp; } } return curr[cols]; } // Function for computing the Levenshtein distance between two documents int LevensDistDoc (const Cell& d1, const Cell& d2) { const size_t rows = d1.numel(); const size_t cols = d2.numel(); vector curr(cols+1, 0); int prev; // Prepopulate 1st column for (size_t j = 0; j <= cols; j++) { curr[j] = j; } // Compute all other elements in distance matrix for (size_t i = 1; i <= rows; i++) { prev = curr[0]; curr[0] = i; for (size_t j = 1; j <= cols; j++) { int temp = curr[j]; if (d1(i - 1).string_value() == d2(i - 1).string_value()) { curr[j] = prev; } else { curr[j] = 1 + minimum (prev, curr[j - 1], curr[j]); } prev = temp; } } return curr[cols]; } // Transform a distance triu matric to a boolean triu matrix boolMatrix double2bool (const Matrix& D, const int& minDist) { const size_t sz = D.rows(); boolMatrix Bmat(sz, sz); for (size_t i = 0; i < sz - 1; i++) { Bmat(i,i) = true; for (size_t j = i + 1; j < sz; j++) { if (D(i,j) <= minDist) { Bmat(i,j) = true; Bmat(j,i) = false; } else { Bmat(i,j) = false; Bmat(j,i) = false; } } } Bmat(sz - 1,sz - 1) = true; return Bmat; } // Transform a distance triu matric to a distance vector Matrix triu2Dvec (const Matrix& D) { const size_t szA = D.rows(); const size_t sz = szA * (szA - 1) / 2; octave_idx_type idx = 0; Matrix Dvec(sz, 1); for (size_t i = 0; i < szA - 1; i++) { for (size_t j = i + 1; j < szA; j++) { Dvec(idx++,0) = D(i,j); } } return Dvec; } // Compute unique indexing IA cell of vectors vector> IAcellvec (const boolMatrix& B) { const size_t rows = B.rows(); const size_t cols = B.columns(); vector> IAcell; for (size_t i = 0; i < rows; i++) { vector IA_cIdx; IA_cIdx.push_back(i); for (size_t j = i + 1; j < cols; j++) { if (B(i,j)) { IA_cIdx.push_back(j); } } IAcell.push_back(IA_cIdx); } return IAcell; } // Transform to IAcellvec to Cell Cell IA2cell (const vector>& IAc, const vector& IAv) { const size_t sz_v = IAv.size(); Cell IA(sz_v, 1); for (size_t i = 0; i < sz_v; i++) { int idx = IAv[i]; const size_t sz_c = IAc[idx].size(); Matrix IAidx(sz_c, 1); for (size_t j = 0; j < sz_c; j++) { IAidx(j,0) = IAc[idx][j] + 1; } IA(i,0) = IAidx; } return IA; } // Compute unique indexing IA vector vector IAvector (const vector>& IAcell) { vector IA; vector IA_done; for (size_t i = 0; i < IAcell.size(); i++) { for (size_t j = 0; j < IAcell[i].size(); j++) { if (binary_search(IA_done.begin(), IA_done.end(), IAcell[i][j])) { break; } else { if (j == 0) { IA.push_back(IAcell[i][j]); } else { IA_done.push_back(IAcell[i][j]); } } } sort (IA_done.begin(), IA_done.end()); } return IA; } // Transform to IAvector to Matrix Matrix IA2mat (const vector& IAv) { const size_t sz_v = IAv.size(); Matrix IA(sz_v, 1); for (size_t i = 0; i < sz_v; i++) { IA(i,0) = IAv[i] + 1; } return IA; } // Compute unique indexing IA vector Matrix ICvector (const vector>& IAc, const size_t& szA) { Matrix IC(szA, 1); vector IC_done; for (size_t i = 0; i < IAc.size(); i++) { for (size_t j = 0; j < IAc[i].size(); j++) { if (binary_search(IC_done.begin(), IC_done.end(), IAc[i][j])) { break; } else { octave_idx_type idx = IAc[i][j]; IC(idx,0) = i + 1; IC_done.push_back(IAc[i][j]); } } } return IC; } // Functionality for uniquetol octave_value_list uniquetol (const int& nargout, const Cell& A, const Matrix& D, const int& minDist, const bool& OutputAllIndices) { octave_value_list retval (nargout); boolMatrix B = double2bool (D, minDist); vector> IAc = IAcellvec (B); vector IAv = IAvector (IAc); // Build cellstr with unique elements Cell C(IAv.size(), 1); if (A.iscellstr()) { for (size_t i = 0; i < IAv.size(); i++) { C(i,0) = A(IAv[i]).string_value(); } } else { for (size_t i = 0; i < IAv.size(); i++) { C(i,0) = A.elem(IAv[i]); } } retval(0) = C; // Build IA vector output if (nargout > 1 && OutputAllIndices) { retval(1) = IA2cell (IAc, IAv); } else if (nargout > 1) { retval(1) = IA2mat (IAv); } // Build IC vector output if (nargout > 2) { retval(2) = ICvector (IAc, A.numel()); } return retval; } // Expand a cell scalar to a cell vector Cell expand (const Cell& IN, const size_t& sz) { //octave_idx_type sz = static_cast(sz_out); Cell OUT(sz, 1); for (size_t i = 0; i < sz; i++) { OUT(i,0) = IN.elem(0); } return OUT; } DEFUN_DLD(editDistance, args, nargout, "-*- texinfo -*-\n\ @deftypefn {statistics} {@var{d} =} editDistance (@var{str})\n\ @deftypefnx {statistics} {@var{d} =} editDistance (@var{doc})\n\ @deftypefnx {statistics} {@var{C} =} editDistance (@dots{}, @var{minDist})\n\ @deftypefnx {statistics} {[@var{C}, @var{IA}, @var{IC}] =} editDistance @\ (@dots{}, @var{minDist})\n\ @deftypefnx {statistics} {[@var{C}, @var{IA}, @var{IC}] =} editDistance @\ (@dots{}, @var{minDist}, @qcode{\"OutputAllIndices\"}, @var{value})\n\ @deftypefnx {statistics} {@var{d} =} editDistance (@var{str1}, @var{str2})\n\ @deftypefnx {statistics} {@var{d} =} editDistance (@var{doc1}, @var{doc2})\n\ \n\ \n\ Compute the edit (Levenshtein) distance between strings or documents. \ \n\n\ @code{@var{d} = editDistance (@var{str})} takes a cell array of character \ vectors and computes the Levenshtein distance between each pair of strings in \ @var{str} as the lowest number of grapheme insertions, deletions, and \ substitutions required to convert string @qcode{@var{str}@{1@}} to string \ @qcode{@var{str}@{2@}}. If @var{str} is a @qcode{cellstr} vector with \ @math{N} elements, the returned distance @var{d} is an @math{(N * (N-1)) / 2)} \ column vector of doubles. If @var{str} is an array (that is @code{all (size \ (str) > 1) = true}), then it is transformed to a column vector as in \ @code{str = str(:)}. @code{editDistance} expects @var{str} to be a column \ vector, if it is row vector, it is transformed to a column vector.\n\n\ \ @code{@var{d} = editDistance (@var{doc})} can also take a cell array \ containing cell arrays of character vectors, in which case each element of \ @var{doc} is regarded as a document, and the character vector in each element \ of the cell string array is regarded a token. @code{editDistance} computes \ the Levenshtein distance between each pair of cell elements in @var{doc} as \ the lowest number of token insertions, deletions, and substitutions required \ to convert document @qcode{@var{doc}@{1@}} to document @qcode{@var{doc}@{2@}}. \ If @var{doc} is a @qcode{cell} vector with @math{N} elements, the distance \ @var{d} is an @math{(N * (N-1)) / 2)} column vector of doubles. If @var{doc} \ is an array (that is @code{all (size (doc) > 1) = true}), then it is converted \ to a column vector as in @code{doc = doc(:)}.\n\n\ \ @code{@var{C} = editDistance (@dots{}, @var{minDist})} specifies a minimum \ distance, @var{minDist}, which is regarded as a similarity threshold between \ each pair of strings or documents, defined in the previous syntaces. In this \ case, @code{editDistance} resembles the functionality of the @code{uniquetol} \ function and returns the unique strings or documents that are similar up to \ @var{minDist} distance. @var{C} is either a cellstring array or a cell array \ of cellstrings, depending on the first input argument.\n\n\ \ @code{[@var{C}, @var{IA}, @var{IC}] = editDistance (@dots{}, @var{minDist})} \ also returns index vectors @var{IA} and @var{IC}. Assuming @var{A} contains \ either strings @var{str} or documents @var{doc} as defined above, @var{IA} \ is a column vector of indices to the first occurrence of similar elements such \ that @qcode{@var{C} = @var{A}(@var{IA})}, and @var{IC} is a column vector of \ indices such that @qcode{@var{A} ~ @var{C}(@var{IC})} where @qcode{~} means \ that the strings or documents are within the specified distance @var{minDist} \ of each other.\n\n\ \ @code{[@var{C}, @var{IA}, @var{IC}] = editDistance (@dots{}, @var{minDist}, \ @qcode{\"OutputAllIndices\"}, @var{value})} specifies the type of the second \ output index @var{IA}. @var{value} must be a logical scalar. When set to \ @code{true}, @var{IA} is a cell array containing the vectors of indices for \ ALL elements in @var{A} that are within the specified distance @var{minDist} \ of each other. Each cell in @var{IA} corresponds to a value in @var{C} and \ the values in each cell correspond to locations in @var{A}. If @var{value} is \ set to @code{false}, then @var{IA} is returned as an index vector described in \ the previous syntax.\n\n\ \ @code{@var{d} = editDistance (@var{str1}, @var{str2})} can also take two \ character vectors, @var{str1} and @var{str2} and compute the Levenshtein \ distance @var{d} as the lowest number of grapheme insertions, deletions, and \ substitutions required to convert @var{str1} to @var{str2}. @var{str1} and \ @var{str2} may also be cellstring arrays, in which case the pairwise distance \ is computed between @qcode{@var{str1}@{n@}} and @qcode{@var{str1}@{n@}}. The \ cellstring arrays must be of the same size or scalars, in which case the \ scalar is expanded to the size of the other cellstring input. The returned \ distance @var{d} is a column vector with the same number of elements as the \ cellstring arrays. If @var{str1} or @var{str2} is an array, then it is \ transformed to a column vector. @code{editDistance} expects both @var{str1} \ and @var{str2} to be a column vectors, if not, they are transformed into \ column vectors.\n\n\ \ @code{@var{d} = editDistance (@var{doc1}, @var{doc2})} can also take two cell \ array containing cell arrays of character vectors, in which case each element \ of @var{doc1} and @var{dos2} is regarded as a document, and the character \ vector in each element of the cell string array is regarded a token. \ @code{editDistance} computes the pairwise Levenshtein distance between the \ of cell elements in @var{doc1} and @var{doc2} as the lowest number of token \ insertions, deletions, and substitutions required to convert document \ @qcode{@var{doc1}@{n@}} to document @qcode{@var{doc1}@{n@}}.\n\n\ @end deftypefn") { int nargin = args.length(); // Add default options bool OutputAllIndices = false; // Parse Name-Value paired arguments if (nargin > 2 && args(nargin-2).is_string()) { string ParamName = "OutputAllIndices"; if (args(nargin - 2).string_value() == ParamName) { const int idx = nargin - 1; if (args(idx).islogical() && args(idx).numel() == 1) { const boolMatrix tmp = args(idx).bool_matrix_value(); OutputAllIndices = tmp(0); } else { error ("editDistance: value for OutputAllIndices " "must be a logical scalar."); } nargin--; nargin--; } } // Check for invalid number of input arguments if (nargin > 3) { error ("editDistance: too many input arguments."); } // Check for last argument being numeric (minDist) int minDist; bool doMinDist; if (nargin > 1 && args(nargin-1).isnumeric()) { // Check minDist input argument if (args(nargin -1 ).numel() != 1) { error ("editDistance: minDist must be a scalar value."); } Matrix tmp = args(nargin - 1).matrix_value(); if (tmp(0,0) < 0 || floor (tmp(0,0)) != tmp(0,0)) { error ("editDistance: minDist must be a nonnegative integer."); } minDist = static_cast(tmp(0,0)); doMinDist = true; nargin--; } else { doMinDist = false; } // Check for invalid number of output arguments if ((nargout > 3 && doMinDist) || (nargout > 1 && ! doMinDist)) { error ("editDistance: too many output arguments."); } // Check cases of string arguments octave_value_list retval (nargout); if (nargin == 1) { if (args(0).iscellstr()) { // Get cellstr input argument const Cell strA = args(0).cellstr_value(); size_t szA = strA.numel(); // For scalar input return distance to itself, i.e. 0 if (szA == 1) { retval(0) = double (0); return retval; } // Compute the edit distance Matrix D(szA, szA); #pragma omp parallel { #pragma omp parallel for for (size_t i = 0; i < szA - 1; i++) { D(i,i) = 0; string s1 = strA(i).string_value(); for (size_t j = i + 1; j < szA; j++) { D(i,j) = LevensDistStr (s1, strA(j).string_value()); } } D(szA - 1,szA - 1) = 0; } // If minDist is given, change functionality from 'pdist' to 'uniquetol' if (doMinDist) { retval = uniquetol (nargout, strA, D, minDist, OutputAllIndices); } else { // Transform to distance vector retval(0) = triu2Dvec (D); } return retval; } else if (args(0).iscell()) { // Get cell input argument const Cell docA = args(0).cell_value(); size_t szA = docA.numel(); // Check that all cell elements contain cellstring arrays for (size_t i = 0; i < szA; i++) { Cell tmp = docA.elem(i); if (! tmp.iscellstr()) { error ("editDistance: tokenizedDocument " "must contain cellstr arrays."); } } // For scalar input return distance to itself, i.e. 0 if (szA == 1) { retval(0) = double (0); return retval; } // Compute the edit distance Matrix D(szA, szA); #pragma omp parallel { #pragma omp parallel for for (size_t i = 0; i < szA - 1; i++) { D(i,i) = 0; Cell d1 = docA.elem(i); for (size_t j = i + 1; j < szA; j++) { D(i,j) = LevensDistDoc (d1, docA.elem(j)); } } D(szA - 1,szA - 1) = 0; } // If minDist is given, change functionality from 'pdist' to 'uniquetol' if (doMinDist) { retval = uniquetol (nargout, docA, D, minDist, OutputAllIndices); } else { // Transform to distance vector retval(0) = triu2Dvec (D); } return retval; } else { error ("editDistance: STR1 must be a cellstr."); } } else if (nargin == 2) { if (args(0).iscellstr() && args(1).iscellstr()) { // Get cellstr input arguments Cell strA = args(0).cellstr_value(); Cell strB = args(1).cellstr_value(); // Check cellstr sizes match size_t szA = strA.numel(); size_t szB = strB.numel(); if (szA != 1 && szB != 1 && szA != szB) { error ("editDistance: cellstr input arguments size mismatch."); } // Preallocate the distance vector and expand as necessary size_t sz = szA; if (szA == 1 && szB != 1) { sz = szB; strA = expand (strA, sz); } else if (szA != 1 && szB == 1) { strB = expand (strB, sz); } Matrix D(sz, 1); // Compute the distance vector for (size_t i = 0; i < sz; i++) { D(i,0) = LevensDistStr (strA(i).string_value(), strB(i).string_value()); } retval(0) = D; } else if (args(0).iscell() && args(1).iscell()) { // Get cell input arguments Cell docA = args(0).cell_value(); Cell docB = args(1).cell_value(); // Check cell sizes match size_t szA = docA.numel(); size_t szB = docB.numel(); if (szA != 1 && szB != 1 && szA != szB) { error ("editDistance: cellstr input arguments size mismatch."); } // Check both cell arrays contain cellstring arrays for (size_t i = 0; i < szA; i++) { Cell tmp = docA.elem(i); if (! tmp.iscellstr()) { error ("editDistance: first tokenizedDocument " "does not contain cellstr arrays."); } } for (size_t i = 0; i < szB; i++) { Cell tmp = docB.elem(i); if (! tmp.iscellstr()) { error ("editDistance: second tokenizedDocument " "does not contain cellstr arrays."); } } // Preallocate the distance vector and expand as necessary int sz = szA; if (szA == 1 && szB != 1) { sz = szB; docA = expand (docA, sz); } else if (szA != 1 && szB == 1) { docB = expand (docB, sz); } Matrix D(sz, 1); // Compute the distance vector for (size_t i = 0; i < sz; i++) { D(i,0) = LevensDistDoc (docA.elem(i), docB.elem(i)); } retval(0) = D; } else if (args(0).is_string() && args(1).is_string()) { retval(0) = LevensDistStr (args(0).string_value(),args(1).string_value()); } else { error ("editDistance: STR1 and STR2 must be either strings or cellstr."); } } return retval; } /* %!error d = editDistance (1, 2, 3, 4); %!error ... %! [C, IA, IC, I] = editDistance ({"AS","SD","AD"}, 1); %!error ... %! [C, IA] = editDistance ({"AS","SD","AD"}); %!error ... %! d = editDistance ({"AS","SD","AD"}, [1, 2]); %!error ... %! d = editDistance ({"AS","SD","AD"}, -2); %!error ... %! d = editDistance ({"AS","SD","AD"}, 1.25); %!error ... %! d = editDistance ({"AS","SD","AD"}, {"AS","SD","AD"}, [1, 2]); %!error ... %! d = editDistance ({"AS","SD","AD"}, {"AS","SD","AD"}, -2); %!error ... %! d = editDistance ({"AS","SD","AD"}, {"AS","SD","AD"}, 1.25); %!error ... %! d = editDistance ("string1", "string2", [1, 2]); %!error ... %! d = editDistance ("string1", "string2", -2); %!error ... %! d = editDistance ("string1", "string2", 1.25); %!error ... %! d = editDistance ({{"string1", "string2"}, 2}); %!error ... %! d = editDistance ({{"string1", "string2"}, 2}, 2); %!error ... %! d = editDistance ([1, 2, 3]); %!error ... %! d = editDistance (["AS","SD","AD","AS"]); %!error ... %! d = editDistance (["AS","SD","AD"], 2); %!error ... %! d = editDistance (logical ([1,2,3]), {"AS","AS","AD"}); %!error ... %! d = editDistance ({"AS","SD","AD"}, logical ([1,2,3])); %!error ... %! d = editDistance ([1,2,3], {"AS","AS","AD"}); %!error ... %! d = editDistance ({1,2,3}, {"AS","SD","AD"}); %!error ... %! d = editDistance ({"AS","SD","AD"}, {1,2,3}); %!error ... %! d = editDistance ({"AS","SD","AD"}, {"AS", "AS"}); %!test %! d = editDistance ({"AS","SD","AD"}); %! assert (d, [2; 1; 1]); %! assert (class (d), "double"); %!test %! C = editDistance ({"AS","SD","AD"}, 1); %! assert (iscellstr (C), true); %! assert (C, {"AS";"SD"}); %!test %! [C, IA] = editDistance ({"AS","SD","AD"}, 1); %! assert (class (IA), "double"); %! assert (IA, [1;2]); %!test %! A = {"ASS"; "SDS"; "FDE"; "EDS"; "OPA"}; %! [C, IA] = editDistance (A, 2, "OutputAllIndices", false); %! assert (class (IA), "double"); %! assert (A(IA), C); %!test %! A = {"ASS"; "SDS"; "FDE"; "EDS"; "OPA"}; %! [C, IA] = editDistance (A, 2, "OutputAllIndices", true); %! assert (class (IA), "cell"); %! assert (C, {"ASS"; "FDE"; "OPA"}); %! assert (A(IA{1}), {"ASS"; "SDS"; "EDS"}); %! assert (A(IA{2}), {"FDE"; "EDS"}); %! assert (A(IA{3}), {"OPA"}); %!test %! A = {"ASS"; "SDS"; "FDE"; "EDS"; "OPA"}; %! [C, IA, IC] = editDistance (A, 2); %! assert (class (IA), "double"); %! assert (A(IA), C); %! assert (IC, [1; 1; 3; 1; 5]); %!test %! d = editDistance ({"AS","SD","AD"}, {"AS", "AD", "SE"}); %! assert (d, [0; 1; 2]); %! assert (class (d), "double"); %!test %! d = editDistance ({"AS","SD","AD"}, {"AS"}); %! assert (d, [0; 2; 1]); %! assert (class (d), "double"); %!test %! d = editDistance ({"AS"}, {"AS","SD","AD"}); %! assert (d, [0; 2; 1]); %! assert (class (d), "double"); %!test %! b = editDistance ("Octave", "octave"); %! assert (b, 1); %! assert (class (b), "double"); */ statistics-release-1.7.3/src/fcnn.cpp000066400000000000000000000411311475240274700175650ustar00rootroot00000000000000/* Copyright (C) 2024 Andreas Bertsatos This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #if defined (_OPENMP) #include #define MY_OMP_SET_THREADS (omp_set_num_threads (this->n_threads)) #else #define MY_OMP_SET_THREADS #endif using namespace std; // Helper functions // Return random number between -1 and 1 double get_random () { // return random number between -1 and 1 return (rand () / (double) RAND_MAX) * 2 - 1; } // Compute accuracy of predicted samples during training double accuracy (vector predictions, vector labels) { double correct = 0.0; for (int i = 0; i < predictions.size (); i++) { if (predictions[i] == labels[i] - 1) { correct += 1.0; } } double accuracy = correct / (double) predictions.size (); return accuracy; } // Class definitions class Neuron { public: // constructor Neuron (int input_size); // destructor ~Neuron (); // methods double forward (vector inputs); void backward (vector last_input, double grad); void descend (double learning_rate); vector get_neuron (); void set_neuron (vector Wb_vector); void zero_gradient (); // data vector weights; vector wgrad; double bias; double bgrad; }; Neuron::Neuron (int input_size) { this->weights = vector (input_size); this->bias = 0.01 * get_random (); for (int i = 0; i < input_size; i++) { this->weights[i] = get_random (); } } Neuron::~Neuron () {} double Neuron::forward (vector inputs) { double total = this->bias; for (int i = 0; i < inputs.size (); i++) { total += inputs[i] * this->weights[i]; } return total; } void Neuron::backward (vector last_input, double grad) { this->bgrad += grad; for (int i = 0; i < this->wgrad.size (); i++) { this->wgrad.at (i) = this->wgrad.at (i) + grad * last_input.at (i); } } void Neuron::descend (double learning_rate) { this->bias -= this->bgrad * learning_rate; for (int i = 0; i < this->weights.size (); i++) { this->weights.at (i) -= this->wgrad.at (i) * learning_rate; } } vector Neuron::get_neuron () { vector Wb_vector = this->weights; Wb_vector.push_back (this->bias); return Wb_vector; } void Neuron::set_neuron (vector Wb_vector) { int w_len = Wb_vector.size () - 1; for (int i = 0; i < w_len; i++) { this->weights.at (i) = Wb_vector[i]; } this->bias = Wb_vector[w_len]; } void Neuron::zero_gradient () { this->wgrad = vector (this->weights.size ()); this->bgrad = 0.0; } class DenseLayer { public: // constructor DenseLayer (int input_size, int output_size); // destructor ~DenseLayer (); // methods vector forward (vector inputs); void backward (vector grad); void descend (double learning_rate); vector> get_layer (); void set_layer (vector> Wb_matrix); void zero_gradient (); // data vector neurons; vector last_input; }; DenseLayer::DenseLayer (int input_size, int output_size) { // initialize neurons this->neurons = vector (); for (int i = 0; i < output_size; i++) { Neuron to_add = Neuron (input_size); this->neurons.push_back (to_add); } } DenseLayer::~DenseLayer () {} vector DenseLayer::forward (vector inputs) { this->last_input = inputs; vector outputs = vector (this->neurons.size()); for (int i = 0; i < this->neurons.size (); i++) { outputs[i] = this->neurons[i].forward (inputs); } return outputs; } void DenseLayer::backward (vector grad) { for (int i = 0; i < this->neurons.size (); i++) { this->neurons[i].backward (last_input, grad[i]); } } void DenseLayer::descend (double learning_rate) { for (int i = 0; i < this->neurons.size (); i++) { this->neurons[i].descend (learning_rate); } } vector> DenseLayer::get_layer () { vector> Wb_matrix; for (int i = 0; i < this->neurons.size (); i++) { vector WB_vector = this->neurons[i].get_neuron (); Wb_matrix.push_back (WB_vector); } return Wb_matrix; } void DenseLayer::set_layer (vector> Wb_matrix) { for (int i = 0; i < Wb_matrix.size (); i++) { this->neurons[i].set_neuron (Wb_matrix[i]); } } void DenseLayer::zero_gradient () { for (int i = 0; i < this->neurons.size (); i++) { this->neurons[i].zero_gradient (); } } class ActivationLayer { public: // constructor ActivationLayer (int activation, int n_threads, double alpha); // destructor ~ActivationLayer (); // methods vector forward (vector inputs); void backward (vector grad); void backward (DenseLayer &prev_layer); // data vector last_input; vector grad; vector last_output; private: int activation; int n_threads; double alpha; }; ActivationLayer::ActivationLayer (int activation, int n_threads, double alpha) { this->activation = activation; this->n_threads = n_threads; this->alpha = alpha; } ActivationLayer::~ActivationLayer () {} vector ActivationLayer::forward (vector inputs) { this->last_input = inputs; int layer_size = inputs.size (); if (layer_size < 1000) { this->n_threads = 1; } vector outputs = vector (layer_size); if (this->activation == 0) // 'Linear' { outputs = inputs; } else if (this->activation == 1) // Sigmoid function { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { outputs[i] = 1 / (1 + exp (-inputs[i])); } } } else if (this->activation == 2) // Rectified Linear Unit (ReLU) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { outputs[i] = inputs[i] > 0 ? inputs[i] : 0; } } } else if (this->activation == 3) // Hyperbolic tangent (tanh) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { double ex = exp (inputs[i]); double e_x = exp (-inputs[i]); outputs[i] = (ex - e_x) / (ex + e_x); } } } else if (this->activation == 4) // Softmax activation { double total = 0.0; double maxel = *max_element (inputs.begin (), inputs.end ()); for (int i = 0; i < layer_size; i++) { outputs[i] = exp (inputs[i] - maxel); total += outputs[i]; } for (int i = 0; i < layer_size; i++) { outputs[i] /= total; } } else if (this->activation == 5) // Parametric or Leaky ReLU { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { outputs[i] = inputs[i] >= 0 ? inputs[i] : inputs[i] * this->alpha; } } } else if (this->activation == 6) // Exponential Linear Unit (ELU) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { outputs[i] = inputs[i] >= 0 ? inputs[i] : (exp (inputs[i]) - 1) * this->alpha; } } } else if (this->activation == 7) // Gaussian Error Linear Unit (GELU) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { double x_3 = pow (inputs[i], 3) * 0.044715; outputs[i] = 0.5 * inputs[i] * (tanh (sqrt (2 / M_PI) * (inputs[i] + x_3))); } } } this->last_output = outputs; return outputs; } void ActivationLayer::backward (vector chain_grad) { int layer_size = this->last_input.size (); if (layer_size < 1000) { this->n_threads = 1; } this->grad = vector (layer_size); if (this->activation == 0) // 'Linear' { this->grad = chain_grad; } else if (this->activation == 1) // Sigmoid function { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { this->grad[i] = this->last_output[i] * (1 - this->last_output[i]) * chain_grad[i]; } } } else if (this->activation == 2) // Rectified Linear Unit (ReLU) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { this->grad[i] = this->last_input[i] > 0 ? chain_grad[i] : 0; } } } else if (this->activation == 3) // Hyperbolic tangent (tanh) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { this->grad[i] = (1 - pow (this->last_output[i], 2)) * chain_grad[i]; } } } else if (this->activation == 4) // Softmax activation { // WARNING: this code may be incorrect this->grad = chain_grad; } else if (this->activation == 5) // Parametric or Leaky ReLU { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { this->grad[i] = this->last_input[i] >= 0 ? chain_grad[i] : chain_grad[i] * this->alpha; } } } else if (this->activation == 6) // Exponential Linear Unit (ELU) { MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { this->grad[i] = this->last_input[i] >= 0 ? chain_grad[i] : chain_grad[i] * exp (this->last_output[i]) * this->alpha; } } } else if (this->activation == 7) // Gaussian Error Linear Unit (GELU) { // WARNING: this code may be incorrect static const double inv_sqrt_2pi = 0.3989422804014327; MY_OMP_SET_THREADS; #pragma omp parallel { #pragma omp parallel for for (int i = 0; i < layer_size; i++) { double pdf_val = inv_sqrt_2pi * exp (-0.5 * this->last_output[i] * this->last_output[i]); this->grad[i] = this->last_output[i] * pdf_val * chain_grad[i]; } } } } void ActivationLayer::backward (DenseLayer &prev_layer) { this->grad = vector (this->last_input.size ()); if (this->activation == 0) // 'Linear' { for (int i = 0; i < prev_layer.last_input.size (); i++) { for (int n = 0; n < prev_layer.neurons.size (); n++) { double curr_grad = this->last_output[n]; double chain_grad = prev_layer.neurons[n].weights[i] * prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += curr_grad * chain_grad; } } } else if (this->activation == 1) // Sigmoid function { for (int i = 0; i < prev_layer.last_input.size (); i++) { for (int n = 0; n < prev_layer.neurons.size (); n++) { double curr_grad = this->last_output[n] * (1 - this->last_output[n]); double chain_grad = prev_layer.neurons[n].weights[i] * prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += curr_grad * chain_grad; } } } else if (this->activation == 2) // Rectified Linear Unit (ReLU) { for (int i = 0; i < prev_layer.last_input.size(); i++) { for (int n = 0; n < prev_layer.neurons.size(); n++) { double grad = prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; if (this->last_input[i] < 0) { this->grad[i] = 0; } else { this->grad[i] += prev_layer.neurons[n].weights[i] * grad; } } } } else if (this->activation == 3) // Hyperbolic tangent (tanh) { for (int i = 0; i < prev_layer.last_input.size (); i++) { for (int n = 0; n < prev_layer.neurons.size (); n++) { double curr_grad = this->last_output[n] * (1 - pow (this->last_output[i], 2)); double chain_grad = prev_layer.neurons[n].weights[i] * prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += curr_grad * chain_grad; } } } else if (this->activation == 4) // Softmax activation { // WARNING: this code may be incorrect for (int i = 0; i < prev_layer.last_input.size (); i++) { for (int n = 0; n < prev_layer.neurons.size (); n++) { double curr_grad = this->last_output[n]; double chain_grad = prev_layer.neurons[n].weights[i] * prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += curr_grad * chain_grad; } } } else if (this->activation == 5) // Parametric or Leaky ReLU { for (int i = 0; i < prev_layer.last_input.size(); i++) { for (int n = 0; n < prev_layer.neurons.size(); n++) { double grad = prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += prev_layer.neurons[n].weights[i] * grad; } } for (int i = 0; i < this->last_input.size(); i++) { this->grad[i] = this->last_input[i] >= 0 ? this->grad[i] : this->grad[i] * this->alpha; } } else if (this->activation == 6) // Exponential Linear Unit (ELU) { for (int i = 0; i < prev_layer.last_input.size(); i++) { for (int n = 0; n < prev_layer.neurons.size(); n++) { double grad = prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += prev_layer.neurons[n].weights[i] * grad; } } for (int i = 0; i < this->last_input.size(); i++) { this->grad[i] = this->last_input[i] >= 0 ? this->grad[i] : this->grad[i] * exp (this->last_output[i]) * this->alpha; } } else if (this->activation == 7) // Gaussian Error Linear Unit (GELU) { // WARNING: this code may be incorrect static const double inv_sqrt_2pi = 0.3989422804014327; for (int i = 0; i < prev_layer.last_input.size (); i++) { for (int n = 0; n < prev_layer.neurons.size (); n++) { double pdf_val = inv_sqrt_2pi * exp (-0.5 * this->last_output[i] * this->last_output[i]); double curr_grad = this->last_output[n] * pdf_val * this->last_output[i]; double chain_grad = prev_layer.neurons[n].weights[i] * prev_layer.neurons[n].wgrad[i] / prev_layer.last_input[i]; this->grad[i] += curr_grad * chain_grad; } } } } class MeanSquaredErrorLoss { public: MeanSquaredErrorLoss (); ~MeanSquaredErrorLoss (); double forward (vector inputs, vector targets); void backward (double grad); // data vector last_input; vector last_target; vector grad; }; MeanSquaredErrorLoss::MeanSquaredErrorLoss () {} MeanSquaredErrorLoss::~MeanSquaredErrorLoss () {} double MeanSquaredErrorLoss::forward (vector inputs, vector targets) { // we only need to calculate the loss for the target class this->last_input = inputs; this->last_target = targets; double total = 0; for (int i = 0; i < inputs.size (); i++) { total += pow (inputs[i] - targets[i], 2); } double loss = total; return loss; } void MeanSquaredErrorLoss::backward (double grad) { this->grad = vector (this->last_input.size ()); for (int i = 0; i < this->last_input.size (); i++) { this->grad.at(i) = 2 * this->last_input[i] - this->last_target[i]; this->grad.at(i) *= grad; } } statistics-release-1.7.3/src/fcnnpredict.cc000066400000000000000000000250571475240274700207540ustar00rootroot00000000000000/* Copyright (C) 2024 Andreas Bertsatos This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include "fcnn.cpp" using namespace std; DEFUN_DLD(fcnnpredict, args, nargout, "-*- texinfo -*-\n\ @deftypefn {statistics} {@var{pred_Y} =} fcnnpredict (@var{Mdl}, @var{XC})\n\ @deftypefnx {statistics} {@var{pred_Y} =} fcnnpredict @\ (@var{Mdl}, @var{XC}, @var{NumThreads})\n\ @deftypefnx {statistics} {[@var{pred_Y}, @var{scores}] =} fcnnpredict (@dots{})\n\ \n\ \n\ Make predictions from a fully connected Neural Network. \n\ \n\n\ \n\n\ @code{@var{pred_Y} = fcnnpredict (@var{Mdl}, @var{XC})} requires the following \ input arguments.\ \n\n\ @itemize \n\ @item @var{Mdl} : A structure containing the trained model parameters as \ generated by the @code{fcnntrain} function. \ \n\ \n\ @item @var{XC} : An @math{NxM} matrix containing the data set to be predicted \ upon. Rows @math{N} correspond to individual samples and columns @math{M} \ correspond to features (dimensions). Type of @var{XC} must be double and the \ number of features must correspond to those of the trained model. \n\ @end itemize \n\ @code{fcnnpredict} can also be called with a third input argument, in which \ case, @var{NumThreads}, a positive scalar integer value, defines the number of \ of threads to be used when computing the activation layers. For layers with \ less than 1000 neurons, @var{NumThreads} always defaults to 1. \ \n\ @code{fcnnpredict} returns the predicted labels, @var{pred_Y}, and if a second \ output argument is requested, it also returns the corresponding values of the \ neural networks output in @var{scores}. \ \n\ \n\ Installation Note: in order to support parallel processing on MacOS, users \ have to manually add support for OpenMP by adding the following flags to \ @qcode{CFLAGS} and @qcode{CXXFLAGS} prior to installing the statistics \ package:\n\n\ @code{setenv (\"CPPFLAGS\", \"-I/opt/homebrew/opt/libomp/include -Xclang -fopenmp\")} \ \n\ \n\ @seealso{fcnntrain, fitcnet, ClassificationNeuralNetwork} \n\ @end deftypefn") { // Check for correct number of input/output arguments if (args.length () < 2) { error ("fcnnpredict: too few input arguments."); } if (nargout > 2) { error ("fcnnpredict: too many output arguments."); } // Do some input validation while loading the trained model if (! (args(0).isstruct () && args(0).numel () == 1)) { error ("fcnnpredict: first argument must be a scalar structure."); } octave_scalar_map fcnn_model = args(0).scalar_map_value (); if (! fcnn_model.isfield ("LayerWeights")) { error ("fcnnpredict: model does not have a 'LayerWeights' field."); } if (! fcnn_model.getfield("LayerWeights").iscell () || ! (fcnn_model.getfield("LayerWeights").rows () == 1 && fcnn_model.getfield("LayerWeights").columns () > 1)) { error ("fcnnpredict: 'LayerWeights' must be a cell row vector."); } Cell LayerWeights = fcnn_model.getfield("LayerWeights").cell_value(); if (! fcnn_model.isfield ("Activations")) { error ("fcnnpredict: model does not have an 'Activations' field."); } if (! fcnn_model.getfield("Activations").isnumeric () || ! (fcnn_model.getfield("Activations").rows () == 1 && fcnn_model.getfield("Activations").columns () > 1)) { error ("fcnnpredict: 'Activations' must be a numeric row vector."); } RowVector ActiveCode = fcnn_model.getfield("Activations").row_vector_value(); if (! fcnn_model.isfield ("Alpha")) { error ("fcnnpredict: model does not have an 'Alpha' field."); } double Alpha = fcnn_model.getfield("Alpha").scalar_value(); // Do some input validation while loading the testing data if (! args(1).isnumeric () || args(1).iscomplex ()) { error ("fcnnpredict: XC must be a real numeric matrix."); } if (args(1).isempty ()) { error ("fcnnpredict: XC cannot be empty."); } if (args(1).columns () != LayerWeights.elem(0).columns () - 1) { error ("fcnnpredict: the features in XC do not match the trained model."); } Matrix X = args(1).matrix_value (); int n = args(1).rows (); int d = args(1).columns (); // Check for optional third input argument to set number of threads int NumThreads = 1; if (args.length () == 3) { if (! args(2).is_scalar_type () || ! args(2).isnumeric () || args(2).scalar_value () < 1 || args(2).iscomplex ()) { error ("fcnnpredict: NumThreads must be a positive integer scalar value."); } NumThreads = args(2).scalar_value (); } // Construct 2D vector from data in XC vector> data (n, vector(d, 0)); for (int i = 0; i < n; i++) { for (int j = 0; j < d; j++) { data[i][j] = X(i,j); } } // Create a vector of layers sized appropriately vector WeightBias; vector Activation; int numlayers = LayerWeights.numel (); int input_size = d; int output_size; for (int i = 0; i < numlayers; i++) { Matrix WB = LayerWeights.elem(i).matrix_value (); output_size = (int) WB.rows (); // Construct 2D vector with Weights-Biases vector> Wb_matrix; for (int r = 0; r < WB.rows (); r++) { vector WB_vector; for (int c = 0; c < WB.columns (); c++) { WB_vector.push_back (WB(r,c)); } Wb_matrix.push_back (WB_vector); } // Create dense layer and set its values DenseLayer DL = DenseLayer (input_size, output_size); DL.set_layer (Wb_matrix); // Create activation layer ActivationLayer AL = ActivationLayer (ActiveCode(i), NumThreads, Alpha); WeightBias.push_back (DL); Activation.push_back (AL); input_size = output_size; } // Initialize Prediction and Score vector predictions = vector (); vector> scores; // Go through all testing samples for (int sample_idx = 0; sample_idx < n; sample_idx++) { vector sample = data[sample_idx]; // Forward pass for (int layer_idx = 0; layer_idx < numlayers; layer_idx++) { sample = WeightBias[layer_idx].forward (sample); sample = Activation[layer_idx].forward (sample); } // Save scores scores.push_back (sample); // Get the prediction for this sample and store it to vector int prediction = 0; // Search for highest value for (int j = 0; j < output_size; j++) { if (sample[j] > sample[prediction]) { prediction = j; } } predictions.push_back (prediction + 1); } // Store predicted labels in ColumnVector ColumnVector Y_pred(n); for (int sample_idx = 0; sample_idx < n; sample_idx++) { Y_pred(sample_idx) = predictions[sample_idx]; } // Store predicted scores in Matrix Matrix Y_scores(n,output_size); for (int r = 0; r < n; r++) { for (int c = 0; c < output_size; c++) { Y_scores(r,c) = scores[r][c]; } } // Prepare returning arguments octave_value_list retval (nargout); retval(0) = Y_pred; if (nargout > 0) { retval(1) = Y_scores; } return retval; } /* %!shared X, Y, MODEL %! load fisheriris %! X = meas; %! Y = grp2idx (species); %! MODEL = fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, 100, false); %!test %! [Y_pred, Y_scores] = fcnnpredict (MODEL, X); %! assert (numel (Y_pred), numel (Y)); %! assert (isequal (size (Y_pred), size (Y)), true); %! assert (columns (Y_scores), numel (unique (Y))); %! assert (rows (Y_scores), numel (Y)); %!error ... %! fcnnpredict (MODEL); %!error ... %! [Q, W, E] = fcnnpredict (MODEL, X); %!error ... %! fcnnpredict (1, X); %!error ... %! fcnnpredict (struct ("L", {1, 2, 3}), X); %!error ... %! fcnnpredict (struct ("L", 1), X); %!error ... %! fcnnpredict (struct ("LayerWeights", 1), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {1}), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {{1; 2; 3}}), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {[{ones(3)},{ones(3)}]}, "R", 2), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {[{ones(3)},{ones(3)}]}, ... %! "Activations", [2]), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {[{ones(3)},{ones(3)}]}, ... %! "Activations", [2; 2]), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {[{ones(3)},{ones(3)}]}, ... %! "Activations", {{2, 2}}), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {[{ones(3)},{ones(3)}]}, ... %! "Activations", {{"sigmoid", "softmax"}}), X); %!error ... %! fcnnpredict (struct ("LayerWeights", {[{ones(3)},{ones(3)}]}, ... %! "Activations", "sigmoid"), X); %!error ... %! fcnnpredict (MODEL, complex (X)); %!error ... %! fcnnpredict (MODEL, {1, 2, 3, 4}); %!error ... %! fcnnpredict (MODEL, "asd"); %!error ... %! fcnnpredict (MODEL, []); %!error ... %! fcnnpredict (MODEL, X(:,[1:3])); */ statistics-release-1.7.3/src/fcnntrain.cc000066400000000000000000000423771475240274700204430ustar00rootroot00000000000000/* Copyright (C) 2024 Andreas Bertsatos This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include "fcnn.cpp" using namespace std; DEFUN_DLD(fcnntrain, args, nargout, "-*- texinfo -*-\n\ @deftypefn {statistics} {@var{Mdl} =} fcnntrain (@var{X}, @var{Y}, @\ @var{LayerSizes}, @var{Activations}, @var{NumThreads}, @var{Alpha}, @\ @var{LearningRate}, @var{Epochs}, @var{DisplayInfo})\n\ \n\ \n\ Train a fully connected Neural Network. \n\ \n\n\ @code{@var{Mdl} = fcnntrain (@dots{})} requires the following input arguments.\ \n\n\ @itemize \n\ @item @var{X} : An @math{NxM} matrix containing the data set to be trained \ upon. Rows @math{N} correspond to individual samples and columns @math{M} \ correspond to features (dimensions). Type of @var{X} must be double. \ \n\ \n\ @item @var{Y} : An @math{Nx1} column vector containing the labels of the \ training dataset. The labels must be natural numbers (positive integers) \ starting from 1 up to the number of classes, similarily as returned by the \ `grp2idx` function. Type of @var{Y} must be double. \ \n\ \n\ @item @var{LayerSizes} : A numeric row vector of integer values defining the \ size of the hidden layers of the network. Input and output layers are \ automatically determined by the training data and their labels. \ \n\ \n\ @item @var{Activations} : A numeric row vector of integer values defining the \ activation functions to be used at each layer including the output layer. The \ corresponding codes to activation functions is: \n\ @itemize \n\ @item @code{0} : @qcode{'Linear'} \n\ @item @code{1} : @qcode{'Sigmoid'} \n\ @item @code{2} : @qcode{'Rectified Linear Unit (ReLU)'} \n\ @item @code{3} : @qcode{'Hyperbolic tangent (tanh)'} \n\ @item @code{4} : @qcode{'Softmax'} \n\ @item @code{5} : @qcode{'Parametric or Leaky ReLU'} \n\ @item @code{6} : @qcode{'Exponential Linear Unit (ELU)'} \n\ @item @code{7} : @qcode{'Gaussian Error Linear Unit (GELU)'} \n\ @end itemize \n\ \n\ \n\ @item @var{NumThreads} : A positive scalar integer value defining the number \ of threads used for computing the activation layers. For layers with less \ than 1000 neurons, @var{NumThreads} always defaults to 1. \ \n\ \n\ @item @var{Alpha} : A positive scalar value defining the parameter \ @qcode{alpha} used in @qcode{ReLU} and @qcode{ELU} activation layers. \ \n\ \n\ @item @var{LearningRate} : A positive scalar value defining the learning rate \ used by the gradient descend algorithm during training. \ \n\ \n\ @item @var{Epochs} : A positive scalar value defining the number of epochs for \ training the model. \ \n\ \n\ @item @var{DisplayInfo} : A boolean scalar indicating whether to print \ information during training. \n\ @end itemize \n\ \n\ \n\ @code{fcnntrain} returns the trained model, @var{Mdl}, as a structure \ containing the following fields: \ \n\ \n\ @itemize \n\ @item @code{LayerWeights} : A cell array with each element containing a matrix \ with the Weights and Biases of each layer including the output layer.\n\ \n\ \n\ @item @code{Activations} : A numeric row vector of integer values defining the \ activation functions to be used at each layer including the output layer. \ \n\ \n\ @item @code{Accuracy} : The prediction accuracy at each iteration during the \ neural network model's training process. \ \n\ \n\ @item @code{Loss} : The loss value recorded at each iteration during the \ neural network model's training process. \ \n\ \n\ @item @code{Alpha} : The value of the Alpha parameter used in @qcode{ReLU} and \ @qcode{ELU} activation layers. \ \n\ \n\ @end itemize \ \n\ \n\ Installation Note: in order to support parallel processing on MacOS, users \ have to manually add support for OpenMP by adding the following flags to \ @qcode{CFLAGS} and @qcode{CXXFLAGS} prior to installing the statistics \ package:\n\n\ @code{setenv (\"CPPFLAGS\", \"-I/opt/homebrew/opt/libomp/include -Xclang -fopenmp\")} \ \n\ \n\ @seealso{fcnnpredict, fitcnet, ClassificationNeuralNetwork} \n\ @end deftypefn") { // Check for correct number of input/output arguments if (args.length () < 9) { error ("fcnntrain: too few input arguments."); } if (nargout > 1) { error ("fcnntrain: too many output arguments."); } // int seed = time(NULL); int seed = 0; srand (seed); // Do some input validation while loading trainind data and labels if (! args(0).isnumeric () || args(0).iscomplex ()) { error ("fcnntrain: X must be a real numeric matrix."); } if (args(0).isempty ()) { error ("fcnntrain: X cannot be empty."); } if (! args(1).isnumeric () || args(1).iscomplex ()) { error ("fcnntrain: Y must be a real numeric matrix."); } if (args(1).isempty ()) { error ("fcnntrain: Y cannot be empty."); } if (args(0).rows () != args(1).rows ()) { error ("fcnntrain: X and Y must have the same number of rows."); } // Construct 2D vector from data in X int n = args(0).rows (); int d = args(0).columns (); vector> data (n, vector(d, 0)); Matrix X = args(0).matrix_value (); for (int i = 0; i < n; i++) { for (int j = 0; j < d; j++) { data[i][j] = X(i,j); } } // Construct 1D vector from labels in Y vector labels(n, 0); ColumnVector Y = args(1).column_vector_value (); for (int i = 0; i < n; i++) { labels[i] = Y(i); if (labels[i] < 1) { error ("fcnntrain: labels in Y must be positive integers."); } } // Check LayerSizes and Activations input arguments if (! args(2).isnumeric () || args(2).iscomplex () || args(2).isempty () || args(2).rows () != 1) { error ("fcnntrain: 'LayerSizes' must be a row vector of integer values."); } if (! args(3).isnumeric () || args(3).iscomplex () || args(3).isempty () || args(3).rows () != 1 || args(3).columns () < 2) { error ("fcnntrain: 'Activations' must be a row vector of integer values."); } if (args(2).numel () != args(3).numel () - 1) { error ("fcnntrain: 'Activations' do not match LayerSizes."); } // Check NumThreads and Alpha input arguments if (! args(4).is_scalar_type () || ! args(4).isnumeric () || args(4).scalar_value () < 1 || args(4).iscomplex ()) { error ("fcnntrain: 'NumThreads' must be a positive integer scalar value."); } int NumThreads = args(4).scalar_value (); if (! args(5).is_scalar_type () || ! args(5).isnumeric () || args(5).scalar_value () <= 0 || args(5).iscomplex ()) { error ("fcnntrain: 'Alpha' must be a positive scalar value."); } double Alpha = args(5).scalar_value (); // Create a vector of layers sized appropriately vector WeightBias; vector Activation; RowVector LayerSizes = args(2).row_vector_value (); RowVector ActiveCode = args(3).row_vector_value (); int numlayers = args(2).numel () + 1; int input_size = d; for (int i = 0; i < args(2).numel (); i++) { int output_size = (int) LayerSizes(i); if (output_size < 1) { error ("fcnntrain: cannot have a layer of zero size."); } DenseLayer DL = DenseLayer (input_size, output_size); int code = ActiveCode(i); if (code < 0 || code > 7) { error ("fcnntrain: invalid 'Activations' code."); } ActivationLayer AL = ActivationLayer (code, NumThreads, Alpha); WeightBias.push_back (DL); Activation.push_back (AL); input_size = output_size; } // Push back last dense layer int output_size = set (labels.begin (), labels.end ()).size (); DenseLayer DL = DenseLayer (input_size, output_size); int last_AC = args(2).numel (); ActivationLayer AL = ActivationLayer (ActiveCode(last_AC), NumThreads, Alpha); WeightBias.push_back (DL); Activation.push_back (AL); // Input validation on LearningRate, Epochs, and DisplayInfo if (! args(6).is_scalar_type () || ! args(6).isnumeric ()) { error ("fcnntrain: 'LearningRate' must be a positive scalar value."); } double learning_rate = args(6).scalar_value (); if (learning_rate <= 0) { error ("fcnntrain: 'LearningRate' must be a positive scalar value."); } if (! args(7).is_scalar_type () || ! args(7).isnumeric ()) { error ("fcnntrain: 'Epochs' must be a positive scalar value."); } if (args(7).scalar_value () < 1) { error ("fcnntrain: 'Epochs' must be a positive scalar value."); } if (! args(8).is_bool_scalar ()) { error ("fcnntrain: 'DisplayInfo' must be a boolean scalar."); } // Initialize return variables octave_idx_type max_epochs = args(7).idx_type_value (); vector Accuracy (max_epochs); vector Loss (max_epochs); // Start training octave_idx_type epoch = 0; for (; epoch < max_epochs; epoch++) { // Initialize Loss and Prediction double sum_loss = 0.0; vector predictions = vector (); // Go through all training samples for (int sample_idx = 0; sample_idx < n; sample_idx++) { vector sample = data[sample_idx]; int label = labels[sample_idx]; // Forward pass for (int layer_idx = 0; layer_idx < numlayers; layer_idx++) { sample = WeightBias[layer_idx].forward (sample); sample = Activation[layer_idx].forward (sample); } // Get the prediction for this sample vector label_vector = vector (output_size); label_vector[label-1] = 1.0; // Labels in Y start from 1 int prediction = 0; // Search for highest value for (int j = 0; j < output_size; j++) { if (sample[j] > sample[prediction]) { prediction = j; } } predictions.push_back (prediction); // Compute loss MeanSquaredErrorLoss loss = MeanSquaredErrorLoss (); double loss_output = loss.forward (sample, label_vector); sum_loss += loss_output; // Print output if (args(8).scalar_value () != 0) { if (sample_idx % 500 == 0) { cout << setprecision(4) << "i:" << sample_idx << " | Mean Loss: "; cout << (sum_loss / (sample_idx + 1)) << "\r" << flush; } } // Backward pass for (int layer_idx = 0; layer_idx < numlayers; layer_idx++) { WeightBias[layer_idx].zero_gradient (); // Reset gradients to zero } loss.backward(1.0); // Compute gradients Activation[numlayers-1].backward(loss.grad); for (int layer_idx = numlayers; layer_idx > 0; layer_idx--) { WeightBias[layer_idx-1].backward (Activation[layer_idx-1].grad); if (layer_idx > 1) { Activation[layer_idx-2].backward (WeightBias[layer_idx-1]); } } // Update weights for (int layer_idx = 0; layer_idx < numlayers; layer_idx++) { WeightBias[layer_idx].descend (learning_rate); } } // Compute Loss and Accuracy on the training set double A = accuracy (predictions, labels); double L = sum_loss / n; Accuracy.push_back (A); Loss.push_back (L); // Print output if (args(8).scalar_value () != 0) { cout << " \r" << "Epoch: " << epoch + 1 << " | Loss: " << L << " | Train Accuracy: " << A << endl; } } // Get weights and biases from each layer and store them in a cell array Cell LayerWeights(1, numlayers); for (int layer_idx = 0; layer_idx < numlayers; layer_idx++) { DenseLayer DL = WeightBias[layer_idx]; vector> Wb_matrix = DL.get_layer (); vector WB_vector = Wb_matrix[0]; octave_idx_type row = Wb_matrix.size (); octave_idx_type col = WB_vector.size (); Matrix WB (row, col); for (int r = 0; r < row; r++) { for (int c = 0; c < col; c++) { WB(r,c) = Wb_matrix[r][c]; } } LayerWeights.elem(layer_idx) = WB; } // Store accuracy and loss vectors in RowVector RowVector A(max_epochs); RowVector L(max_epochs); for (epoch = 0; epoch < max_epochs; epoch++) { A(epoch) = Accuracy[epoch]; L(epoch) = Loss[epoch]; } // Prepare returning arguments octave_scalar_map fcnn_model; fcnn_model.assign ("LayerWeights", LayerWeights); fcnn_model.assign ("Activations", ActiveCode); fcnn_model.assign ("Accuracy", A); fcnn_model.assign ("Loss", L); fcnn_model.assign ("Alpha", Alpha); octave_value_list retval (1); retval(0) = fcnn_model; return retval; } /* %!shared X, Y, MODEL %! load fisheriris %! X = meas; %! Y = grp2idx (species); %!error ... %! model = fcnntrain (X, Y); %!error ... %! [Q, W] = fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (complex (X), Y, 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain ({X}, Y, 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain ([], Y, 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, complex (Y), 10, 1, 0.01, [1, 1], 0.025, 50, false); %!error ... %! fcnntrain (X, {Y}, 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, [], 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y([1:50]), 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y - 1, 10, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, [10; 5], [1, 1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, "10", [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, {10}, [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, complex (10), [1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1; 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, {1, 1}, 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, "1", 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, complex ([1, 1]), 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, [10, 0, 5], [1, 1, 1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [-1, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [8, 1], 1, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 0, 0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, -0.01, 0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, -0.025, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, [0.025, 0.001], 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, {0.025}, 50, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, 0, false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, [50, 25], false); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, 50, 0); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, 50, 1); %!error ... %! fcnntrain (X, Y, 10, [1, 1], 1, 0.01, 0.025, 50, [false, false]); */ statistics-release-1.7.3/src/libsvmread.cc000066400000000000000000000131551475240274700206010ustar00rootroot00000000000000/* Copyright (C) 2022 Andreas Bertsatos Adapted from MATLAB libsvmwrite.c file from the LIBSVM 3.25 (2021) library by Chih-Chung Chang and Chih-Jen Lin. This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef max #define max(x,y) (((x)>(y))?(x):(y)) #endif #ifndef min #define min(x,y) (((x)<(y))?(x):(y)) #endif using namespace std; static char *line; static int max_line_len; static char* readline(FILE *input) { int len; if(fgets(line,max_line_len,input) == NULL) { return NULL; } while(strrchr(line,'\n') == NULL) { max_line_len *= 2; line = (char *) realloc(line, max_line_len); len = (int) strlen(line); if(fgets(line+len,max_line_len-len,input) == NULL) { break; } } return line; } // read the file in libsvm format void read(string filename, ColumnVector &label_vec, SparseMatrix &instance_mat) { int max_index, min_index, inst_max_index; size_t elements, k, i, l=0; FILE *fp = fopen(filename.c_str(),"r"); char *endptr; octave_idx_type *ir, *jc; double *labels, *samples; if(fp == NULL) { printf("can't open input file %s\n",filename.c_str()); return; } max_line_len = 1024; line = (char *) malloc(max_line_len*sizeof(char)); max_index = 0; min_index = 1; // our index starts from 1 elements = 0; while(readline(fp) != NULL) { char *idx, *val; // features int index = 0; inst_max_index = -1; strtok(line," \t"); while (1) { idx = strtok(NULL,":"); val = strtok(NULL," \t"); if(val == NULL) break; errno = 0; index = (int) strtol(idx,&endptr,10); if(endptr == idx || errno != 0 || *endptr != '\0' || index <= inst_max_index) { printf("libsvmread: wrong input format at line %d.\n", (int)l+1); return; } else inst_max_index = index; min_index = min(min_index, index); elements++; } max_index = max(max_index, inst_max_index); l++; } rewind(fp); // y label_vec = ColumnVector(l, 1); // x^T if (min_index <= 0) { octave_idx_type r = max_index-min_index+1; octave_idx_type c = l; octave_idx_type val = elements; instance_mat = SparseMatrix(r, c, val); } else { octave_idx_type r = max_index-min_index+1; octave_idx_type c = l; octave_idx_type val = elements; instance_mat = SparseMatrix(r, c, val); } labels = (double*)label_vec.data(); samples = (double*)instance_mat.data(); ir = (octave_idx_type*)instance_mat.ridx(); jc = (octave_idx_type*)instance_mat.cidx(); k=0; for(i=0;i start from 0 ir[k] = strtol(idx,&endptr,10) - min_index; errno = 0; samples[k] = strtod(val,&endptr); if (endptr == val || errno != 0 || (*endptr != '\0' && !isspace(*endptr))) { printf("libsvmread: wrong input format at line %d.\n", (int)i+1); return; } ++k; } } jc[l] = k; fclose(fp); free(line); // transpose instance sparse matrix in row format instance_mat.transpose(); } DEFUN_DLD (libsvmread, args, nargout, "-*- texinfo -*- \n\n\ @deftypefn {statistics} {[@var{labels}, @var{data}] =} libsvmread (@var{filename})\n\ \n\ \n\ This function reads the labels and the corresponding instance_matrix from a \ LIBSVM data file and stores them in @var{labels} and @var{data} respectively. \ These can then be used as inputs to @code{svmtrain} or @code{svmpredict} \ function. \ \n\ \n\ @end deftypefn") { if(args.length() != 1 || nargout != 2) { error ("libsvmread: wrong number of input or output arguments."); } if(!args(0).is_string()) { error ("libsvmread: filename must be a string."); } string filename = args(0).string_value(); octave_value_list retval(nargout); ColumnVector label_vec; SparseMatrix instance_mat; read(filename, label_vec, instance_mat); retval(0) = label_vec; retval(1) = instance_mat.transpose(); return retval; } /* %!error [L, D] = libsvmread (24); %!error ... %! D = libsvmread ("filename"); %!test %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %! assert (size (L), [270, 1]); %! assert (size (D), [270, 13]); %!test %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %! assert (issparse (L), false); %! assert (issparse (D), true); */ statistics-release-1.7.3/src/libsvmwrite.cc000066400000000000000000000106641475240274700210220ustar00rootroot00000000000000/* Copyright (C) 2022 Andreas Bertsatos Adapted from MATLAB libsvmwrite.c file from the LIBSVM 3.25 (2021) library by Chih-Chung Chang and Chih-Jen Lin. This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; void write(string filename, ColumnVector label_vec, SparseMatrix instance_mat) { // open file FILE *fp = fopen(filename.c_str(),"w"); if (fp == NULL) { error ("libsvmwrite: error opening file for write."); } else { // check for equal number of instances and labels int im_rows = (int)instance_mat.rows(); int lv_rows = (int)label_vec.rows(); if(im_rows != lv_rows) { // close file fclose (fp); remove (filename.c_str()); error ("libsvmwrite: length of label vector does not match instances."); } // transpose instance sparse matrix in column format SparseMatrix instance_mat_col = instance_mat.transpose(); octave_idx_type *ir, *jc, k, low, high; size_t i, l, label_vector_row_num; double *samples, *labels; // each column is one instance labels = (double*)label_vec.data(); samples = (double*)instance_mat_col.data(); ir = (octave_idx_type*)instance_mat_col.ridx(); jc = (octave_idx_type*)instance_mat_col.cidx(); for(int i = 0; i < lv_rows; i++) { fprintf(fp, "%.17g", labels[i]); low = jc[i], high = jc[i+1]; for(k=low;k 0) { error ("libsvmwrite: wrong number of output arguments."); return octave_value_list(); } // Transform the input Matrix to libsvm format if(args.length() == 3) { if(!args(1).is_double_type() || !args(2).is_double_type()) { error ("libsvmwrite: label vector and instance matrix must be double."); } if(!args(0).is_string()) { error ("libsvmwrite: filename must be a string."); } string filename = args(0).string_value(); if(args(2).issparse()) { ColumnVector label_vec = args(1).column_vector_value(); SparseMatrix instance_mat = args(2).sparse_matrix_value(); write(filename, label_vec, instance_mat); } else { error ("libsvmwrite: instance_matrix must be sparse."); } } else { error ("libsvmwrite: wrong number of input arguments."); } return octave_value_list(); } /* %!shared L, D %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %!error libsvmwrite ("", L, D); %!error ... %! libsvmwrite (tempname (), [L;L], D); %!error ... %! OUT = libsvmwrite (tempname (), L, D); %!error ... %! libsvmwrite (tempname (), single (L), D); %!error libsvmwrite (13412, L, D); %!error ... %! libsvmwrite (tempname (), L, full (D)); %!error ... %! libsvmwrite (tempname (), L, D, D); */ statistics-release-1.7.3/src/svm.cpp000066400000000000000000002007561475240274700174600ustar00rootroot00000000000000/* Copyright (C) 2021 Chih-Chung Chang and Chih-Jen Lin This file is part of the statistics package for GNU Octave. Permission granted by Chih-Jen Lin to the package maintainer to include this file and double license under GPLv3 by means of personal communication. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include "svm.h" int libsvm_version = LIBSVM_VERSION; typedef float Qfloat; typedef signed char schar; #ifndef min template static inline T min(T x,T y) { return (x static inline T max(T x,T y) { return (x>y)?x:y; } #endif template static inline void swap(T& x, T& y) { T t=x; x=y; y=t; } template static inline void clone(T*& dst, S* src, int n) { dst = new T[n]; memcpy((void *)dst,(void *)src,sizeof(T)*n); } static inline double powi(double base, int times) { double tmp = base, ret = 1.0; for(int t=times; t>0; t/=2) { if(t%2==1) ret*=tmp; tmp = tmp * tmp; } return ret; } #define INF HUGE_VAL #define TAU 1e-12 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static void print_string_stdout(const char *s) { fputs(s,stdout); fflush(stdout); } static void (*svm_print_string) (const char *) = &print_string_stdout; #if 1 static void info(const char *fmt,...) { char buf[BUFSIZ]; va_list ap; va_start(ap,fmt); vsnprintf(buf,sizeof(buf),fmt,ap); va_end(ap); (*svm_print_string)(buf); } #else static void info(const char *fmt,...) {} #endif // // Kernel Cache // // l is the number of total data items // size is the cache size limit in bytes // class Cache { public: Cache(int l,long int size); ~Cache(); // request data [0,len) // return some position p where [p,len) need to be filled // (p >= len if nothing needs to be filled) int get_data(const int index, Qfloat **data, int len); void swap_index(int i, int j); private: int l; long int size; struct head_t { head_t *prev, *next; // a circular list Qfloat *data; int len; // data[0,len) is cached in this entry }; head_t *head; head_t lru_head; void lru_delete(head_t *h); void lru_insert(head_t *h); }; Cache::Cache(int l_,long int size_):l(l_),size(size_) { head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0 size /= sizeof(Qfloat); size -= l * sizeof(head_t) / sizeof(Qfloat); size = max(size, 2 * (long int) l); // cache must be large enough for two columns lru_head.next = lru_head.prev = &lru_head; } Cache::~Cache() { for(head_t *h = lru_head.next; h != &lru_head; h=h->next) free(h->data); free(head); } void Cache::lru_delete(head_t *h) { // delete from current location h->prev->next = h->next; h->next->prev = h->prev; } void Cache::lru_insert(head_t *h) { // insert to last position h->next = &lru_head; h->prev = lru_head.prev; h->prev->next = h; h->next->prev = h; } int Cache::get_data(const int index, Qfloat **data, int len) { head_t *h = &head[index]; if(h->len) lru_delete(h); int more = len - h->len; if(more > 0) { // free old space while(size < more) { head_t *old = lru_head.next; lru_delete(old); free(old->data); size += old->len; old->data = 0; old->len = 0; } // allocate new space h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len); size -= more; swap(h->len,len); } lru_insert(h); *data = h->data; return len; } void Cache::swap_index(int i, int j) { if(i==j) return; if(head[i].len) lru_delete(&head[i]); if(head[j].len) lru_delete(&head[j]); swap(head[i].data,head[j].data); swap(head[i].len,head[j].len); if(head[i].len) lru_insert(&head[i]); if(head[j].len) lru_insert(&head[j]); if(i>j) swap(i,j); for(head_t *h = lru_head.next; h!=&lru_head; h=h->next) { if(h->len > i) { if(h->len > j) swap(h->data[i],h->data[j]); else { // give up lru_delete(h); free(h->data); size += h->len; h->data = 0; h->len = 0; } } } } // // Kernel evaluation // // the static method k_function is for doing single kernel evaluation // the constructor of Kernel prepares to calculate the l*l kernel matrix // the member function get_Q is for getting one column from the Q Matrix // class QMatrix { public: virtual Qfloat *get_Q(int column, int len) const = 0; virtual double *get_QD() const = 0; virtual void swap_index(int i, int j) const = 0; virtual ~QMatrix() {} }; class Kernel: public QMatrix { public: Kernel(int l, svm_node * const * x, const svm_parameter& param); virtual ~Kernel(); static double k_function(const svm_node *x, const svm_node *y, const svm_parameter& param); virtual Qfloat *get_Q(int column, int len) const = 0; virtual double *get_QD() const = 0; virtual void swap_index(int i, int j) const // no so const... { swap(x[i],x[j]); if(x_square) swap(x_square[i],x_square[j]); } protected: double (Kernel::*kernel_function)(int i, int j) const; private: const svm_node **x; double *x_square; // svm_parameter const int kernel_type; const int degree; const double gamma; const double coef0; static double dot(const svm_node *px, const svm_node *py); double kernel_linear(int i, int j) const { return dot(x[i],x[j]); } double kernel_poly(int i, int j) const { return powi(gamma*dot(x[i],x[j])+coef0,degree); } double kernel_rbf(int i, int j) const { return exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j]))); } double kernel_sigmoid(int i, int j) const { return tanh(gamma*dot(x[i],x[j])+coef0); } double kernel_precomputed(int i, int j) const { return x[i][(int)(x[j][0].value)].value; } }; Kernel::Kernel(int l, svm_node * const * x_, const svm_parameter& param) :kernel_type(param.kernel_type), degree(param.degree), gamma(param.gamma), coef0(param.coef0) { switch(kernel_type) { case LINEAR: kernel_function = &Kernel::kernel_linear; break; case POLY: kernel_function = &Kernel::kernel_poly; break; case RBF: kernel_function = &Kernel::kernel_rbf; break; case SIGMOID: kernel_function = &Kernel::kernel_sigmoid; break; case PRECOMPUTED: kernel_function = &Kernel::kernel_precomputed; break; } clone(x,x_,l); if(kernel_type == RBF) { x_square = new double[l]; for(int i=0;iindex != -1 && py->index != -1) { if(px->index == py->index) { sum += px->value * py->value; ++px; ++py; } else { if(px->index > py->index) ++py; else ++px; } } return sum; } double Kernel::k_function(const svm_node *x, const svm_node *y, const svm_parameter& param) { switch(param.kernel_type) { case LINEAR: return dot(x,y); case POLY: return powi(param.gamma*dot(x,y)+param.coef0,param.degree); case RBF: { double sum = 0; while(x->index != -1 && y->index !=-1) { if(x->index == y->index) { double d = x->value - y->value; sum += d*d; ++x; ++y; } else { if(x->index > y->index) { sum += y->value * y->value; ++y; } else { sum += x->value * x->value; ++x; } } } while(x->index != -1) { sum += x->value * x->value; ++x; } while(y->index != -1) { sum += y->value * y->value; ++y; } return exp(-param.gamma*sum); } case SIGMOID: return tanh(param.gamma*dot(x,y)+param.coef0); case PRECOMPUTED: //x: test (validation), y: SV return x[(int)(y->value)].value; default: return 0; // Unreachable } } // An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918 // Solves: // // min 0.5(\alpha^T Q \alpha) + p^T \alpha // // y^T \alpha = \delta // y_i = +1 or -1 // 0 <= alpha_i <= Cp for y_i = 1 // 0 <= alpha_i <= Cn for y_i = -1 // // Given: // // Q, p, y, Cp, Cn, and an initial feasible point \alpha // l is the size of vectors and matrices // eps is the stopping tolerance // // solution will be put in \alpha, objective value will be put in obj // class Solver { public: Solver() {}; virtual ~Solver() {}; struct SolutionInfo { double obj; double rho; double upper_bound_p; double upper_bound_n; double r; // for Solver_NU }; void Solve(int l, const QMatrix& Q, const double *p_, const schar *y_, double *alpha_, double Cp, double Cn, double eps, SolutionInfo* si, int shrinking); protected: int active_size; schar *y; double *G; // gradient of objective function enum { LOWER_BOUND, UPPER_BOUND, FREE }; char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE double *alpha; const QMatrix *Q; const double *QD; double eps; double Cp,Cn; double *p; int *active_set; double *G_bar; // gradient, if we treat free variables as 0 int l; bool unshrink; // XXX double get_C(int i) { return (y[i] > 0)? Cp : Cn; } void update_alpha_status(int i) { if(alpha[i] >= get_C(i)) alpha_status[i] = UPPER_BOUND; else if(alpha[i] <= 0) alpha_status[i] = LOWER_BOUND; else alpha_status[i] = FREE; } bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; } bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; } bool is_free(int i) { return alpha_status[i] == FREE; } void swap_index(int i, int j); void reconstruct_gradient(); virtual int select_working_set(int &i, int &j); virtual double calculate_rho(); virtual void do_shrinking(); private: bool be_shrunk(int i, double Gmax1, double Gmax2); }; void Solver::swap_index(int i, int j) { Q->swap_index(i,j); swap(y[i],y[j]); swap(G[i],G[j]); swap(alpha_status[i],alpha_status[j]); swap(alpha[i],alpha[j]); swap(p[i],p[j]); swap(active_set[i],active_set[j]); swap(G_bar[i],G_bar[j]); } void Solver::reconstruct_gradient() { // reconstruct inactive elements of G from G_bar and free variables if(active_size == l) return; int i,j; int nr_free = 0; for(j=active_size;j 2*active_size*(l-active_size)) { for(i=active_size;iget_Q(i,active_size); for(j=0;jget_Q(i,l); double alpha_i = alpha[i]; for(j=active_size;jl = l; this->Q = &Q; QD=Q.get_QD(); clone(p, p_,l); clone(y, y_,l); clone(alpha,alpha_,l); this->Cp = Cp; this->Cn = Cn; this->eps = eps; unshrink = false; // initialize alpha_status { alpha_status = new char[l]; for(int i=0;iINT_MAX/100 ? INT_MAX : 100*l); int counter = min(l,1000)+1; while(iter < max_iter) { // show progress and do shrinking if(--counter == 0) { counter = min(l,1000); if(shrinking) do_shrinking(); info("."); } int i,j; if(select_working_set(i,j)!=0) { // reconstruct the whole gradient reconstruct_gradient(); // reset active set size and check active_size = l; info("*"); if(select_working_set(i,j)!=0) break; else counter = 1; // do shrinking next iteration } ++iter; // update alpha[i] and alpha[j], handle bounds carefully const Qfloat *Q_i = Q.get_Q(i,active_size); const Qfloat *Q_j = Q.get_Q(j,active_size); double C_i = get_C(i); double C_j = get_C(j); double old_alpha_i = alpha[i]; double old_alpha_j = alpha[j]; if(y[i]!=y[j]) { double quad_coef = QD[i]+QD[j]+2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (-G[i]-G[j])/quad_coef; double diff = alpha[i] - alpha[j]; alpha[i] += delta; alpha[j] += delta; if(diff > 0) { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = diff; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = -diff; } } if(diff > C_i - C_j) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = C_i - diff; } } else { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = C_j + diff; } } } else { double quad_coef = QD[i]+QD[j]-2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (G[i]-G[j])/quad_coef; double sum = alpha[i] + alpha[j]; alpha[i] -= delta; alpha[j] += delta; if(sum > C_i) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = sum - C_i; } } else { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = sum; } } if(sum > C_j) { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = sum - C_j; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = sum; } } } // update G double delta_alpha_i = alpha[i] - old_alpha_i; double delta_alpha_j = alpha[j] - old_alpha_j; for(int k=0;k= max_iter) { if(active_size < l) { // reconstruct the whole gradient to calculate objective value reconstruct_gradient(); active_size = l; info("*"); } fprintf(stderr,"\nWARNING: reaching max number of iterations\n"); } // calculate rho si->rho = calculate_rho(); // calculate objective value { double v = 0; int i; for(i=0;iobj = v/2; } // put back the solution { for(int i=0;iupper_bound_p = Cp; si->upper_bound_n = Cn; info("\noptimization finished, #iter = %d\n",iter); delete[] p; delete[] y; delete[] alpha; delete[] alpha_status; delete[] active_set; delete[] G; delete[] G_bar; } // return 1 if already optimal, return 0 otherwise int Solver::select_working_set(int &out_i, int &out_j) { // return i,j such that // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmax = -INF; double Gmax2 = -INF; int Gmax_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmax) { Gmax = -G[t]; Gmax_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmax) { Gmax = G[t]; Gmax_idx = t; } } int i = Gmax_idx; const Qfloat *Q_i = NULL; if(i != -1) // NULL Q_i not accessed: Gmax=-INF if i=-1 Q_i = Q->get_Q(i,active_size); for(int j=0;j= Gmax2) Gmax2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff= Gmax-G[j]; if (-G[j] >= Gmax2) Gmax2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(Gmax+Gmax2 < eps || Gmin_idx == -1) return 1; out_i = Gmax_idx; out_j = Gmin_idx; return 0; } bool Solver::be_shrunk(int i, double Gmax1, double Gmax2) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax2); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax1); } else return(false); } void Solver::do_shrinking() { int i; double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) } // find maximal violating pair first for(i=0;i= Gmax1) Gmax1 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax2) Gmax2 = G[i]; } } else { if(!is_upper_bound(i)) { if(-G[i] >= Gmax2) Gmax2 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax1) Gmax1 = G[i]; } } } if(unshrink == false && Gmax1 + Gmax2 <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; info("*"); } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2)) { swap_index(i,active_size); break; } active_size--; } } } double Solver::calculate_rho() { double r; int nr_free = 0; double ub = INF, lb = -INF, sum_free = 0; for(int i=0;i0) r = sum_free/nr_free; else r = (ub+lb)/2; return r; } // // Solver for nu-svm classification and regression // // additional constraint: e^T \alpha = constant // class Solver_NU: public Solver { public: Solver_NU() {} void Solve(int l, const QMatrix& Q, const double *p, const schar *y, double *alpha, double Cp, double Cn, double eps, SolutionInfo* si, int shrinking) { this->si = si; Solver::Solve(l,Q,p,y,alpha,Cp,Cn,eps,si,shrinking); } private: SolutionInfo *si; int select_working_set(int &i, int &j); double calculate_rho(); bool be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4); void do_shrinking(); }; // return 1 if already optimal, return 0 otherwise int Solver_NU::select_working_set(int &out_i, int &out_j) { // return i,j such that y_i = y_j and // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmaxp = -INF; double Gmaxp2 = -INF; int Gmaxp_idx = -1; double Gmaxn = -INF; double Gmaxn2 = -INF; int Gmaxn_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmaxp) { Gmaxp = -G[t]; Gmaxp_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmaxn) { Gmaxn = G[t]; Gmaxn_idx = t; } } int ip = Gmaxp_idx; int in = Gmaxn_idx; const Qfloat *Q_ip = NULL; const Qfloat *Q_in = NULL; if(ip != -1) // NULL Q_ip not accessed: Gmaxp=-INF if ip=-1 Q_ip = Q->get_Q(ip,active_size); if(in != -1) Q_in = Q->get_Q(in,active_size); for(int j=0;j= Gmaxp2) Gmaxp2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[ip]+QD[j]-2*Q_ip[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff=Gmaxn-G[j]; if (-G[j] >= Gmaxn2) Gmaxn2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[in]+QD[j]-2*Q_in[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps || Gmin_idx == -1) return 1; if (y[Gmin_idx] == +1) out_i = Gmaxp_idx; else out_i = Gmaxn_idx; out_j = Gmin_idx; return 0; } bool Solver_NU::be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax4); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax3); } else return(false); } void Solver_NU::do_shrinking() { double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) } double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) } double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) } // find maximal violating pair first int i; for(i=0;i Gmax1) Gmax1 = -G[i]; } else if(-G[i] > Gmax4) Gmax4 = -G[i]; } if(!is_lower_bound(i)) { if(y[i]==+1) { if(G[i] > Gmax2) Gmax2 = G[i]; } else if(G[i] > Gmax3) Gmax3 = G[i]; } } if(unshrink == false && max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4)) { swap_index(i,active_size); break; } active_size--; } } } double Solver_NU::calculate_rho() { int nr_free1 = 0,nr_free2 = 0; double ub1 = INF, ub2 = INF; double lb1 = -INF, lb2 = -INF; double sum_free1 = 0, sum_free2 = 0; for(int i=0;i 0) r1 = sum_free1/nr_free1; else r1 = (ub1+lb1)/2; if(nr_free2 > 0) r2 = sum_free2/nr_free2; else r2 = (ub2+lb2)/2; si->r = (r1+r2)/2; return (r1-r2)/2; } // // Q matrices for various formulations // class SVC_Q: public Kernel { public: SVC_Q(const svm_problem& prob, const svm_parameter& param, const schar *y_) :Kernel(prob.l, prob.x, param) { clone(y,y_,prob.l); cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j)); } return data; } double *get_QD() const { return QD; } void swap_index(int i, int j) const { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(y[i],y[j]); swap(QD[i],QD[j]); } ~SVC_Q() { delete[] y; delete cache; delete[] QD; } private: schar *y; Cache *cache; double *QD; }; class ONE_CLASS_Q: public Kernel { public: ONE_CLASS_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j); } return data; } double *get_QD() const { return QD; } void swap_index(int i, int j) const { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(QD[i],QD[j]); } ~ONE_CLASS_Q() { delete cache; delete[] QD; } private: Cache *cache; double *QD; }; class SVR_Q: public Kernel { public: SVR_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { l = prob.l; cache = new Cache(l,(long int)(param.cache_size*(1<<20))); QD = new double[2*l]; sign = new schar[2*l]; index = new int[2*l]; for(int k=0;k*kernel_function)(k,k); QD[k+l] = QD[k]; } buffer[0] = new Qfloat[2*l]; buffer[1] = new Qfloat[2*l]; next_buffer = 0; } void swap_index(int i, int j) const { swap(sign[i],sign[j]); swap(index[i],index[j]); swap(QD[i],QD[j]); } Qfloat *get_Q(int i, int len) const { Qfloat *data; int j, real_i = index[i]; if(cache->get_data(real_i,&data,l) < l) { for(j=0;j*kernel_function)(real_i,j); } // reorder and copy Qfloat *buf = buffer[next_buffer]; next_buffer = 1 - next_buffer; schar si = sign[i]; for(j=0;jl; double *minus_ones = new double[l]; schar *y = new schar[l]; int i; for(i=0;iy[i] > 0) y[i] = +1; else y[i] = -1; } Solver s; s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y, alpha, Cp, Cn, param->eps, si, param->shrinking); double sum_alpha=0; for(i=0;il)); for(i=0;il; double nu = param->nu; schar *y = new schar[l]; for(i=0;iy[i]>0) y[i] = +1; else y[i] = -1; double sum_pos = nu*l/2; double sum_neg = nu*l/2; for(i=0;ieps, si, param->shrinking); double r = si->r; info("C = %f\n",1/r); for(i=0;irho /= r; si->obj /= (r*r); si->upper_bound_p = 1/r; si->upper_bound_n = 1/r; delete[] y; delete[] zeros; } static void solve_one_class( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *zeros = new double[l]; schar *ones = new schar[l]; int i; int n = (int)(param->nu*prob->l); // # of alpha's at upper bound for(i=0;il) alpha[n] = param->nu * prob->l - n; for(i=n+1;ieps, si, param->shrinking); delete[] zeros; delete[] ones; } static void solve_epsilon_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *alpha2 = new double[2*l]; double *linear_term = new double[2*l]; schar *y = new schar[2*l]; int i; for(i=0;ip - prob->y[i]; y[i] = 1; alpha2[i+l] = 0; linear_term[i+l] = param->p + prob->y[i]; y[i+l] = -1; } Solver s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, param->C, param->C, param->eps, si, param->shrinking); double sum_alpha = 0; for(i=0;iC*l)); delete[] alpha2; delete[] linear_term; delete[] y; } static void solve_nu_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double C = param->C; double *alpha2 = new double[2*l]; double *linear_term = new double[2*l]; schar *y = new schar[2*l]; int i; double sum = C * param->nu * l / 2; for(i=0;iy[i]; y[i] = 1; linear_term[i+l] = prob->y[i]; y[i+l] = -1; } Solver_NU s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, C, param->eps, si, param->shrinking); info("epsilon = %f\n",-si->r); for(i=0;il); Solver::SolutionInfo si; switch(param->svm_type) { case C_SVC: solve_c_svc(prob,param,alpha,&si,Cp,Cn); break; case NU_SVC: solve_nu_svc(prob,param,alpha,&si); break; case ONE_CLASS: solve_one_class(prob,param,alpha,&si); break; case EPSILON_SVR: solve_epsilon_svr(prob,param,alpha,&si); break; case NU_SVR: solve_nu_svr(prob,param,alpha,&si); break; } info("obj = %f, rho = %f\n",si.obj,si.rho); // output SVs int nSV = 0; int nBSV = 0; for(int i=0;il;i++) { if(fabs(alpha[i]) > 0) { ++nSV; if(prob->y[i] > 0) { if(fabs(alpha[i]) >= si.upper_bound_p) ++nBSV; } else { if(fabs(alpha[i]) >= si.upper_bound_n) ++nBSV; } } } info("nSV = %d, nBSV = %d\n",nSV,nBSV); decision_function f; f.alpha = alpha; f.rho = si.rho; return f; } // Platt's binary SVM Probablistic Output: an improvement from Lin et al. static void sigmoid_train( int l, const double *dec_values, const double *labels, double& A, double& B) { double prior1=0, prior0 = 0; int i; for (i=0;i 0) prior1+=1; else prior0+=1; int max_iter=100; // Maximal number of iterations double min_step=1e-10; // Minimal step taken in line search double sigma=1e-12; // For numerically strict PD of Hessian double eps=1e-5; double hiTarget=(prior1+1.0)/(prior1+2.0); double loTarget=1/(prior0+2.0); double *t=Malloc(double,l); double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize; double newA,newB,newf,d1,d2; int iter; // Initial Point and Initial Fun Value A=0.0; B=log((prior0+1.0)/(prior1+1.0)); double fval = 0.0; for (i=0;i0) t[i]=hiTarget; else t[i]=loTarget; fApB = dec_values[i]*A+B; if (fApB>=0) fval += t[i]*fApB + log(1+exp(-fApB)); else fval += (t[i] - 1)*fApB +log(1+exp(fApB)); } for (iter=0;iter= 0) { p=exp(-fApB)/(1.0+exp(-fApB)); q=1.0/(1.0+exp(-fApB)); } else { p=1.0/(1.0+exp(fApB)); q=exp(fApB)/(1.0+exp(fApB)); } d2=p*q; h11+=dec_values[i]*dec_values[i]*d2; h22+=d2; h21+=dec_values[i]*d2; d1=t[i]-p; g1+=dec_values[i]*d1; g2+=d1; } // Stopping Criteria if (fabs(g1)= min_step) { newA = A + stepsize * dA; newB = B + stepsize * dB; // New function value newf = 0.0; for (i=0;i= 0) newf += t[i]*fApB + log(1+exp(-fApB)); else newf += (t[i] - 1)*fApB +log(1+exp(fApB)); } // Check sufficient decrease if (newf=max_iter) info("Reaching maximal iterations in two-class probability estimates\n"); free(t); } static double sigmoid_predict(double decision_value, double A, double B) { double fApB = decision_value*A+B; // 1-p used later; avoid catastrophic cancellation if (fApB >= 0) return exp(-fApB)/(1.0+exp(-fApB)); else return 1.0/(1+exp(fApB)) ; } // Method 2 from the multiclass_prob paper by Wu, Lin, and Weng static void multiclass_probability(int k, double **r, double *p) { int t,j; int iter = 0, max_iter=max(100,k); double **Q=Malloc(double *,k); double *Qp=Malloc(double,k); double pQp, eps=0.005/k; for (t=0;tmax_error) max_error=error; } if (max_error=max_iter) info("Exceeds max_iter in multiclass_prob\n"); for(t=0;tl); double *dec_values = Malloc(double,prob->l); // random shuffle for(i=0;il;i++) perm[i]=i; for(i=0;il;i++) { int j = i+rand()%(prob->l-i); swap(perm[i],perm[j]); } for(i=0;il/nr_fold; int end = (i+1)*prob->l/nr_fold; int j,k; struct svm_problem subprob; subprob.l = prob->l-(end-begin); subprob.x = Malloc(struct svm_node*,subprob.l); subprob.y = Malloc(double,subprob.l); k=0; for(j=0;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; ++k; } for(j=end;jl;j++) { subprob.x[k] = prob->x[perm[j]]; subprob.y[k] = prob->y[perm[j]]; ++k; } int p_count=0,n_count=0; for(j=0;j0) p_count++; else n_count++; if(p_count==0 && n_count==0) for(j=begin;j 0 && n_count == 0) for(j=begin;j 0) for(j=begin;jx[perm[j]],&(dec_values[perm[j]])); // ensure +1 -1 order; reason not using CV subroutine dec_values[perm[j]] *= submodel->label[0]; } svm_free_and_destroy_model(&submodel); svm_destroy_param(&subparam); } free(subprob.x); free(subprob.y); } sigmoid_train(prob->l,dec_values,prob->y,probA,probB); free(dec_values); free(perm); } // Return parameter of a Laplace distribution static double svm_svr_probability( const svm_problem *prob, const svm_parameter *param) { int i; int nr_fold = 5; double *ymv = Malloc(double,prob->l); double mae = 0; svm_parameter newparam = *param; newparam.probability = 0; svm_cross_validation(prob,&newparam,nr_fold,ymv); for(i=0;il;i++) { ymv[i]=prob->y[i]-ymv[i]; mae += fabs(ymv[i]); } mae /= prob->l; double std=sqrt(2*mae*mae); int count=0; mae=0; for(i=0;il;i++) if (fabs(ymv[i]) > 5*std) count=count+1; else mae+=fabs(ymv[i]); mae /= (prob->l-count); info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma= %g\n",mae); free(ymv); return mae; } // label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data // perm, length l, must be allocated before calling this subroutine static void svm_group_classes(const svm_problem *prob, int *nr_class_ret, int **label_ret, int **start_ret, int **count_ret, int *perm) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; int *label = Malloc(int,max_nr_class); int *count = Malloc(int,max_nr_class); int *data_label = Malloc(int,l); int i; for(i=0;iy[i]; int j; for(j=0;jparam = *param; model->free_sv = 0; // XXX if(param->svm_type == ONE_CLASS || param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR) { // regression or one-class-svm model->nr_class = 2; model->label = NULL; model->nSV = NULL; model->probA = NULL; model->probB = NULL; model->sv_coef = Malloc(double *,1); if(param->probability && (param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR)) { model->probA = Malloc(double,1); model->probA[0] = svm_svr_probability(prob,param); } decision_function f = svm_train_one(prob,param,0,0); model->rho = Malloc(double,1); model->rho[0] = f.rho; int nSV = 0; int i; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) ++nSV; model->l = nSV; model->SV = Malloc(svm_node *,nSV); model->sv_coef[0] = Malloc(double,nSV); model->sv_indices = Malloc(int,nSV); int j = 0; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) { model->SV[j] = prob->x[i]; model->sv_coef[0][j] = f.alpha[i]; model->sv_indices[j] = i+1; ++j; } free(f.alpha); } else { // classification int l = prob->l; int nr_class; int *label = NULL; int *start = NULL; int *count = NULL; int *perm = Malloc(int,l); // group training data of the same class svm_group_classes(prob,&nr_class,&label,&start,&count,perm); if(nr_class == 1) info("WARNING: training data in only one class. See README for details.\n"); svm_node **x = Malloc(svm_node *,l); int i; for(i=0;ix[perm[i]]; // calculate weighted C double *weighted_C = Malloc(double, nr_class); for(i=0;iC; for(i=0;inr_weight;i++) { int j; for(j=0;jweight_label[i] == label[j]) break; if(j == nr_class) fprintf(stderr,"WARNING: class label %d specified in weight is not found\n", param->weight_label[i]); else weighted_C[j] *= param->weight[i]; } // train k*(k-1)/2 models bool *nonzero = Malloc(bool,l); for(i=0;iprobability) { probA=Malloc(double,nr_class*(nr_class-1)/2); probB=Malloc(double,nr_class*(nr_class-1)/2); } int p = 0; for(i=0;iprobability) svm_binary_svc_probability(&sub_prob,param,weighted_C[i],weighted_C[j],probA[p],probB[p]); f[p] = svm_train_one(&sub_prob,param,weighted_C[i],weighted_C[j]); for(k=0;k 0) nonzero[si+k] = true; for(k=0;k 0) nonzero[sj+k] = true; free(sub_prob.x); free(sub_prob.y); ++p; } // build output model->nr_class = nr_class; model->label = Malloc(int,nr_class); for(i=0;ilabel[i] = label[i]; model->rho = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;irho[i] = f[i].rho; if(param->probability) { model->probA = Malloc(double,nr_class*(nr_class-1)/2); model->probB = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;iprobA[i] = probA[i]; model->probB[i] = probB[i]; } } else { model->probA=NULL; model->probB=NULL; } int total_sv = 0; int *nz_count = Malloc(int,nr_class); model->nSV = Malloc(int,nr_class); for(i=0;inSV[i] = nSV; nz_count[i] = nSV; } info("Total nSV = %d\n",total_sv); model->l = total_sv; model->SV = Malloc(svm_node *,total_sv); model->sv_indices = Malloc(int,total_sv); p = 0; for(i=0;iSV[p] = x[i]; model->sv_indices[p++] = perm[i] + 1; } int *nz_start = Malloc(int,nr_class); nz_start[0] = 0; for(i=1;isv_coef = Malloc(double *,nr_class-1); for(i=0;isv_coef[i] = Malloc(double,total_sv); p = 0; for(i=0;isv_coef[j-1][q++] = f[p].alpha[k]; q = nz_start[j]; for(k=0;ksv_coef[i][q++] = f[p].alpha[ci+k]; ++p; } free(label); free(probA); free(probB); free(count); free(perm); free(start); free(x); free(weighted_C); free(nonzero); for(i=0;il; int *perm = Malloc(int,l); int nr_class; if (nr_fold > l) { nr_fold = l; fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n"); } fold_start = Malloc(int,nr_fold+1); // stratified cv may not give leave-one-out rate // Each class to l folds -> some folds may have zero elements if((param->svm_type == C_SVC || param->svm_type == NU_SVC) && nr_fold < l) { int *start = NULL; int *label = NULL; int *count = NULL; svm_group_classes(prob,&nr_class,&label,&start,&count,perm); // random shuffle and then data grouped by fold using the array perm int *fold_count = Malloc(int,nr_fold); int c; int *index = Malloc(int,l); for(i=0;ix[perm[j]]; subprob.y[k] = prob->y[perm[j]]; ++k; } for(j=end;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; ++k; } struct svm_model *submodel = svm_train(&subprob,param); if(param->probability && (param->svm_type == C_SVC || param->svm_type == NU_SVC)) { double *prob_estimates=Malloc(double,svm_get_nr_class(submodel)); for(j=begin;jx[perm[j]],prob_estimates); free(prob_estimates); } else for(j=begin;jx[perm[j]]); svm_free_and_destroy_model(&submodel); free(subprob.x); free(subprob.y); } free(fold_start); free(perm); } int svm_get_svm_type(const svm_model *model) { return model->param.svm_type; } int svm_get_nr_class(const svm_model *model) { return model->nr_class; } void svm_get_labels(const svm_model *model, int* label) { if (model->label != NULL) for(int i=0;inr_class;i++) label[i] = model->label[i]; } void svm_get_sv_indices(const svm_model *model, int* indices) { if (model->sv_indices != NULL) for(int i=0;il;i++) indices[i] = model->sv_indices[i]; } int svm_get_nr_sv(const svm_model *model) { return model->l; } double svm_get_svr_probability(const svm_model *model) { if ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=NULL) return model->probA[0]; else { fprintf(stderr,"Model doesn't contain information for SVR probability inference\n"); return 0; } } double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values) { int i; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) { double *sv_coef = model->sv_coef[0]; double sum = 0; for(i=0;il;i++) sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param); sum -= model->rho[0]; *dec_values = sum; if(model->param.svm_type == ONE_CLASS) return (sum>0)?1:-1; else return sum; } else { int nr_class = model->nr_class; int l = model->l; double *kvalue = Malloc(double,l); for(i=0;iSV[i],model->param); int *start = Malloc(int,nr_class); start[0] = 0; for(i=1;inSV[i-1]; int *vote = Malloc(int,nr_class); for(i=0;inSV[i]; int cj = model->nSV[j]; int k; double *coef1 = model->sv_coef[j-1]; double *coef2 = model->sv_coef[i]; for(k=0;krho[p]; dec_values[p] = sum; if(dec_values[p] > 0) ++vote[i]; else ++vote[j]; p++; } int vote_max_idx = 0; for(i=1;i vote[vote_max_idx]) vote_max_idx = i; free(kvalue); free(start); free(vote); return model->label[vote_max_idx]; } } double svm_predict(const svm_model *model, const svm_node *x) { int nr_class = model->nr_class; double *dec_values; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) dec_values = Malloc(double, 1); else dec_values = Malloc(double, nr_class*(nr_class-1)/2); double pred_result = svm_predict_values(model, x, dec_values); free(dec_values); return pred_result; } double svm_predict_probability( const svm_model *model, const svm_node *x, double *prob_estimates) { if ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=NULL && model->probB!=NULL) { int i; int nr_class = model->nr_class; double *dec_values = Malloc(double, nr_class*(nr_class-1)/2); svm_predict_values(model, x, dec_values); double min_prob=1e-7; double **pairwise_prob=Malloc(double *,nr_class); for(i=0;iprobA[k],model->probB[k]),min_prob),1-min_prob); pairwise_prob[j][i]=1-pairwise_prob[i][j]; k++; } if (nr_class == 2) { prob_estimates[0] = pairwise_prob[0][1]; prob_estimates[1] = pairwise_prob[1][0]; } else multiclass_probability(nr_class,pairwise_prob,prob_estimates); int prob_max_idx = 0; for(i=1;i prob_estimates[prob_max_idx]) prob_max_idx = i; for(i=0;ilabel[prob_max_idx]; } else return svm_predict(model, x); } static const char *svm_type_table[] = { "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",NULL }; static const char *kernel_type_table[]= { "linear","polynomial","rbf","sigmoid","precomputed",NULL }; int svm_save_model(const char *model_file_name, const svm_model *model) { FILE *fp = fopen(model_file_name,"w"); if(fp==NULL) return -1; char *old_locale = setlocale(LC_ALL, NULL); if (old_locale) { old_locale = strdup(old_locale); } setlocale(LC_ALL, "C"); const svm_parameter& param = model->param; fprintf(fp,"svm_type %s\n", svm_type_table[param.svm_type]); fprintf(fp,"kernel_type %s\n", kernel_type_table[param.kernel_type]); if(param.kernel_type == POLY) fprintf(fp,"degree %d\n", param.degree); if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID) fprintf(fp,"gamma %.17g\n", param.gamma); if(param.kernel_type == POLY || param.kernel_type == SIGMOID) fprintf(fp,"coef0 %.17g\n", param.coef0); int nr_class = model->nr_class; int l = model->l; fprintf(fp, "nr_class %d\n", nr_class); fprintf(fp, "total_sv %d\n",l); { fprintf(fp, "rho"); for(int i=0;irho[i]); fprintf(fp, "\n"); } if(model->label) { fprintf(fp, "label"); for(int i=0;ilabel[i]); fprintf(fp, "\n"); } if(model->probA) // regression has probA only { fprintf(fp, "probA"); for(int i=0;iprobA[i]); fprintf(fp, "\n"); } if(model->probB) { fprintf(fp, "probB"); for(int i=0;iprobB[i]); fprintf(fp, "\n"); } if(model->nSV) { fprintf(fp, "nr_sv"); for(int i=0;inSV[i]); fprintf(fp, "\n"); } fprintf(fp, "SV\n"); const double * const *sv_coef = model->sv_coef; const svm_node * const *SV = model->SV; for(int i=0;ivalue)); else while(p->index != -1) { fprintf(fp,"%d:%.8g ",p->index,p->value); p++; } fprintf(fp, "\n"); } setlocale(LC_ALL, old_locale); free(old_locale); if (ferror(fp) != 0 || fclose(fp) != 0) return -1; else return 0; } static char *line = NULL; static int max_line_len; static char* readline(FILE *input) { int len; if(fgets(line,max_line_len,input) == NULL) return NULL; while(strrchr(line,'\n') == NULL) { max_line_len *= 2; line = (char *) realloc(line,max_line_len); len = (int) strlen(line); if(fgets(line+len,max_line_len-len,input) == NULL) break; } return line; } // // FSCANF helps to handle fscanf failures. // Its do-while block avoids the ambiguity when // if (...) // FSCANF(); // is used // #define FSCANF(_stream, _format, _var) do{ if (fscanf(_stream, _format, _var) != 1) return false; }while(0) bool read_model_header(FILE *fp, svm_model* model) { svm_parameter& param = model->param; // parameters for training only won't be assigned, but arrays are assigned as NULL for safety param.nr_weight = 0; param.weight_label = NULL; param.weight = NULL; char cmd[81]; while(1) { FSCANF(fp,"%80s",cmd); if(strcmp(cmd,"svm_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;svm_type_table[i];i++) { if(strcmp(svm_type_table[i],cmd)==0) { param.svm_type=i; break; } } if(svm_type_table[i] == NULL) { fprintf(stderr,"unknown svm type.\n"); return false; } } else if(strcmp(cmd,"kernel_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;kernel_type_table[i];i++) { if(strcmp(kernel_type_table[i],cmd)==0) { param.kernel_type=i; break; } } if(kernel_type_table[i] == NULL) { fprintf(stderr,"unknown kernel function.\n"); return false; } } else if(strcmp(cmd,"degree")==0) FSCANF(fp,"%d",¶m.degree); else if(strcmp(cmd,"gamma")==0) FSCANF(fp,"%lf",¶m.gamma); else if(strcmp(cmd,"coef0")==0) FSCANF(fp,"%lf",¶m.coef0); else if(strcmp(cmd,"nr_class")==0) FSCANF(fp,"%d",&model->nr_class); else if(strcmp(cmd,"total_sv")==0) FSCANF(fp,"%d",&model->l); else if(strcmp(cmd,"rho")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->rho = Malloc(double,n); for(int i=0;irho[i]); } else if(strcmp(cmd,"label")==0) { int n = model->nr_class; model->label = Malloc(int,n); for(int i=0;ilabel[i]); } else if(strcmp(cmd,"probA")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probA = Malloc(double,n); for(int i=0;iprobA[i]); } else if(strcmp(cmd,"probB")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probB = Malloc(double,n); for(int i=0;iprobB[i]); } else if(strcmp(cmd,"nr_sv")==0) { int n = model->nr_class; model->nSV = Malloc(int,n); for(int i=0;inSV[i]); } else if(strcmp(cmd,"SV")==0) { while(1) { int c = getc(fp); if(c==EOF || c=='\n') break; } break; } else { fprintf(stderr,"unknown text in model file: [%s]\n",cmd); return false; } } return true; } svm_model *svm_load_model(const char *model_file_name) { FILE *fp = fopen(model_file_name,"rb"); if(fp==NULL) return NULL; char *old_locale = setlocale(LC_ALL, NULL); if (old_locale) { old_locale = strdup(old_locale); } setlocale(LC_ALL, "C"); // read parameters svm_model *model = Malloc(svm_model,1); model->rho = NULL; model->probA = NULL; model->probB = NULL; model->sv_indices = NULL; model->label = NULL; model->nSV = NULL; // read header if (!read_model_header(fp, model)) { fprintf(stderr, "ERROR: fscanf failed to read model\n"); setlocale(LC_ALL, old_locale); free(old_locale); free(model->rho); free(model->label); free(model->nSV); free(model); return NULL; } // read sv_coef and SV int elements = 0; long pos = ftell(fp); max_line_len = 1024; line = Malloc(char,max_line_len); char *p,*endptr,*idx,*val; while(readline(fp)!=NULL) { p = strtok(line,":"); while(1) { p = strtok(NULL,":"); if(p == NULL) break; ++elements; } } elements += model->l; fseek(fp,pos,SEEK_SET); int m = model->nr_class - 1; int l = model->l; model->sv_coef = Malloc(double *,m); int i; for(i=0;isv_coef[i] = Malloc(double,l); model->SV = Malloc(svm_node*,l); svm_node *x_space = NULL; if(l>0) x_space = Malloc(svm_node,elements); int j=0; for(i=0;iSV[i] = &x_space[j]; p = strtok(line, " \t"); model->sv_coef[0][i] = strtod(p,&endptr); for(int k=1;ksv_coef[k][i] = strtod(p,&endptr); } while(1) { idx = strtok(NULL, ":"); val = strtok(NULL, " \t"); if(val == NULL) break; x_space[j].index = (int) strtol(idx,&endptr,10); x_space[j].value = strtod(val,&endptr); ++j; } x_space[j++].index = -1; } free(line); setlocale(LC_ALL, old_locale); free(old_locale); if (ferror(fp) != 0 || fclose(fp) != 0) return NULL; model->free_sv = 1; // XXX return model; } void svm_free_model_content(svm_model* model_ptr) { if(model_ptr->free_sv && model_ptr->l > 0 && model_ptr->SV != NULL) free((void *)(model_ptr->SV[0])); if(model_ptr->sv_coef) { for(int i=0;inr_class-1;i++) free(model_ptr->sv_coef[i]); } free(model_ptr->SV); model_ptr->SV = NULL; free(model_ptr->sv_coef); model_ptr->sv_coef = NULL; free(model_ptr->rho); model_ptr->rho = NULL; free(model_ptr->label); model_ptr->label= NULL; free(model_ptr->probA); model_ptr->probA = NULL; free(model_ptr->probB); model_ptr->probB= NULL; free(model_ptr->sv_indices); model_ptr->sv_indices = NULL; free(model_ptr->nSV); model_ptr->nSV = NULL; } void svm_free_and_destroy_model(svm_model** model_ptr_ptr) { if(model_ptr_ptr != NULL && *model_ptr_ptr != NULL) { svm_free_model_content(*model_ptr_ptr); free(*model_ptr_ptr); *model_ptr_ptr = NULL; } } void svm_destroy_param(svm_parameter* param) { free(param->weight_label); free(param->weight); } const char *svm_check_parameter(const svm_problem *prob, const svm_parameter *param) { // svm_type int svm_type = param->svm_type; if(svm_type != C_SVC && svm_type != NU_SVC && svm_type != ONE_CLASS && svm_type != EPSILON_SVR && svm_type != NU_SVR) return "unknown svm type"; // kernel_type, degree int kernel_type = param->kernel_type; if(kernel_type != LINEAR && kernel_type != POLY && kernel_type != RBF && kernel_type != SIGMOID && kernel_type != PRECOMPUTED) return "unknown kernel type"; if((kernel_type == POLY || kernel_type == RBF || kernel_type == SIGMOID) && param->gamma < 0) return "gamma < 0"; if(kernel_type == POLY && param->degree < 0) return "degree of polynomial kernel < 0"; // cache_size,eps,C,nu,p,shrinking if(param->cache_size <= 0) return "cache_size <= 0"; if(param->eps <= 0) return "eps <= 0"; if(svm_type == C_SVC || svm_type == EPSILON_SVR || svm_type == NU_SVR) if(param->C <= 0) return "C <= 0"; if(svm_type == NU_SVC || svm_type == ONE_CLASS || svm_type == NU_SVR) if(param->nu <= 0 || param->nu > 1) return "nu <= 0 or nu > 1"; if(svm_type == EPSILON_SVR) if(param->p < 0) return "p < 0"; if(param->shrinking != 0 && param->shrinking != 1) return "shrinking != 0 and shrinking != 1"; if(param->probability != 0 && param->probability != 1) return "probability != 0 and probability != 1"; if(param->probability == 1 && svm_type == ONE_CLASS) return "one-class SVM probability output not supported yet"; // check whether nu-svc is feasible if(svm_type == NU_SVC) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; int *label = Malloc(int,max_nr_class); int *count = Malloc(int,max_nr_class); int i; for(i=0;iy[i]; int j; for(j=0;jnu*(n1+n2)/2 > min(n1,n2)) { free(label); free(count); return "specified nu is infeasible"; } } } free(label); free(count); } return NULL; } int svm_check_probability_model(const svm_model *model) { return ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=NULL && model->probB!=NULL) || ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=NULL); } void svm_set_print_string_function(void (*print_func)(const char *)) { if(print_func == NULL) svm_print_string = &print_string_stdout; else svm_print_string = print_func; } statistics-release-1.7.3/src/svm.h000066400000000000000000000102461475240274700171160ustar00rootroot00000000000000/* Copyright (C) 2021 Chih-Chung Chang and Chih-Jen Lin This file is part of the statistics package for GNU Octave. Permission granted by Chih-Jen Lin to the package maintainer to include this file and double license under GPLv3 by means of personal communication. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #ifndef _LIBSVM_H #define _LIBSVM_H #define LIBSVM_VERSION 325 #ifdef __cplusplus extern "C" { #endif extern int libsvm_version; struct svm_node { int index; double value; }; struct svm_problem { int l; double *y; struct svm_node **x; }; enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR }; /* svm_type */ enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */ struct svm_parameter { int svm_type; int kernel_type; int degree; /* for poly */ double gamma; /* for poly/rbf/sigmoid */ double coef0; /* for poly/sigmoid */ /* these are for training only */ double cache_size; /* in MB */ double eps; /* stopping criteria */ double C; /* for C_SVC, EPSILON_SVR and NU_SVR */ int nr_weight; /* for C_SVC */ int *weight_label; /* for C_SVC */ double* weight; /* for C_SVC */ double nu; /* for NU_SVC, ONE_CLASS, and NU_SVR */ double p; /* for EPSILON_SVR */ int shrinking; /* use the shrinking heuristics */ int probability; /* do probability estimates */ }; // // svm_model // struct svm_model { struct svm_parameter param; /* parameter */ int nr_class; /* number of classes, = 2 in regression/one class svm */ int l; /* total #SV */ struct svm_node **SV; /* SVs (SV[l]) */ double **sv_coef; /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */ double *rho; /* constants in decision functions (rho[k*(k-1)/2]) */ double *probA; /* pariwise probability information */ double *probB; int *sv_indices; /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */ /* for classification only */ int *label; /* label of each class (label[k]) */ int *nSV; /* number of SVs for each class (nSV[k]) */ /* nSV[0] + nSV[1] + ... + nSV[k-1] = l */ /* XXX */ int free_sv; /* 1 if svm_model is created by svm_load_model*/ /* 0 if svm_model is created by svm_train */ }; struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param); void svm_cross_validation(const struct svm_problem *prob, const struct svm_parameter *param, int nr_fold, double *target); int svm_save_model(const char *model_file_name, const struct svm_model *model); struct svm_model *svm_load_model(const char *model_file_name); int svm_get_svm_type(const struct svm_model *model); int svm_get_nr_class(const struct svm_model *model); void svm_get_labels(const struct svm_model *model, int *label); void svm_get_sv_indices(const struct svm_model *model, int *sv_indices); int svm_get_nr_sv(const struct svm_model *model); double svm_get_svr_probability(const struct svm_model *model); double svm_predict_values(const struct svm_model *model, const struct svm_node *x, double* dec_values); double svm_predict(const struct svm_model *model, const struct svm_node *x); double svm_predict_probability(const struct svm_model *model, const struct svm_node *x, double* prob_estimates); void svm_free_model_content(struct svm_model *model_ptr); void svm_free_and_destroy_model(struct svm_model **model_ptr_ptr); void svm_destroy_param(struct svm_parameter *param); const char *svm_check_parameter(const struct svm_problem *prob, const struct svm_parameter *param); int svm_check_probability_model(const struct svm_model *model); void svm_set_print_string_function(void (*print_func)(const char *)); #ifdef __cplusplus } #endif #endif /* _LIBSVM_H */ statistics-release-1.7.3/src/svm_model_octave.cc000066400000000000000000000214061475240274700217750ustar00rootroot00000000000000/* Copyright (C) 2022 Andreas Bertsatos Based on the Octave LIBSVM wrapper created by Alan Meeson (2014) based on an earlier version of the LIBSVM (3.18) library for MATLAB. Current implementation is based on LIBSVM 3.25 (2021) by Chih-Chung Chang and Chih-Jen Lin. This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include "svm.h" #define NUM_OF_RETURN_FIELD 11 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static const char *field_names[] = { "Parameters", "nr_class", "totalSV", "rho", "Label", "sv_indices", "ProbA", "ProbB", "nSV", "sv_coef", "SVs" }; const char *model_to_octave_structure(octave_value_list &plhs, int num_of_feature, struct svm_model *model) { int i, j, n; double *ptr; octave_scalar_map osm_model; // Parameters ColumnVector cm_parameters (5); cm_parameters(0) = model->param.svm_type; cm_parameters(1) = model->param.kernel_type; cm_parameters(2) = model->param.degree; cm_parameters(3) = model->param.gamma; cm_parameters(4) = model->param.coef0; osm_model.assign("Parameters", cm_parameters); // nr_class osm_model.assign("nr_class", octave_value(model->nr_class)); // total SV osm_model.assign("totalSV", octave_value(model->l)); // rho n = model->nr_class*(model->nr_class-1)/2; ColumnVector cm_rho(n); for(i = 0; i < n; i++) { cm_rho(i) = model->rho[i]; } osm_model.assign("rho", cm_rho); // Label if(model->label) { ColumnVector cm_label(model->nr_class); for(i = 0; i < model->nr_class; i++) { cm_label(i) = model->label[i]; } osm_model.assign("Label", cm_label); } else { osm_model.assign("Label", ColumnVector(0)); } // sv_indices if(model->sv_indices) { ColumnVector cm_sv_indices(model->l); for(i = 0; i < model->l; i++) { cm_sv_indices(i) = model->sv_indices[i]; } osm_model.assign("sv_indices", cm_sv_indices); } else { osm_model.assign("sv_indices", ColumnVector(0)); } // probA if(model->probA != NULL) { ColumnVector cm_proba(n); for(i = 0; i < n; i++) { cm_proba(i) = model->probA[i]; } osm_model.assign("ProbA", cm_proba); } else { osm_model.assign("ProbA", ColumnVector(0)); } // probB if(model->probB != NULL) { ColumnVector cm_probb(n); for(i = 0; i < n; i++) { cm_probb(i) = model->probB[i]; } osm_model.assign("ProbB", cm_probb); } else { osm_model.assign("ProbB", ColumnVector(0)); } // nSV if(model->nSV) { ColumnVector cm_nsv(model->nr_class); for(i = 0; i < model->nr_class; i++) { cm_nsv(i) = model->nSV[i]; } osm_model.assign("nSV", cm_nsv); } else { osm_model.assign("nSV", ColumnVector(0)); } // sv_coef Matrix m_sv_coef(model->l, model->nr_class-1); for (i = 0; i < model->nr_class-1; i++) { for(j = 0; j < model->l; j++) { m_sv_coef(j,i) = model->sv_coef[i][j]; } } osm_model.assign("sv_coef", m_sv_coef); // SVs { int ir_index, nonzero_element; octave_idx_type *ir, *jc; //mxArray *pprhs[1], *pplhs[1]; if(model->param.kernel_type == PRECOMPUTED) { nonzero_element = model->l; num_of_feature = 1; } else { nonzero_element = 0; for(i = 0; i < model->l; i++) { j = 0; while(model->SV[i][j].index != -1) { nonzero_element++; j++; } } } // SV in column, easier accessing SparseMatrix sm_rhs = SparseMatrix((octave_idx_type)num_of_feature, (octave_idx_type)model->l, (octave_idx_type)nonzero_element); ir = sm_rhs.ridx(); jc = sm_rhs.cidx(); ptr = (double*) sm_rhs.data(); jc[0] = ir_index = 0; for(i = 0; i < model->l; i++) { if(model->param.kernel_type == PRECOMPUTED) { // make a (1 x model->l) matrix ir[ir_index] = 0; ptr[ir_index] = model->SV[i][0].value; ir_index++; jc[i+1] = jc[i] + 1; } else { int x_index = 0; while (model->SV[i][x_index].index != -1) { ir[ir_index] = model->SV[i][x_index].index - 1; ptr[ir_index] = model->SV[i][x_index].value; ir_index++, x_index++; } jc[i+1] = jc[i] + x_index; } } // transpose back to SV in row sm_rhs = sm_rhs.transpose(); osm_model.assign("SVs", sm_rhs); } /* return */ plhs(0) = osm_model; return NULL; } struct svm_model *octave_matrix_to_model(octave_scalar_map &octave_model, const char **msg) { int i, j, n, num_of_fields; double *ptr; int id = 0; struct svm_node *x_space; struct svm_model *model; model = Malloc(struct svm_model, 1); model->rho = NULL; model->probA = NULL; model->probB = NULL; model->label = NULL; model->sv_indices = NULL; model->nSV = NULL; model->free_sv = 1; // XXX //Parameters ColumnVector cm_parameters = octave_model.getfield("Parameters").column_vector_value(); model->param.svm_type = (int)cm_parameters(0); model->param.kernel_type = (int)cm_parameters(1); model->param.degree = (int)cm_parameters(2); model->param.gamma = cm_parameters(3); model->param.coef0 = cm_parameters(4); //nr_class model->nr_class = (int)octave_model.getfield("nr_class").int_value(); //total SV model->l = (int)octave_model.getfield("totalSV").int_value(); //rho n = model->nr_class * (model->nr_class-1)/2; model->rho = (double*) malloc(n*sizeof(double)); ColumnVector cm_rho = octave_model.getfield("rho").column_vector_value(); for(i = 0; i < n; i++) { model->rho[i] = cm_rho(i); } //label if (!octave_model.getfield("Label").isempty()) { model->label = (int*) malloc(model->nr_class*sizeof(int)); ColumnVector cm_label = octave_model.getfield("Label").column_vector_value(); for(i = 0; i < model->nr_class; i++) { model->label[i] = (int)cm_label(i); } } //sv_indices if (!octave_model.getfield("sv_indices").isempty()) { model->sv_indices = (int*) malloc(model->l*sizeof(int)); ColumnVector cv_svi = octave_model.getfield("sv_indices").column_vector_value(); for(i = 0; i < model->l; i++) { model->sv_indices[i] = (int)cv_svi(i); } } // probA if(!octave_model.getfield("ProbA").isempty()) { model->probA = (double*) malloc(n*sizeof(double)); ColumnVector cv_proba = octave_model.getfield("ProbA").column_vector_value(); for(i = 0; i < n; i++) { model->probA[i] = cv_proba(i); } } // probB if(!octave_model.getfield("ProbB").isempty()) { model->probB = (double*) malloc(n*sizeof(double)); ColumnVector cv_probb = octave_model.getfield("ProbB").column_vector_value(); for(i = 0; i < n; i++) { model->probB[i] = cv_probb(i); } } // nSV if(!octave_model.getfield("nSV").isempty()) { model->nSV = (int*) malloc(model->nr_class*sizeof(int)); ColumnVector cv_nsv = octave_model.getfield("nSV").column_vector_value(); for(i = 0; i < model->nr_class; i++) { model->nSV[i] = (int)cv_nsv(i); } } // sv_coef Matrix m_sv_coef = octave_model.getfield("sv_coef").matrix_value(); ptr = (double*) m_sv_coef.data(); model->sv_coef = (double**) malloc((model->nr_class-1)*sizeof(double)); for(i = 0; i < model->nr_class - 1; i++ ) { model->sv_coef[i] = (double*) malloc((model->l)*sizeof(double)); } for(i = 0; i < model->nr_class - 1; i++) { for(j = 0; j < model->l; j++) { model->sv_coef[i][j] = ptr[i*(model->l)+j];//m_sv_coef(i,j); } } // SV { int sr, sc, elements; int num_samples; octave_idx_type *ir, *jc; // transpose SV SparseMatrix sm_sv = octave_model.getfield("SVs").sparse_matrix_value(); sm_sv = sm_sv.transpose(); sr = (int)sm_sv.cols(); sc = (int)sm_sv.rows(); ptr = (double*)sm_sv.data(); ir = sm_sv.ridx(); jc = sm_sv.cidx(); num_samples = (int)sm_sv.nzmax(); elements = num_samples + sr; model->SV = (struct svm_node **) malloc(sr * sizeof(struct svm_node *)); x_space = (struct svm_node *)malloc(elements * sizeof(struct svm_node)); // SV is in column for(i = 0; i < sr; i++) { int low = (int)jc[i], high = (int)jc[i+1]; int x_index = 0; model->SV[i] = &x_space[low+i]; for(j = low; j < high; j++) { model->SV[i][x_index].index = (int)ir[j] + 1; model->SV[i][x_index].value = ptr[j]; x_index++; } model->SV[i][x_index].index = -1; } id++; } return model; } statistics-release-1.7.3/src/svm_model_octave.h000066400000000000000000000023771475240274700216450ustar00rootroot00000000000000/* Copyright (C) 2022 Andreas Bertsatos Based on the Octave LIBSVM wrapper created by Alan Meeson (2014) based on an earlier version of the LIBSVM (3.18) library for MATLAB. Current implementation is based on LIBSVM 3.25 (2021) by Chih-Chung Chang and Chih-Jen Lin. This file is part of the statistics package for GNU Octave. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include "svm.h" const char *model_to_octave_structure(octave_value_list &plhs, int num_of_feature, struct svm_model *model); struct svm_model *octave_matrix_to_model(octave_scalar_map &octave_struct, const char **error_message); statistics-release-1.7.3/src/svmpredict.cc000066400000000000000000000327041475240274700206320ustar00rootroot00000000000000/* Copyright (C) 2022 Andreas Bertsatos Based on the Octave LIBSVM wrapper adapted by Alan Meeson (2014) based on an earlier version of the LIBSVM (3.18) library for MATLAB. Current implementation is based on LIBSVM 3.25 (2021) by Chih-Chung Chang and Chih-Jen Lin. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "svm.h" #include "svm_model_octave.h" #define CMD_LEN 2048 int print_null(const char *s,...) {return 0;} int (*info)(const char *fmt,...) = &printf; void read_sparse_instance(const SparseMatrix &args, int index, struct svm_node *x) { int i, j, low, high; octave_idx_type *ir, *jc; double *samples; ir = (octave_idx_type*)args.ridx(); jc = (octave_idx_type*)args.cidx(); samples = (double*)args.data(); // each column is one instance j = 0; low = (int)jc[index], high = (int)jc[index+1]; for(i=low;iparam.kernel_type == PRECOMPUTED) { // precomputed kernel requires dense matrix, so we make one t_data = args(1).matrix_value(); } else { //If it's a sparse matrix with a non PRECOMPUTED kernel, transpose it pplhs = args(1).sparse_matrix_value().transpose(); } } else { t_data = args(1).matrix_value(); } ptr_instance = (double*)t_data.data(); if(predict_probability) { if(svm_type==NU_SVR || svm_type==EPSILON_SVR) { info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma=%g\n", svm_get_svr_probability(model)); } else { prob_estimates = (double*) malloc(nr_class*sizeof(double)); } } ColumnVector cv_predictions(testing_instance_number); tplhs(0) = cv_predictions; if(predict_probability) { // prob estimates are in plhs[2] if(svm_type == C_SVC || svm_type == NU_SVC) { Matrix m_pe(testing_instance_number, nr_class); tplhs(2) = m_pe; } else { Matrix m_pe(0,0); tplhs(2) = m_pe; } } else { // decision values are in plhs[2] if(svm_type == ONE_CLASS || svm_type == EPSILON_SVR || svm_type == NU_SVR || nr_class == 1) { Matrix m_pe(testing_instance_number, 1); tplhs(2) = m_pe; } else { Matrix m_pe(testing_instance_number, nr_class*(nr_class-1)/2); tplhs(2) = m_pe; } } ptr_predict_label = (double*)tplhs(0).column_vector_value().data(); ptr_prob_estimates = (double*)tplhs(2).matrix_value().data(); ptr_dec_values = (double*)tplhs(2).matrix_value().data(); x = (struct svm_node*)malloc((feature_number+1)*sizeof(struct svm_node)); for(instance_index=0;instance_indexparam.kernel_type != PRECOMPUTED) { read_sparse_instance(pplhs, instance_index, x); } else { for(i = 0; i < feature_number; i++) { x[i].index = i+1; x[i].value = ptr_instance[testing_instance_number*i+instance_index]; } x[feature_number].index = -1; } if(predict_probability) { if(svm_type == C_SVC || svm_type == NU_SVC) { predict_label = svm_predict_probability(model, x, prob_estimates); ptr_predict_label[instance_index] = predict_label; for(i = 0; i < nr_class; i++) { ptr_prob_estimates[instance_index + i * testing_instance_number] = prob_estimates[i]; } } else { predict_label = svm_predict(model,x); ptr_predict_label[instance_index] = predict_label; } } else { if(svm_type == ONE_CLASS || svm_type == EPSILON_SVR || svm_type == NU_SVR) { double res; predict_label = svm_predict_values(model, x, &res); ptr_dec_values[instance_index] = res; } else { double *dec_values = (double *) malloc(sizeof(double) * nr_class*(nr_class-1)/2); predict_label = svm_predict_values(model, x, dec_values); if(nr_class == 1) { ptr_dec_values[instance_index] = 1; } else { for(i = 0; i < (nr_class * (nr_class - 1)) / 2; i++) { ptr_dec_values[instance_index + i * testing_instance_number] = dec_values[i]; } } free(dec_values); } ptr_predict_label[instance_index] = predict_label; } if(predict_label == target_label) { ++correct; } error += (predict_label-target_label)*(predict_label-target_label); sump += predict_label; sumt += target_label; sumpp += predict_label*predict_label; sumtt += target_label*target_label; sumpt += predict_label*target_label; ++total; } if(svm_type==NU_SVR || svm_type==EPSILON_SVR) { info("Mean squared error = %g (regression)\n",error/total); info("Squared correlation coefficient = %g (regression)\n", ((total*sumpt-sump*sumt)*(total*sumpt-sump*sumt))/ ((total*sumpp-sump*sump)*(total*sumtt-sumt*sumt))); } else { info("Accuracy = %g%% (%d/%d) (classification)\n", (double)correct/total*100,correct,total); } // return accuracy, mean squared error, squared correlation coefficient ColumnVector cv_acc(3); ptr = (double*)cv_acc.data(); ptr[0] = (double)correct/total*100; ptr[1] = error/total; ptr[2] = ((total*sumpt-sump*sumt)*(total*sumpt-sump*sumt))/ ((total*sumpp-sump*sump)*(total*sumtt-sumt*sumt)); tplhs(1) = cv_acc; free(x); if(prob_estimates != NULL) { free(prob_estimates); } switch(nlhs) { case 3: plhs(2) = tplhs(2); plhs(1) = tplhs(1); case 1: case 0: plhs(0) = tplhs(0); } } DEFUN_DLD (svmpredict, args, nargout, "-*- texinfo -*- \n\n\ @deftypefn {statistics} {@var{predicted_label} =} svmpredict (@var{labels}, @var{data}, @var{model})\n\ @deftypefnx {statistics} {@var{predicted_label} =} svmpredict (@var{labels}, @var{data}, @var{model}, ""libsvm_options"")\n\ @deftypefnx {statistics} {[@var{predicted_label}, @var{accuracy}, @var{decision_values}] =} svmpredict (@var{labels}, @var{data}, @var{model}, ""libsvm_options"")\n\ @deftypefnx {statistics} {[@var{predicted_label}, @var{accuracy}, @var{prob_estimates}] =} svmpredict (@var{labels}, @var{data}, @var{model}, ""libsvm_options"")\n\ \n\ \n\ This function predicts new labels from a testing instance matrtix based on an \ SVM @var{model} created with @code{svmtrain}. \ \n\ \n\ @itemize \n\ @item @var{labels} : An m by 1 vector of prediction labels. If labels \ of test data are unknown, simply use any random values. (type must be double) \ \n\ \n\ @item @var{data} : An m by n matrix of m testing instances with n features. \ It can be dense or sparse. (type must be double) \ \n\ \n\ @item @var{model} : The output of @code{svmtrain} function. \ \n\ \n\ @item @code{libsvm_options} : A string of testing options in the same format \ as that of LIBSVM. \ \n\ \n\ @end itemize \ \n\ \n\ @code{libsvm_options} :\n\ \n\ @itemize \n\ @item @code{-b} : probability_estimates; whether to predict probability \ estimates. For one-class SVM only 0 is supported.\n\ \n\ @end itemize \ \n\ @multitable @columnfractions 0.1 0.1 0.8 \n\ @item @tab 0 @tab return decision values. (default) \n\ \n\ @item @tab 1 @tab return probability estimates. \ \n\ @end multitable \ \n\ \n\ @itemize \n\ @item @code{-q} : quiet mode. (no outputs) \ \n\ @end itemize \ \n\ \n\ The @code{svmpredict} function has three outputs. The first one, \ @var{predicted_label}, is a vector of predicted labels. The second output, \ @var{accuracy}, is a vector including accuracy (for classification), mean \ squared error, and squared correlation coefficient (for regression). The \ third is a matrix containing decision values or probability estimates \ (if @code{-b 1}' is specified). If @math{k} is the number of classes in \ training data, for decision values, each row includes results of predicting \ @math{k(k-1)/2} binary-class SVMs. For classification, @math{k = 1} is a \ special case. Decision value +1 is returned for each testing instance, \ instead of an empty vector. For probabilities, each row contains @math{k} \ values indicating the probability that the testing instance is in each class. \ Note that the order of classes here is the same as @code{Label} field in the \ @var{model} structure. \ \n\ @end deftypefn") { int nlhs = nargout; int nrhs = args.length(); octave_value_list plhs(nlhs); int prob_estimate_flag = 0; struct svm_model *model; info = &print_null; if(nlhs == 2 || nlhs > 3) { error ("svmpredict: wrong number of output arguments."); } if(nrhs > 4 || nrhs < 3) { error ("svmpredict: wrong number of input arguments."); } if(!args(0).is_double_type() || !args(1).is_double_type()) { error ("svmpredict: label vector and instance matrix must be double."); } if(args(2).isstruct()) { const char *error_msg; // parse options if(nrhs==4) { int i, argc = 1; char cmd[CMD_LEN], *argv[CMD_LEN/2]; // put options in argv[] strncpy(cmd, args(3).string_value().c_str(), CMD_LEN); if((argv[argc] = strtok(cmd, " ")) != NULL) { while((argv[++argc] = strtok(NULL, " ")) != NULL); } for(i=1;i=argc) && argv[i-1][1] != 'q') { fake_answer(nlhs, plhs); return plhs; } switch(argv[i-1][1]) { case 'b': prob_estimate_flag = atoi(argv[i]); break; case 'q': i--; info = &print_null; break; default: printf("svmpredict: unknown option: -%c\n", argv[i-1][1]); fake_answer(nlhs, plhs); return plhs; } } } octave_scalar_map osm_model = args(2).scalar_map_value(); model = octave_matrix_to_model(osm_model, &error_msg); if (model == NULL) { printf("svmpredict: can't read model: %s\n", error_msg); fake_answer(nlhs, plhs); return plhs; } if(prob_estimate_flag) { if(svm_check_probability_model(model)==0) { svm_free_and_destroy_model(&model); error ("svmpredict: model does not support probabiliy estimates.\n"); } } else { if(svm_check_probability_model(model)!=0) info("Model supports probability estimates, but disabled in prediction.\n"); } predict(nlhs, plhs, args, model, prob_estimate_flag); // destroy model svm_free_and_destroy_model(&model); } else { error ("svmpredict: model should be a struct array."); } return plhs; } /* %!test %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %! model = svmtrain (L, D, '-c 1 -g 0.07'); %! [predict_label, accuracy, dec_values] = svmpredict (L, D, model); %! assert (size (predict_label), size (dec_values)); %! assert (accuracy, [86.666, 0.533, 0.533]', [1e-3, 1e-3, 1e-3]'); %! assert (dec_values(1), 1.225836001973273, 1e-14); %! assert (dec_values(2), -0.3212992933043805, 1e-14); %! assert (predict_label(1), 1); %!shared L, D, model %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %! model = svmtrain (L, D, '-c 1 -g 0.07'); %!error ... %! [p, a] = svmpredict (L, D, model); %!error p = svmpredict (L, D); %!error ... %! p = svmpredict (single (L), D, model); %!error p = svmpredict (L, D, 123); */ statistics-release-1.7.3/src/svmtrain.cc000066400000000000000000000366471475240274700203270ustar00rootroot00000000000000/* Copyright (C) 2022 Andreas Bertsatos Based on the Octave LIBSVM wrapper adapted by Alan Meeson (2014) based on an earlier version of the LIBSVM (3.18) library for MATLAB. Current implementation is based on LIBSVM 3.25 (2021) by Chih-Chung Chang and Chih-Jen Lin. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "svm.h" #include "svm_model_octave.h" #define CMD_LEN 2048 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) void print_null(const char *s) {} //void print_string_octave(const char *s) {&printf;} int print_null_(const char *s,...) {return 0;} int (*info_)(const char *fmt,...) = &printf; // svm arguments struct svm_parameter param; // set by parse_command_line struct svm_problem prob; // set by read_problem struct svm_model *model; struct svm_node *x_space; int cross_validation; int nr_fold; double do_cross_validation() { int i; int total_correct = 0; double total_error = 0; double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0; double *target = Malloc(double,prob.l); double retval = 0.0; svm_cross_validation(&prob,¶m,nr_fold,target); if(param.svm_type == EPSILON_SVR || param.svm_type == NU_SVR) { for(i=0;i 2) { // put options in argv[] strncpy(cmd, args(2).string_value().c_str(), CMD_LEN); //mxGetString(args[2], cmd, mxGetN(args[2]) + 1); if((argv[argc] = strtok(cmd, " ")) != NULL) while((argv[++argc] = strtok(NULL, " ")) != NULL) ; } // parse options for(i=1;i=argc && argv[i-1][1] != 'q') // since option -q has no parameter return 1; switch(argv[i-1][1]) { case 's': param.svm_type = atoi(argv[i]); break; case 't': param.kernel_type = atoi(argv[i]); break; case 'd': param.degree = atoi(argv[i]); break; case 'g': param.gamma = atof(argv[i]); break; case 'r': param.coef0 = atof(argv[i]); break; case 'n': param.nu = atof(argv[i]); break; case 'm': param.cache_size = atof(argv[i]); break; case 'c': param.C = atof(argv[i]); break; case 'e': param.eps = atof(argv[i]); break; case 'p': param.p = atof(argv[i]); break; case 'h': param.shrinking = atoi(argv[i]); break; case 'b': param.probability = atoi(argv[i]); break; case 'q': print_func = &print_null; info_ = &print_null_; i--; break; case 'v': cross_validation = 1; nr_fold = atoi(argv[i]); if(nr_fold < 2) { printf("n-fold cross validation: n must >= 2\n"); return 1; } break; case 'w': ++param.nr_weight; param.weight_label = (int *)realloc(param.weight_label,sizeof(int)*param.nr_weight); param.weight = (double *)realloc(param.weight,sizeof(double)*param.nr_weight); param.weight_label[param.nr_weight-1] = atoi(&argv[i-1][2]); param.weight[param.nr_weight-1] = atof(argv[i]); break; default: printf("svmtrain: unknown option -%c\n", argv[i-1][1]); return 1; } } svm_set_print_string_function(print_func); return 0; } // read in a problem (in svmlight format) int read_problem_dense(ColumnVector &label_vec, Matrix &instance_mat) { int i, j, k; int elements, max_index, sc, label_vector_row_num; double *samples, *labels; prob.x = NULL; prob.y = NULL; x_space = NULL; labels = (double*)label_vec.data();//mxGetPr(label_vec); samples = (double*)instance_mat.data(); sc = (int)instance_mat.cols(); elements = 0; // the number of instance prob.l = (int)instance_mat.rows(); label_vector_row_num = (int)label_vec.rows(); if(label_vector_row_num!=prob.l) { printf("svmtrain: length of label vector does not match # of instances.\n"); return -1; } if(param.kernel_type == PRECOMPUTED) elements = prob.l * (sc + 1); else { for(i = 0; i < prob.l; i++) { for(k = 0; k < sc; k++) if(samples[k * prob.l + i] != 0) elements++; // count the '-1' element elements++; } } prob.y = Malloc(double,prob.l); prob.x = Malloc(struct svm_node *,prob.l); x_space = Malloc(struct svm_node, elements); max_index = sc; j = 0; for(i = 0; i < prob.l; i++) { prob.x[i] = &x_space[j]; prob.y[i] = labels[i]; for(k = 0; k < sc; k++) { if(param.kernel_type == PRECOMPUTED || samples[k * prob.l + i] != 0) { x_space[j].index = k + 1; x_space[j].value = samples[k * prob.l + i]; j++; } } x_space[j++].index = -1; } if(param.gamma == 0 && max_index > 0) param.gamma = 1.0/max_index; if(param.kernel_type == PRECOMPUTED) for(i=0;i max_index) { printf("svmtrain: wrong input format: sample_serial_number out of range\n"); return -1; } } return 0; } int read_problem_sparse(ColumnVector &label_vec, SparseMatrix &instance_mat) { int i, j, k, low, high; octave_idx_type *ir, *jc; int elements, max_index, num_samples, label_vector_row_num; double *samples, *labels; // transposed instance sparse matrix SparseMatrix instance_mat_col = instance_mat.transpose(); prob.x = NULL; prob.y = NULL; x_space = NULL; // each column is one instance labels = (double*)label_vec.data(); samples = (double*)instance_mat_col.data(); ir = (octave_idx_type*)instance_mat_col.ridx(); jc = (octave_idx_type*)instance_mat_col.cidx(); num_samples = (int)instance_mat_col.nzmax(); // the number of instance prob.l = (int)instance_mat_col.cols(); label_vector_row_num = (int)label_vec.rows(); if(label_vector_row_num!=prob.l) { printf("svmtrain: length of label vector does not match # of instances.\n"); return -1; } elements = num_samples + prob.l; max_index = (int)instance_mat_col.rows(); prob.y = Malloc(double,prob.l); prob.x = Malloc(struct svm_node *,prob.l); x_space = Malloc(struct svm_node, elements); j = 0; for(i=0;i 0) { param.gamma = 1.0/max_index; } return 0; } static void fake_answer(int nlhs, octave_value_list &plhs) { int i; for(i=0;i 1) { error ("svmtrain: wrong number of output arguments."); } // Transform the input Matrix to libsvm format if(nrhs > 1 && nrhs < 4) { int err; if(!args(0).is_double_type() || !args(1).is_double_type()) { error ("svmtrain: label vector and instance matrix must be double."); } if(parse_command_line(nrhs, args, NULL)) { svm_destroy_param(¶m); error ("svmtrain: wrong values in parameter string."); } if(args(1).issparse()) { if(param.kernel_type == PRECOMPUTED) { // precomputed kernel requires dense matrix, so we make one ColumnVector cv_lab = args(0).column_vector_value(); Matrix m_dat = args(1).matrix_value(); err = read_problem_dense(cv_lab, m_dat); } else { ColumnVector cv_lab = args(0).column_vector_value(); SparseMatrix m_dat = args(1).sparse_matrix_value(); err = read_problem_sparse(cv_lab, m_dat); } } else { ColumnVector cv_lab = args(0).column_vector_value(); Matrix m_dat = args(1).matrix_value(); err = read_problem_dense(cv_lab, m_dat); } // svmtrain's original code error_msg = svm_check_parameter(&prob, ¶m); if(err || error_msg) { if (error_msg != NULL) { printf("svmtrain: %s\n", error_msg); } svm_destroy_param(¶m); free(prob.y); free(prob.x); free(x_space); fake_answer(nlhs, plhs); return plhs; } if(cross_validation) { double ptr = do_cross_validation(); plhs(0) = octave_value(ptr); } else { int nr_feat = (int)args(1).matrix_value().cols(); const char *error_msg; model = svm_train(&prob, ¶m); error_msg = model_to_octave_structure(plhs, nr_feat, model); if(error_msg) { printf("svmtrain: can't convert libsvm model to matrix structure: %s\n", error_msg); } svm_free_and_destroy_model(&model); } svm_destroy_param(¶m); free(prob.y); free(prob.x); free(x_space); return plhs; } else { error ("svmtrain: wrong number of input arguments."); } } /* %!test %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %! model = svmtrain(L, D, '-c 1 -g 0.07'); %! [predict_label, accuracy, dec_values] = svmpredict(L, D, model); %! assert (isstruct (model), true); %! assert (isfield (model, "Parameters"), true); %! assert (model.totalSV, 130); %! assert (model.nr_class, 2); %! assert (size (model.Label), [2, 1]); %!shared L, D %! [L, D] = libsvmread (file_in_loadpath ("heart_scale.dat")); %!error [L, D] = svmtrain (L, D); %!error ... %! model = svmtrain (single (L), D); %!error ... %! model = svmtrain (L, D, "", ""); */