Showing posts with label mathematica. Show all posts
Showing posts with label mathematica. Show all posts

Thursday, February 4, 2016

Wolfram Language Attribute: OneIdentity

Attributes modify the behavior of entire classes of functions. Some Attributes cause functions to have well-known mathematical properties such as commutativity, associativity, or idempotency, notably for valid pattern-matching. Others control the way the Wolfram Language evaluates or does not evaluate a function's arguments, which if you study that you will see is at the very core of Wolfram Language operation.

Listable is a valuable functional language Attribute since it incorporates Thread into a function so it automatically maps over a List and instead of taking a List apart, we apply a function to it as a gestalt.

NumericFunction is an Attribute of all Wolfram Language mathematical functions and is True when all of their arguments are numerical. I suspect it is integral to the interaction of many functions with the underlying knowledge base of Mathematica and therefore to many functions' valid behavior (e.g. Piecewise – always define piecewise mathematical functions with Piecewise).

Attribute: OneIdentity


OneIdentity is the mathematical property of idempotency, which dictates that applying a function any number of times to its argument (i.e. Nest) returns the argument unchanged. Examples are adding zero to any number, or multiplying any number by one, or raising any number to 1st or 0th power (except 0 - someone explain that inconsistency to me!).

0^0

During evaluation of In[549]:= Power::indet: Indeterminate expression 0^0 encountered. >>

Out[549]= Indeterminate

The Doc Center says that OneIdentity is necessary for pattern-matching to work with the default of Times, Plus, and Power, which is a period. I.e., x_+ y_. => x + 0, x_ * y_. => x * 1, and x_^y_. => x^1.

MatchQ[x, Times[n_. , x_]]

True

You can see that OneIdentity removes nested Heads. It says to the evaluator, 'If the given pattern x_ matches any of these patterns, return True.'

a+b+c/.x_:>u/;Print@FullForm[Plus[x]//HoldForm]

HoldForm[Plus[Plus[a,b,c]]]
HoldForm[Plus[Plus]]
HoldForm[Plus[a]]
HoldForm[Plus[b]]
HoldForm[Plus[c]]

a+b+c

Here are related posts on Flat and Orderless. This post shows how to reveal all built-in functions with a given Attribute, categorized by the Attribute.

This is an excellent out-of-print book but there is plenty to learn from it. I'd compare it to David Wagner's Power Programming in Mathematica in its insight to how the Wolfram Language works.

Bahder, Thomas B. Mathematica for Scientists and Engineers. Reading, MA: Addison-Wesley. 1994.




Get new posts by email:

Wolfram Language Attribute: Orderless

Attributes modify the behavior of entire classes of functions. Some Attributes cause functions to have well-known mathematical properties such as commutativity, associativity, or idempotency, notably for valid pattern-matching. Others control the way the Wolfram Language evaluates or does not evaluate a function's arguments, which if you study that you will see is at the very core of Wolfram Language operation.

Listable is a valuable functional language Attribute since it incorporates Thread into a function so it automatically maps over a List and instead of taking a List apart, we apply a function to it as a gestalt.

NumericFunction is an Attribute of all WL mathematical functions and is True when all of their arguments are numerical. I suspect it is integral to the interaction of many functions with the underlying knowledge base of Mathematica and therefore to many functions' valid behavior (e.g. Piecewise  – always define piecewise mathematical functions with Piecewise).

Attribute: Orderless


Thomas Bahder uses a neat trick to show how Attributes work and how some affect pattern-matching. Here is the trick revealing the mechanism of Orderless, which is equivalent to the mathematical commutative property, that is, x + y = y + x.



Bahder uses a Condition that will never be True, which causes WL to try all permutations of the function's arguments that are valid under Orderless. In the false predicate he inserts a Print statement to reveal how the WL evaluator works. HoldForm is necessary to stop the evaluator from putting the arguments into standard order before printing them.

Orderless says to the Wolfram Language evaluator, 'If given pattern matches any of these patterns, return True.'

a+b+c/.x_+y_+z_:>u/;Print@FullForm[Plus[x,y,z]//HoldForm]

HoldForm[Plus[a,b,c]]
HoldForm[Plus[a,c,b]]
HoldForm[Plus[b,a,c]]
HoldForm[Plus[b,c,a]]
HoldForm[Plus[c,a,b]]
HoldForm[Plus[c,b,a]]

a+b+c

Here are related posts on Flat and OneIdentity. This post lists all Wolfram Language functions that have any Attribute except Protected.

This is an excellent out-of-print book but there is plenty to learn from it. I'd compare it to David Wagner's Power Programming in Mathematica in its insight to how the Wolfram Language works. 


Bahder, Thomas B. Mathematica for Scientists and Engineers. Reading, MA: Addison-Wesley. 1994.


Sunday, October 28, 2012

File Operations: Deleting Files


Delete one or more files

DeleteFile deletes one file, or more files if they are given in a List. If you are deleting more than one file, typically you'd first use FileNames to acquire those files from a directory. DeleteFile returns Null (i.e. nothing) if it works and $Failed if it doesn't. To delete a few files in the current directory it's as simple as this.

FileNames["test*", "C:\\Users\\82V\\Documents"] // DeleteFile

Here is a more complicated example in which I delete 3500 files in multiple directories.


Acquire FileNames and Delete Files in all subdirectories

The usual sequence of steps goes like this. Set the directory in which you want to search for files. In Windows, I usually use Windows Explorer to navigate to the directory, then click in the address bar to select it, Ctrl+C to copy it, and paste it after a quote mark in Mathematica, which says to me:



In[114]:= SetDirectory@
  "C:\\Users\\82V\\Documents\\Neuroscience\\Arle-Shils\\commandUNCuS\\\
DataFiles";

Search for all the files using a wildcard and store their names in a variable (datFiles). Search in all subdirectories within your directory by using a wildcard for subdirectory names in the optional second argument, and Infinity for depth of subdirectories in the optional third argument.

In[97]:= datFiles = FileNames["*.dat", {"*"}, Infinity];

See how many filenames you captured.

In[98]:= datFiles // Length

Out[98]= 3594

Take a quick peek at the first five files to be deleted to make sure you're capturing the right ones.

In[99]:= datFiles[[1 ;; 5]]

Out[99]= {"Short_Test_File\\tc125_101.dat", "Short_Test_File\\tc125_101pre.dat", \
"Short_Test_File\\tc125_102.dat", "Short_Test_File\\tc125_102pre.dat", \
"Short_Test_File\\tc125_103.dat"}

Delete all the files to be deleted.

In[100]:= DeleteFile@datFiles

Reset the directory to whatever it was before you set it to the one in which to search for files.

In[112]:= Directory[]

Out[112]= "C:\\Users\\82V\\Documents\\Neuroscience\\Arle-Shils\\commandUNCuS\\DataFiles"

In[113]:= ResetDirectory[]

Out[113]= "C:\\Users\\82V\\Documents"

Note that if you set the directory n times to delete files in each, you need to repeat ResetDirectory[] that many times to get it back to where it was, since each time you set a directory it pushes the previous ones down a stack (which you can reveal using DirectoryStack[]), and each time you use ResetDirectory[] it pops the directories back up the stack. You can use a Do loop on ResetDirectory[] n times to do this. Using the Print statement will show you the pop sequence of directories you have visited. I prefaced the command with Directory[] to show the current directory first.

In[117]:= Directory[]; Do[Print@ResetDirectory[], {3}]

C:\Users\82V\Documents\commandUNCuS

C:\Users\82V\Documents\Neuroscience\Arle-Shils\commandUNCuS\DataFiles

C:\Users\82V\Documents


Wednesday, October 3, 2012

How It Works: DeleteDuplicates


Delete Duplicates

While Union is commonly used to select all unique elements from a List, including a set of Lists, DeleteDuplicates is commonly used to select unique elements from a single List, which Union can do, too. Union sorts the result, while DeleteDuplicates leaves the result in its original order. Consequently, DeleteDuplicates is a faster function if you do not need the Sort. Both functions include an optional second argument to specify the function used to remove duplicates, which greatly increases their power and versatility. First, here is DeleteDuplicates' basic functionality.

DeleteDuplicates@{c,a,b,d,a,c,a,e,e,a,a,e}

{c,a,b,d,e}

Note that if you do feed DeleteDuplicates a set of Lists, you do need to enclose the Lists in curly brackets.

DeleteDuplicates[{c,a,b},{d,a,c},{a,e},{e,a},{a,e}]

DeleteDuplicates::argb: DeleteDuplicates called with 5 arguments; between 1 and 3 arguments are expected. >>

DeleteDuplicates[{{c,a,b},{d,a,c},{a,e},{e,a},{a,e}}]

{{c,a,b},{d,a,c},{a,e},{e,a}}


You can use DeleteDuplicates' second argument to increase its breadth by specifying how it will detect the duplicates. So in the example above, by default neither Union nor DeleteDuplicates treats Lists with the same elements as equivalent, as would be the case in set theory, while this can be done with their sameness test.


It is relatively straightforward to construct the second argument if you keep in mind that the default is DeleteDuplicates[expr, SameQ] and therefore extensions of the function can take the form DeleteDuplicates[expr, f@#~SameQ~f#2&], where the comparison function f can be as complex as you wish. Here we need Sort because:

{a,b}~SameQ~{b,a}

False

DeleteDuplicates[{{c,a,b},{d,a,c},{a,e},{e,a},{a,e}},Sort@#~SameQ~Sort@#2&]

{{c,a,b},{d,a,c},{a,e}}

Here is a second, neat example from the Doc Center. Extending the power of DeleteDuplicates, this function uses Equal instead of SameQ, possibly since Equal will yield True for Reals and non-Reals. I've modified the example to show that.

5==5.

True

5===5.

False

list1 = {{0,0,0,1,0},{1,0,1,0,1},{1.,1.,1.,0.,0.},{0,0,0,0,1},{1,1,1,0,1}};

DeleteDuplicates[list1,Total@#==Total@#2&]

{{0,0,0,1,0},{1,0,1,0,1},{1,1,1,0,1}}

Here is a potential issue that limits the power of DeleteDuplicates. Trace tells us that the second 4 gets removed before it can be compared to the 16.

squaresList=Table[{x,x^2},{x,2,4}]//Flatten

{2,4,3,9,4,16}

DeleteDuplicates[squaresList,#2==#^2&]//Trace
{{squaresList,{2,4,3,9,4,16}},DeleteDuplicates[{2,4,3,9,4,16},#2==#1^2&],{(#2==#1^2&)[2,4],4==2^2,{2^2,4},4==4,True},{(#2==#1^2&)[2,3],3==2^2,{2^2,4},3==4,False},{(#2==#1^2&)[2,9],9==2^2,{2^2,4},9==4,False},{(#2==#1^2&)[2,4],4==2^2,{2^2,4},4==4,True},{(#2==#1^2&)[2,16],16==2^2,{2^2,4},16==4,False},{(#2==#1^2&)[3,9],9==3^2,{3^2,9},9==9,True},{(#2==#1^2&)[3,4],4==3^2,{3^2,9},4==9,False},{(#2==#1^2&)[3,16],16==3^2,{3^2,9},16==9,False},{2,3,16}}

We're asking DeleteDuplicates to do something beyond deleting duplicates. We should use DeleteCases to do this job.

DeleteCases[squaresList,x_/;(Sqrt@x//IntegerQ)]

{2,3}

How It Works

Somewhere Roman Maeder gives a solution to deleting duplicates and implies that his solution is efficient. From memory here is the solution (with my more efficient syntax). We create a simple list of duplicate integers.

dupeList = Table[{i, i}, {i, 10}] // Flatten

{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10}

We partition them into sets of two with offset 1 (meaning overlap of one on the original List).

dupeListPartitioned2Offset1 = Partition[dupeList, 2, 1]

{{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}, {3, 4}, {4, 4}, {4, 5}, {5, 5}, {5, 6}, {6, 6}, {6, 7}, {7, 7}, {7, 8}, {8, 8}, {8, 9}, {9, 9}, {9, 10}, {10, 10}}

Now it's a simple matter to Select the sets where Part 1 is not the same as Part 2. Select always takes a predicate, sometimes of the form testQ, but here with an abbreviated operator, UnsameQ. Unequal would work for numerical entries, but UnsameQ will also work for symbols and Strings.

dupeListdupeSetsDeleted = Select[dupeListPartitioned2Offset1, #[[1]] =!= #[[2]] &]

{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {9, 10}}

We're still left with duplicates. So we take the First entry of each set, and Append the Last entry of the Last set, which would have been left out. I remember thinking at this point that I would have spent time trying to not 'hack' this last part--somehow capture that last entry without another operation--but if it's good enough for Maeder, it's good enough for me. The lesson is to do what is expedient and move on to the next task.

Append[First /@ dupeListdupeSetsDeleted, Last@Last@dupeListdupeSetsDeleted]

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

DeleteDuplicates

Sunday, April 15, 2012

Cyclically Selecting Parts Using Mod with Offset = 1


The Doc Center notes this usage of the offset feature in Mod to cyclically select Parts. Setting offset to 1 means Part will cycle from Part #1 through the value of n instead of the expression itself (i.e. Part #0) tbrough n - 1. However I have not seen a useful application of this idiom.

In[1]:= {a, b, c}[[Mod[Range[10], 3, 1]]]


Out[1]= {a, b, c, a, b, c, a, b, c, a}

Saturday, March 24, 2012

RecurrenceTable - Introduction


RecurrenceTable defines a sequence by setting f[n+1] equal to some function of f[n], and an initial condition for f[1]. Recurrence relations are also called difference equations and the method of finite differences uses recurrence relations. These are discrete versions of differential calculus. See the excellent articles in Wolfram Mathworld.

In our computer simulations of neural circuits, while the equations governing the neurons' behavior are phrased as differential equations, in reality we compute them as difference equations. For one thing we add noise to them, and for another, in practice they contain singularities. The "forward Euler method" et cetera won't work. I suspect that many prima facie differential equations are, in practice, unsolvable, and mask an underlying reality actually described by difference equations.

Here is a comparison between Table and RecurrenceTable that simply shows how to use the syntax of RecurrenceTable.

RecurrenceTable

In[21]:= powersOfOneHalf = Table[2^-i, {i, 1, 25}] // N

Out[21]= {0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625, 0.0078125, 0.00390625, \
0.00195313, 0.000976563, 0.000488281, 0.000244141, 0.00012207, 0.0000610352, \
0.0000305176, 0.0000152588, 7.62939*10^-6, 3.8147*10^-6, 1.90735*10^-6,
 9.53674*10^-7, 4.76837*10^-7, 2.38419*10^-7, 1.19209*10^-7, 5.96046*10^-8,
 2.98023*10^-8}

In[23]:= RecurrenceTable[{a[n + 1] == .5 a@n, a@1 == 0.5}, a, {n, 25}]

Out[23]= {0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625, 0.0078125, 0.00390625, \
0.00195313, 0.000976563, 0.000488281, 0.000244141, 0.00012207, 0.0000610352, \
0.0000305176, 0.0000152588, 7.62939*10^-6, 3.8147*10^-6, 1.90735*10^-6,
 9.53674*10^-7, 4.76837*10^-7, 2.38419*10^-7, 1.19209*10^-7, 5.96046*10^-8,
 2.98023*10^-8}

We can define f[n] in terms of more than one preceding term. If so, we need to supply the initial conditions for as many terms as specify the recurrence relations. Here is a Fibonacci-type series, a different flavor defined using three preceding terms rather than two. The initial conditions can also be thought of as boundary conditions.

In[4]:= RecurrenceTable[{a[n] == a[n - 1] + a[n - 2] + a[n - 3], a[1] == 1, a[2] == 1,
   a@3 == 1}, a,
   {n, 10}]

Out[4]= {1, 1, 1, 3, 5, 9, 17, 31, 57, 105}

Saturday, March 10, 2012

RecurrenceTable and NestList


RecurrenceTable and NestList do the same thing—build a list of values by repeatedly applying a function to an initial expression. However NestList is the more general function and RecurrenceTable is specialized to handle recurrence relations, and the syntax of each reflects this. Here is a simple comparison. See the examples in the Doc Center for RecurrenceTable for more sophisticated examples.

In[1]:= NestList[Sqrt, 100.0, 5]

Out[1]= {100., 10., 3.16228, 1.77828, 1.33352, 1.15478}

In[2]:= NestList[#^2 &, Last@%, 5]

Out[2]= {1.15478, 1.33352, 1.77828, 3.16228, 10., 100.}

In[3]:= RecurrenceTable[{a[n + 1] == Sqrt@a@n, a[1] == 100}, a, {n, 1, 5}] // N

Out[3]= {100., 10., 3.16228, 1.77828, 1.33352}

In[4]:= RecurrenceTable[{a[n + 1] == a[n]^2, a[1] == Last@%}, a, {n, 1, 5}]

Out[4]= {1.33352, 1.77828, 3.16228, 10., 100.}

Sunday, February 12, 2012

Physical Constants


PhysicalConstants

The Physical Constants package pre-dates most of the curated data and, not being in the same format as the latter, is accessed differently.

In[284]:= << PhysicalConstants`

In[285]:= Names["PhysicalConstants`*"]

Out[285]= {"AccelerationDueToGravity", "AgeOfUniverse", "AvogadroConstant", \
"BohrRadius", "BoltzmannConstant", "ClassicalElectronRadius", \
"CosmicBackgroundTemperature", "DeuteronMagneticMoment", "DeuteronMass", \
"EarthMass", "EarthRadius", "ElectronCharge", "ElectronComptonWavelength", \
"ElectronGFactor", "ElectronMagneticMoment", "ElectronMass", \
"FaradayConstant", "FineStructureConstant", "GalacticUnit", \
"GravitationalConstant", "HubbleConstant", "IcePoint", "MagneticFluxQuantum", \
"MolarGasConstant", "MolarVolume", "MuonGFactor", "MuonMagneticMoment", \
"MuonMass", "NeutronComptonWavelength", "NeutronMagneticMoment", \
"NeutronMass", "PlanckConstant", "PlanckConstantReduced", "PlanckMass", \
"ProtonComptonWavelength", "ProtonMagneticMoment", "ProtonMass", \
"QuantizedHallConductance", "RydbergConstant", "SackurTetrodeConstant", \
"SolarConstant", "SolarLuminosity", "SolarRadius", \
"SolarSchwarzschildRadius", "SpeedOfLight", "SpeedOfSound", "StefanConstant", \
"ThomsonCrossSection", "VacuumPermeability", "VacuumPermittivity", \
"WeakMixingAngle"}

In[286]:= AccelerationDueToGravity

Out[286]= (196133 Meter)/(20000 Second^2)


This will generate a 3-column list of the constants and their values. Note the particular syntax of Partition, which is used to handle remnants at the end of a partitioned List. I explain this under Partition.

Table[{i, ToExpression@i}, {i, Names@"PhysicalConstants`*"}] //
  Partition[#, 3, 3, 1, {}] & // TableForm

And this will generate a list of the explanations of all constants. Note that the list is not in a form that can be manipulated, such as put into a more compact table, without some machinations of which I'm ignorant.

Information@# & /@ Names@"PhysicalConstants`*"

Finally, for this blog, here is a compact two-column table of the constants.


In[287]:= Thread[{Names["PhysicalConstants`*"],
   ToExpression /@ Names["PhysicalConstants`*"]}] // TableForm

Mathematica Table of Physical Constants


Element Data


Again we typically list the Properties available for the curated data collection.

ElementData@"Properties"

{"Abbreviation", "AbsoluteBoilingPoint", "AbsoluteMeltingPoint", \
"AdiabaticIndex", "AllotropeNames", "AllotropicMultiplicities", \
"AlternateNames", "AlternateStandardNames", "AtomicNumber", "AtomicRadius", \
"AtomicWeight", "Block", "BoilingPoint", "BrinellHardness", "BulkModulus", \
"CASNumber", "Color", "CommonCompoundNames", "CovalentRadius", \
"CriticalPressure", "CriticalTemperature", "CrustAbundance", \
"CrystalStructure", "CuriePoint", "DecayMode", "Density", \
"DiscoveryCountries", "DiscoveryYear", "ElectricalConductivity", \
"ElectricalType", "ElectronAffinity", "ElectronConfiguration", \
"ElectronConfigurationString", "Electronegativity", \
"ElectronShellConfiguration", "FusionHeat", "GasAtomicMultiplicities", \
"Group", "HalfLife", "HumanAbundance", "IconColor", "IonizationEnergies", \
"IsotopeAbundances", "KnownIsotopes", "LatticeAngles", "LatticeConstants", \
"Lifetime", "LiquidDensity", "MagneticType", "MassMagneticSusceptibility", \
"MeltingPoint", "Memberships", "MeteoriteAbundance", "MohsHardness", \
"MolarMagneticSusceptibility", "MolarVolume", "Name", "NeelPoint", \
"NeutronCrossSection", "NeutronMassAbsorption", "OceanAbundance", "Period", \
"Phase", "PoissonRatio", "QuantumNumbers", "Radioactive", "RefractiveIndex", \
"Resistivity", "ShearModulus", "SolarAbundance", "SoundSpeed", \
"SpaceGroupName", "SpaceGroupNumber", "SpecificHeat", "StableIsotopes", \
"StandardName", "SuperconductingPoint", "ThermalConductivity", \
"ThermalExpansion", "UniverseAbundance", "Valence", "VanDerWaalsRadius", \
"VaporizationHeat", "VickersHardness", "VolumeMagneticSusceptibility", \
"YoungModulus"}

There are often, but not always, annotations explaining the meaning of a given data property or class.

In[104]:= ElementData["Hydrogen", "ElectronShellConfiguration", "LongDescription"]

Out[104]= "occupation numbers for each complete energy shell"

Here is the complete table. We use a Table listing the properties we want to extract from the database and display that iterates over the atomic numbers from 1 to 118. Then we need to Flatten the List for each element so that the last field, ElectronShellConfiguration, displays the number of electrons in each shell in a row instead of a column. Compare this method to those used for AstronomicalData and PhysicalConstants.

In[283]:= Table[{ElementData[z, "AtomicNumber"], ElementData[z, "Abbreviation"],
    ElementData[z, "Name"], ElementData[z, "Series"],
    ElementData[z, "AtomicWeight"], ElementData[z, "MeltingPoint"],
    ElementData[z, "BoilingPoint"],
    ElementData[z, "ElectronShellConfiguration"]}, {z, 118}] //
  Flatten /@ # & //
 TableForm[#,
   TableHeadings -> {{}, {"Number", "Symbol", "Name", "Group",
      "Atomic Weight", "Melting Point", "Boiling Point",
      "Electron Shell\nConfiguration"}}] &


Astronomical Data


A recent extrapolation on the number of Earth-like planets is here with an exemplar here: Kepler 452b, just 1400 light years away in the constellation Cygnus. See also "The comet-like composition of a protoplanetary disk as revealed by complex cyanides."

There is a typical procedure to explore a curated data collection and extract what we need from it. For most of the curated data collections, we can find out what categories of information are available by calling the Properties field.

In[105]:= AstronomicalData@"Properties"

Out[105]= {"AbsoluteMagnitude", "AbsoluteMagnitudeH", "Albedo", "AlphanumericName", \
"AlternateNames", "AlternateStandardNames", "Altitude", "Apoapsis", \
"ApparentMagnitude", "AscendingNodeLongitude", "Azimuth", "BayerName", \
"BayerStandardName", "BlueBandMagnitude", "BVColorIndex", "Classes", \
"Constellation", "ConstellationName", "Declination", "Density", "Diameter", \
"DiscoveryYear", "Distance", "DistanceLightYears", "DurchmusterungName", \
"DurchmusterungStandardName", "EarthMOID", "Eccentricity", \
"EffectiveTemperature", "EquatorialDiameter", "EquatorialRadius", \
"EscapeVelocity", "FlamsteedName", "FlamsteedStandardName", "GalaxyType", \
"GlieseName", "GlieseStandardName", "GlobularClusterType", "Gravity", \
"HDName", "HDNumber", "HipparcosName", "HipparcosNumber", "HRName", \
"HRNumber", "HubbleType", "IAUName", "IAUNumber", "ICNumber", "Image", \
"Inclination", "LastRiseTime", "LastSetTime", "Luminosity", "Mass", \
"MeanMotion", "Name", "NextRiseTime", "NextSetTime", "NGCNumber", \
"ObjectType", "Oblateness", "Obliquity", "OrbitCenter", "OrbitPath", \
"OrbitPeriod", "OrbitPeriodYears", "OrbitRules", "Parallax", "Periapsis", \
"PeriapsisArgument", "PeriapsisLongitude", "PerihelionTime", "PGCNumber", \
"PolarDiameter", "PolarRadius", "Position", "PositionLightYears", \
"PrimaryDesignation", "PrimaryStarKSemiamplitude", "ProvisionalDesignation", \
"RadialVelocity", "Radius", "Redshift", "RightAscension", "RotationPeriod", \
"SAOName", "SAONumber", "Satellites", "SemimajorAxis", "SMASSSpectralType", \
"SpectralClass", "SpectralClassList", "Speed", "StandardName", \
"SurfaceBrightness", "TholenSpectralType", "VisualBandMagnitude"}

In[106]:= % // Length

Out[106]= 98

So there are currently 98 fields of information available for AstronomicalData. Next, note that you can list all objects in the collection by omitting any parameter.

In[107]:= {AstronomicalData[] // Short, AstronomicalData[] // Length}


You can see why I used Short--there are 156,134 astronomical objects listed in the database. In any of the collections we typically would work with a subset. There are often logical subsets built-in. In AstronomicalData they are called Classes, while in, for example, ElementData there are Classes but other categorizations there could be called classes as well.

In[121]:= AstronomicalData@"Classes"

Out[121]= {"AmorAsteroid", "ApolloAsteroid", "AtenAsteroid", "BarredSpiralGalaxy", \
"BayerObject", "BrightGiant", "BrightHIIRegion", "CarbonStar", \
"CentaurAsteroid", "ChironTypeComet", "ClassAStar", "ClassBStar", \
"ClassCStar", "ClassFStar", "ClassGStar", "ClassKStar", "ClassMStar", \
"ClassNStar", "ClassOStar", "ClassRStar", "ClassSStar", "Comet", \
"DeepSkyObject", "DwarfEllipticalGalaxy", "DwarfPlanet", \
"DwarfSpheroidalGalaxy", "EllipticalGalaxy", "EnckeTypeComet", "Exoplanet", \
"FlamsteedObject", "Galaxy", "GlieseObject", "GlobularCluster", \
"HalleyTypeComet", "HDObject", "HipparcosObject", "HRObject", \
"HyperbolicComet", "ICObject", "InnerMainBeltAsteroid", "IrregularGalaxy", \
"JupiterFamilyComet", "LocalGroup", "MainBeltAsteroid", "MainSequenceStar", \
"MarsCrossingAsteroid", "MessierObject", "MinorPlanet", "MultipleStar", \
"NakedEyeStar", "NearEarthAsteroid", "Nebula", "NGCObject", "NormalGiant", \
"OpenCluster", "OuterMainBeltAsteroid", "ParabolicComet", "PGCObject", \
"Planet", "PlanetaryMoon", "PlanetaryNebula", "Plutoid", "SAOObject", \
"SpiralGalaxy", "Star", "StarBrightest10", "StarBrightest100", \
"StarNearest10", "StarNearest100", "Subdwarf", "Subgiant", "Supergiant", \
"TransNeptunianObject", "TrojanAsteroid", "WhiteDwarfStar", "WolfRayetStar"}

In[108]:= brightest100Stars = AstronomicalData@"StarBrightest100"

Out[108]= {"Sun", "Sirius", "Canopus", "Arcturus", "RigelKentaurusA", "Vega", \
"Capella", "Rigel", "Procyon", "Achernar", "Betelgeuse", "Hadar", "Altair", \
"Acrux", "Aldebaran", "CapellaAb", "Spica", "Antares", "Pollux", "Fomalhaut", \
"Mimosa", "Deneb", "RigelKentaurusB", "Regulus", "Adhara", "Castor", \
"Gacrux", "Shaula", "Bellatrix", "Alnath", "Miaplacidus", "Alnilam", \
"Alnair", "Alnitak", "Regor", "Alioth", "Mirphak", "KausAustralis", "Dubhe", \
"Wezen", "Alkaid", "Avior", "ThetaScorpii", "Menkalinan", "Atria", "Alhena", \
"DeltaVelorum", "Peacock", "Polaris", "Mirzam", "Alphard", "Hamal", \
"Algieba", "Diphda", "Nunki", "Menkent", "Alpheratz", "Mirach", "Saiph", \
"Kochab", "BetaGruis", "Rasalhague", "Algol", "Almach", "Denebola", "Tsih", \
"GammaCentauri", "Naos", "Aspidiske", "Alphekka", "Alsuhail", "Mizar", \
"Sadr", "Shedir", "Etamin", "Mintaka", "Caph", "EpsilonCentauri", "Dschubba", \
"EpsilonScorpii", "AlphaLupi", "EtaCentauri", "Merak", "Izar", "Enif", \
"KappaScorpii", "Ankaa", "Phad", "Sabik", "Scheat", "Aludra", "Alderamin", \
"KappaVelorum", "EpsilonCygni", "Markab", "Menkar", "ZetaOphiuchi", \
"ZetaCentauri", "Zosma", "Acrab"}

I see many old friends from my early teens, when I spent cold nights with ideal still, less-stratified air gazing through a 6-inch reflector. Note, too, the plethora of Arabic names, attesting to the Muslim renaissance, which is the more significant flowstream running from the Greeks and their great predecessors to our modern culture than the story told by the typical Western-biased version of the history of science. But getting back to the matter at hand, extracting a single piece of information about a single object in the database uses the simple syntax of a descriptor for the object, such as "Sun", and a descriptor of the field, such as "AbsoluteMagnitude", both Strings.

In[130]:= AstronomicalData["Sun", "AbsoluteMagnitude"]

Out[130]= 4.84

While I can think of logical and compact syntaxes that are consistent within Mathematica to retrieve a set of objects and related fields from the database, such as AstronomicalData[{1, 100}, "ApparentMagnitude"], or AstronomicalData["StarBrightest100", "ApparentMagnitude"], we must use a less user - friendly form to extract the fields. A cumbersome syntax for this basic operation is common to all the curated databases that I know, and the syntax is also not uniform across them. Compare the following methods with the examples for ElementData and PhysicalConstants. Note again all fields are described by Strings.

In[109]:= AstronomicalData[#, "ApparentMagnitude"] & /@ brightest100Stars

Out[109]= {-26.72, -1.44, -0.62, -0.05, -0.01, 0.03, 0.08, 0.18, 0.4, 0.45, 0.45, 0.61, \
0.76, 0.77, 0.87, 0.96, 0.98, 1.06, 1.16, 1.17, 1.25, 1.25, 1.35, 1.36, 1.5, \
1.58, 1.59, 1.62, 1.64, 1.65, 1.67, 1.69, 1.73, 1.74, 1.75, 1.76, 1.79, 1.79, \
1.81, 1.83, 1.85, 1.86, 1.86, 1.9, 1.91, 1.93, 1.93, 1.94, 1.97, 1.98, 1.99, \
2.01, 2.01, 2.04, 2.05, 2.06, 2.07, 2.07, 2.07, 2.07, 2.07, 2.07, 2.09, 2.1, \
2.14, 2.15, 2.2, 2.21, 2.21, 2.22, 2.23, 2.23, 2.23, 2.24, 2.24, 2.25, 2.28, \
2.29, 2.29, 2.29, 2.3, 2.33, 2.34, 2.35, 2.38, 2.39, 2.4, 2.40, 2.43, 2.44, \
2.45, 2.45, 2.47, 2.48, 2.49, 2.54, 2.54, 2.55, 2.57, 2.56}

An alternate method to using Map is to use Table, and if we want to compile information or display a set of fields, that is how we would do it. Note the very nifty ability of Table to iterate over an arbitrary symbolic List, in this case the List of 100 brightest stars, and not just a numerical iterator.

In[118]:= Table[AstronomicalData[i, "DistanceLightYears"], {i, brightest100Stars}]

Out[118]= {0.0000158, 8.60, 3.1*10^2, 36.7, 4.39, 25.3, 42.2, 8.7*10^2, 11.4, 1.4*10^2,
 6.4*10^2, 398., 16.8, 3.*10^2, 65.1, 42.2, 2.5*10^2, 6.1*10^2, 33.7, 25.1,
 2.9*10^2, 1.55*10^3, 4.39, 77.5, 404., 51.5, 87.9, 701., 243., 131., 111.,
 1.34*10^3, 101., 7.8*10^2, 1.14*10^3, 80.9, 592., 145., 124.,
 1.79*10^3, 101., 632., 272., 82.1, 415., 105., 79.7, 183., 431., 499., 177., \
65.9, 126., 95.8, 224., 60.9, 97.0, 199., 723., 127., 170., 46.7, 92.8, 355., \
36.2, 613., 130., 1.09*10^3, 692., 74.7, 573., 77.9, 1.52*10^3, 228., 148.,
 7.2*10^2, 54.4, 376., 401., 65.4, 548., 308., 79.4, 209., 672., 464., 77.4, \
83.5, 83.9, 199.,
 3.20*10^3, 48.8, 537., 72.0, 139., 220., 365., 384., 57.7, 530.}

One of the most interesting discoveries of this epoch is the disclosure of planets surrounding stars. This discovery drastically affects the Drake equation, simple enough but elegant and pointed in conception, which calculates the number of planets bearing life and intelligent life. As Paul Horowitz pointed out years ago, if we assume the speed of light as a limiting factor governing the coincidence of an intelligent species' communication, and then consider the duration from the inception of a species' technical ability to communicate across space (e.g. radio) to their loss of desire to communicate due to the singularity of accelerated artificial intelligence (machine or genetic), there may not be many interactions between intelligent species. As of the last few months, however, extrapolating from current data, the number of planets that are capable of bearing life may be orders of magnitude greater than estimated until now. However it seems unlikely that the span from inception of interstellar communication, even inadvertent such as Marconi's invention of radio, to the advent of AI, is any different from what we currently envision, perhaps 300 years, a narrow window.

In[132]:= AstronomicalData@"Planet"

Out[132]= {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", \
"Neptune"}

No extra-solar planets ("exoplanets") here. Hmm, for that matter, how is Pluto categorized? Mathematica does have some syntactical elegance built-in for searching the list of all objects, but to search a class, we need Cases.

In[162]:= AstronomicalData@"*Plu*"

Out[162]= {"Pluto", "Plutarchos", "Plucker", "Pluis"}

In[143]:= AstronomicalData@"TransNeptunianObject" // Cases[#, "Pluto"] &

Out[143]= {"Pluto"}

It appears Mathematica has a separate class of extra - solar planet ("exoplanet"), a little out of date, judging by the number.

In[151]:= AstronomicalData@"Exoplanet" // Length

Out[151]= 209

So let's put together a table of information about the nearest stars and include properties that may relate to their likelihood of hosting a life-supporting planet.

In[122]:= ourNeighbors = AstronomicalData["StarNearest100"]

Out[122]= {"Sun", "ProximaCentauri", "RigelKentaurusA", "RigelKentaurusB", \
"BarnardsStar", "Wolf359", "Lalande21185", "Luyten726-8A", "Luyten726-8B", \
"Sirius", "SiriusB", "HIP92403", "Gl905", "EpsilonEridani", "Lacaille9352", \
"HIP57548", "Gl866A", "HIP104214", "Procyon", "Gl280B", "HIP104217", \
"HIP91772", "HIP91768", "HIP1475", "Gl15B", "GJ1111", "EpsilonIndi", \
"TauCeti", "HIP5643", "LuytensStar", "KapteynsStar", "Lacaille8760", \
"Kruger60", "Gl860B", "HIP30920", "Gl234B", "HIP72511", "HIP80824", "GJ1061", \
"Gl473A", "Gl473B", "HIP439", "HIP15689", "VanMaanensStar", "NN3522", \
"Gl83.1", "NN3618", "HIP72509", "NN3622", "HIP86162", "HIP85523", \
"HIP114110", "HIP57367", "GJ1002", "HIP113020", "GJ1245A", "GJ1245B", \
"HIP54211", "Gl412B", "Groombridge1618", "Gl388", "HIP82725", "HIP85605", \
"HIP106440", "HIP86214", "Omicron2Eridani", "Gl166B", "Gl166C", "HIP112460", \
"HIP88601", "Gl702B", "Altair", "HIP1242", "GJ1116A", "GJ1116B", "NN3379", \
"HIP57544", "HIP67155", "HIP103039", "HIP21088", "Gl169.1B", "HIP33226", \
"HIP53020", "HIP25878", "Gl754", "HIP82817", "Gl644B", "Gl644C", "Alsaphi", \
"HIP29295", "HIP26857", "HIP86990", "HIP94761", "Gl752B", "Gl300", \
"HIP73184", "HIP37766", "HIP76074", "Achird", "Gl34B"}

In[124]:= Table[AstronomicalData[i, "DistanceLightYears"], {i, ourNeighbors}]

Out[124]= {0.0000158, 4.22, 4.39, 4.39, 5.94, 7.79, 8.31, 8.56, 8.56, 8.60, 8.60, 9.69, \
10.3, 10.5, 10.7, 10.9, 11.1, 11.4, 11.4, 11.4, 11.4, 11.5, 11.6, 11.6, 11.6, \
11.8, 11.8, 11.9, 12.1, 12.4, 12.8, 12.9, 13.1, 13.1, 13.4, 13.4, 13.9, 13.9, \
14.0, 14.0, 14.0, 14.2, 14.3, 14.4, 14.6, 14.6, 14.6, 14.7, 14.8, 14.8, 14.8, \
15.1, 15.1, 15.3, 15.3, 15.4, 15.4, 15.8, 15.8, 15.9, 16.0, 16.1, 16.1, 16.1, \
16.4, 16.4, 16.4, 16.4, 16.4, 16.6, 16.6, 16.8, 17.0, 17.0, 17.0, 17.5, 17.6, \
17.7, 17.9, 18.0, 18.0, 18.0, 18.4, 18.6, 18.6, 18.7, 18.7, 18.7, 18.8, 18.8, \
18.9, 18.9, 19.1, 19.1, 19.2, 19.3, 19.3, 19.3, 19.4, 19.4}

To extract more fields, we use Table to iterate over the class of objects ourNeighbors and within it, Map (/@) the template for extracting a single field over a List of fields. We show just the first two to note that this method uses Transpose to group the properties of each object together. Again, compare to the method used with ElementData. Here are the apparent magnitude and absolute magnitude of the Sun and Sirius first grouped by property then with Transpose by object.

Table[AstronomicalData[i, #], {i,
    ourNeighbors[[1 ;; 2]]}] & /@ {"ApparentMagnitude", "AbsoluteMagnitude"}

Out[145]= {{-26.72, 11.01}, {4.84, 15.4}}


And here is the entire Table. I built it using the first three records so I could see what was going on as I added fields and functionality. Note that all these stars are less than 20 light years from us.

In[149]:= Sort[{{b, c}, {a, d}, {z, a}}]

Out[149]= {{a, d}, {b, c}, {z, a}}

In[282]:= Table[AstronomicalData[i, #], {i, ourNeighbors}] & /@ {"Name",
     "DistanceLightYears", "SpectralClass", "Constellation",
     "ApparentMagnitude", "AbsoluteMagnitude", "NakedEyeStar",
     "RightAscension", "Declination", "Diameter"} // Transpose //
  Sort[#, #[[2]] < #2[[2]] &] & //
 TableForm[#,
   TableHeadings -> {{}, {"Name", "Distance in\nLightYears",
      "Spectral\nClass", "Constellation", "Apparent\nMagnitude",
      "Absolute\nMagnitude", "NakedEye\nStar", "Right\nAscension",
      "Declination", "Diameter"}}] &And here is the entire Table. I built it using the first three records so I could see what was going on as I added fields and functionality. Note that all these stars are less than 20 light years from us.



Curated Data


On March 11, 1985, at a talk given at Harvard University, I heard Stephen Wolfram say that he intended what was then his precursor to Mathematica, SMP (Symbolic Manipulation Program), to contain the core of mathematical knowledge, and a library of additional files, 400 at that time, to greatly extend SMP's mathematical knowledge. Some years later, I heard him muse about how big a project it would be to include a core of scientific knowledge in Mathematica. WRI's curated data collection is the growing realization of that vision.

The curated data is extensive and there is a typical procedure to explore most of the collections. Here are several examples.

Sort, SortBy, Ordering, OrderedQ


This small family of functions is essential to programming in Mathematica. Indeed, sorting has been central to computer programming and algorithm analysis since programming's inception in the late 1950s.

See posts for:

Sort, SortBy, Ordering, OrderedQ

OrderingQ


OrderingQ

OrderingQ is the predicate of the family, which by default returns True of False if a List (or List of arguments of any function) is in "canonical" order, defined as lowest numerical or symbolic value to highest.

In[269]:= numericalList = Range@12 // RandomSample[#, 12] &

Out[269]= {11, 2, 5, 9, 1, 3, 8, 7, 4, 12, 10, 6}

In[270]:= OrderedQ@numericalList

Out[270]= False

Sort by default sorts from low to high.

In[271]:= Sort@numericalList

Out[271]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}

In[272]:= OrderedQ@%

Out[272]= True

In[273]:= alphaList = CharacterRange["a", "z"] // RandomSample[#, 12] &

Out[273]= {"l", "d", "f", "x", "o", "z", "i", "y", "w", "s", "m", "g"}

In[274]:= OrderedQ@alphaList

Out[274]= False

Again, Sort by default sorts from low to high, technically using the ASCII code for the characters.

In[275]:= Sort@alphaList

Out[275]= {"d", "f", "g", "i", "l", "m", "o", "s", "w", "x", "y", "z"}

In[276]:= OrderedQ@%

Out[276]= True

In[277]:= ToCharacterCode@alphaList

Out[277]= {{108}, {100}, {102}, {120}, {111}, {122}, {105}, {121}, {119}, {115}, {109}, \
{103}}

In[278]:= Sort@%

Out[278]= {{100}, {102}, {103}, {105}, {108}, {109}, {111}, {115}, {119}, {120}, {121}, \
{122}}

We can give OrderedQ our own ordering function, as complex as we like, to decide if the List is ordered. In this example we simply override OrderedQ's default, which is normally in accord with Sort's of ordering from small to large, and ask OrderedQ if the List is ordered from large to small.

In[281]:= OrderedQ[Sort@numericalList, Greater]

Out[281]= False

[b]

Ordering


Ordering reads out the order of the elements of a List if they were sorted. So in the Table below, the first row is the list itself, the second row shows the list sorted, and the third row shows the Ordering of the list: the eighth element (1) would be first, the third element (2) would be second, the fifth element (3) would be third, the 4th element would be last, etc. Incidentally I use SeedRandom here so that if you evaluate the code you will get the same random sample as I got.

In[208]:= SeedRandom@135; aList = RandomSample[Range@10, 10]

Out[208]= {7, 5, 2, 10, 3, 6, 4, 1, 9, 8}

In[209]:= {aList, Sort@aList, Ordering@aList} //
 TableForm[#, TableHeadings -> {{"aList", "Sort", "Ordering"}, {}}] &





What are the Positions of the largest two elements in aList? Use this syntax:

In[210]:= Ordering[aList, {-2, -1}]

Out[210]= {9, 4}

SortBy


Let's say we want to Sort a List as if a function were applied to the List but return the original elements of the List. That is what distinguishes SortBy from Sort. For example, say you want to sort 10 rolls of a die 3 times by the number of 6s that come up.

In[265]:= rollADie = Table[RandomChoice[Range@6, 3], {10}]

Out[265]= {{3, 5, 4}, {2, 1, 5}, {6, 3, 4}, {6, 6, 6}, {5, 2, 2}, {1, 3, 1}, {5, 4,
  4}, {2, 4, 1}, {2, 2, 3}, {4, 1, 4}}

In[266]:= SortBy[rollADie, Count[#, 6] &] // Reverse

Out[266]= {{6, 6, 6}, {6, 3, 4}, {5, 4, 4}, {5, 2, 2}, {4, 1, 4}, {3, 5, 4}, {2, 4,
  1}, {2, 2, 3}, {2, 1, 5}, {1, 3, 1}}

A way to think about SortBy is to consider that it can only "see" the result of applying the function to the list elements and Sorts by that result. For instance, we can write more compact expressions for sorting by one List position than with Sort. Here SortBy only sees the 2nd element of each List and Sorts by it (in ascending order since that is Sort's default).

In[267]:= SortBy[rollADie, #[[2]] &]

Out[267]= {{2, 1, 5}, {4, 1, 4}, {2, 2, 3}, {5, 2, 2}, {1, 3, 1}, {6, 3, 4}, {2, 4,
  1}, {5, 4, 4}, {3, 5, 4}, {6, 6, 6}}

SortBy can take more than one sorting function and applies them in order to break ties. In this sense SortBy can be used like successive sorts in a database or program like Microsoft Excel used as a database.

In[268]:= SortBy[rollADie, {#[[2]] &, Last}]

Out[268]= {{4, 1, 4}, {2, 1, 5}, {5, 2, 2}, {2, 2, 3}, {1, 3, 1}, {6, 3, 4}, {2, 4,
  1}, {5, 4, 4}, {3, 5, 4}, {6, 6, 6}}

 SortBy[e,f] is equivalent to Sort[{f[#],#}&/@e][[All,-1]].

Last, the Doc Center unveils the function underlying SortBy: SortBy[e,f]is equivalent to Sort[{f[#],#}&/@e][[All,-1]]. Translated into English, this says "make a List of the function f applied to each argument and that argument, then Sort by the first element of the List (i.e. the function applied to the argument), then only return the 2nd element of each List (i.e., the original List elements, but now re-ordered by the Sort.

Sort


Sort is simple in concept: It sorts a List according to a function that dictates the ordering. The default ordering function is smaller to larger (the function Less).

In[216]:= SeedRandom@345; aList1 = RandomInteger[100, 10]

Out[216]= {84, 52, 80, 41, 53, 24, 22, 0, 47, 42}

In[218]:= {Sort@aList1, Sort[aList1, Less]} // TableForm





We can specify the ordering function that Sort uses.

In[219]:= Sort[aList1, Greater]

Out[219]= {84, 80, 53, 52, 47, 42, 41, 24, 22, 0}

The ordering function always takes two arguments and compares them. Here we Sort by the second element of each sublist.

In[222]:= Sort[{{a, 2, 85}, {c, 1, 0.61}, {d, 3, 5^5}}, #[[2]] < #2[[2]] &]

Out[222]= {{c, 1, 0.61}, {a, 2, 85}, {d, 3, 3125}}

Whatever the algorithmic magic used by Sort to do its job efficiently, note that Slot1 picks out the first element to compare to the second element, which is picked out by Slot2. Here I show a Slot3 as well.

{#1[[2]], #2[[2]], #3[[2]]} & @@ {{a, 2}, {c, 1}, {d, 3}}

{2, 1, 3}

So under the hood Sort must start by doing something like applying the ordering function as a predicate, but then in architecture using the latest, greatest sorting algorithm from the computational complexity literature.

#1[[2]] < #2[[2]] &[{a, 2}, {c, 1}]

False

As holds throughout Mathematica, Sort works on symbolic arguments. If the symbols had been assigned values, of course those values would be used.

In[228]:= Clear[d, b, c, a]; Sort@{d, b, c, a}

Out[228]= {a, b, c, d}

In[229]:= a = 7; b = 2;

Sort@{d, b, c, a}

Out[230]= {2, 7, c, d}

Monday, January 2, 2012

Predicates, Tests, and Test Patterns: How to Build Your Own

Build Your Own: User-defined Examples

Xiangdong Wen of Tech Support noted that the hard part of constructing Predicates is considering all the possible inputs and exceptions. To do a thorough job, we might need to do a Piecewise definition of a Predicate, or define a Predicate with Which, and if desired leave an error message as the final condition at the bottom to catch any exceptions that escaped us.

Limit a Built-in Predicate: lessThanOneQ

lessThanOneQ@x_ := If[ x1 < 1, True, False]

testSet = Range[0, 1.5, .3]

{0., 0.3, 0.6, 0.9, 1.2, 1.5}

lessThanOneQ /@ testSet

{True, True, True, True, False, False}

Test for Specified Length: lengthTwoQ

Clear@lengthTwoQ; lengthTwoQ@x_ := If[Length@x == 2, True, False]

lengthTwoQ /@ {{x, y}, {y, x}, {x, y, z}}

{True, True, False}

Test for Meeting Boolean Combination of Conditions: BinaryQ

By setting the Attributes of our Predicate to Listable, we shortcut the need to use Map over a List.

ClearAll@BinaryQ; SetAttributes[BinaryQ, Listable];
BinaryQ@x_ := If[x === 1 || x === 0, True, False ]

BinaryQ@{0, 3, 1, 1., 2}

{True, False, True, False, False}

One can also use Boolean And (&&) similarly, or any combination of And and Or in a compound Expression.

Test for Membership in a Set: integerLessThan10Q

Here we make a Predicate from a test for an element's membership in a defined set.

integerLessThan10 = Range@10

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

integerLessThan10Q@x_ := If[MemberQ[integerLessThan10, x], True, False]

integerLessThan10Q /@ {4, 4/5, Pi, 3, 11, 251/25}

{True, False, False, True, False, False}

More Examples: maxEqualQ, maxNearQ, valueAtQ, positionNearQ

Different Predicates can test for a variety of related conditions. These are functions I used to test properties of book bestseller lists but here I use random lists for illustration. The first one tests to see if the maximum value of two lists of numbers are the same.

maxEqualQ[x_List, y_List] := If[Max@x == Max@y, True, False]

list1 = RandomInteger[{0, 25}, 20] // Sort

{0, 2, 2, 4, 5, 6, 8, 8, 9, 9, 9, 11, 15, 16, 16, 17, 17, 19, 19, 19}

list2 = RandomInteger[{0, 25}, 20] // Sort

{1, 2, 5, 6, 6, 6, 9, 12, 13, 14, 17, 17, 18, 18, 23, 23, 24, 24, 25, 25}

maxEqualQ[list1, list2]

False

This tests to see if difference between the maximum of two lists is less than a specified magnitude.

maxNearQ[x_List, y_List, nearness_Integer] :=
 If[Abs[Max@x - Max@y] <= nearness, True, False]

maxNearQ[list1, list2, 10]

True

maxNearQ[list1, list2, 1]

False

This tests to see if the Position of the maxima of two lists is the same.

maxPositionEqualQ[x_List, y_List] :=
 If[Position[list1, Max@x] == Position[list2, Max@y], True, False]

maxPositionEqualQ[list1, list2]

False

Using Predicates in Select, Cases, and Related Filter Functions

As you look at these simple examples realize they are but a glimpse of the power of this type of usage that I mentioned at the start of this section. The Predicates, Conditions and Boolean conjunctions used in these filter functions can be arbitrarily complex, and as Mangone notes we are only restricted to anything that Mathematica can compute.

First we define a Predicate and use pure Function since those are idioms that Select uses, then we just use Condition in Cases, since that is the syntax that Cases uses. Consequently I recommend using the Q suffix when naming a Predicate used in Select. Note in the Cases example the additional restriction to Reals using a Head test.

Consider using Predicates in the related filter functions Count, Position, Tally, Gather, Sort, SortBy, etc.

Clear@lessThanOneQ; lessThanOneQ@x_ := If[ x < 1, True, False]

Select[Range[-0.2, 1.1, 0.2], lessThanOneQ]

{-0.2, 0., 0.2, 0.4, 0.6, 0.8}

Select[Range[-0.2, 1.1, 0.2], # < 1 &]

{-0.2, 0., 0.2, 0.4, 0.6, 0.8}

Function[x, x < 1] /@ Range[-0.2, 1.1, 0.2]

{True, True, True, True, True, True, False}

Cases[Range[-0.2, 1.1, 0.2], x_Real /; x < 1]

{-0.2, 0., 0.2, 0.4, 0.6, 0.8}

lessThanOnePositiveQ@x_ :=
 If[0 < x < 1 , True, False](* version restricted to positive numbers *)

lessThanOnePositiveQv2@x_ :=
 If[x < 1 && Positive@x, True, False] (* alternate version *)

Select[Range[-0.2, 1.1, 0.2], lessThanOnePositiveQ]

{0.2, 0.4, 0.6, 0.8}

Select[Range[-0.2, 1.1, 0.2], lessThanOnePositiveQv2]

{0.2, 0.4, 0.6, 0.8}

Predicate Using a Piecewise Function

Note the usage in the Named Pattern variable of a NumberQ predicate to allow both Reals and Integers. This example is intended to illustrate that a Piecewise-defined Predicate can include an unlimited number of orthogonal conditions.

Clear@pieceWiseElementQ; pieceWiseElementQ@number_?NumberQ :=
 Piecewise[{{False, number < -3}, { True, -3 < number < 0}, {False,
    0 <= number <= 6}, {True, number > 6}, {True, number === 4.5}}]

pieceWiseElementQ /@ {-3.00001, -0.0000000001, -4/5, 0, 0.000000001, 4/5, 4.5,
   6, 6.01, 9}

{False, True, True, False, False, False, False, False, True, True}

Predicates, Tests and Test Patterns: NumberQ, NumericQ, Equal, SameQ

NumberQ and NumericQ

NumberQ is more restrictive than NumericQ and seems to be True only if the evaluated expression is explicitly a number, while NumericQ tests for a "numeric quantity," which seems to mean that Mathematica tests each component to see if its fully evaluated form is a number. NumberQ probably also contains exceptions that should be considered "numeric," for instance all of the built-in numerical constants. More Predicates are implicit in built-in defined sets, such as Rationals and Reals (see below).

NumberQ@3.14159

True

NumberQ@Pi

False

NumericQ@Pi

True

NumberQ@Sqrt@2

False

Both Predicates will test a compound expression, but only NumericQ will test the fully evaluated form of each component. If any component evaluates False by NumberQ, the whole expression evaluates as False.

NumberQ[Sqrt@2 + 5]

False

NumericQ[Sqrt@2 + 5]

True

NumberQ@Sin@Sqrt@2

False

Since in the end the sine of the square root of 2 evaluates to a Real, NumericQ tests it as True; in effect it's testing 0.987766, while NumberQ cannot see the evaluated form.

Sin@Sqrt@2 // N

0.987766

NumericQ@Sin@Sqrt@2

True

Equal (==) and SameQ (===)

The distinction between these two Predicates can be confusing, but the basic idea is Equal tests for numeric equality while SameQ tests for syntactical equality. There are subtleties; here Mathematica compares Pi using its internal storage form.

N[Pi, 4] == N[Pi, 6]

True

However if we compare explicitly different numbers Equal will return False.

3.141 == 3.14159

False

SameQ will pick up syntactic differences that Equal misses.

Pi === N[Pi, 5]

False

Here is a nice example from a good introduction to Mathematica by Nancy Blachman and Colin Williams of using SameQ to test syntax in a paradigm for testing user input for a correctness (Mathematica, A Practical Introduction, 2nd ed., p 433, syntax modifications mine).

testUser1[question_, correctAnswer_] :=
 Module[{userAnswer = Input@question},
  If[userAnswer === correctAnswer, Print@"Correct!",
   Print["You said ", userAnswer, " but the correct answer is ",
    correctAnswer] ]
  ]


testUser1["What is Newton's second law of motion?", f = ma]

Correct!

It is easy to see how using Equal or StringMatchQ would give different tests for equality ("What is the square root of 2?") or an exact string match ("What is the largest continent?") in user input.

Part 3 will show how to build and use your own user-defined Predicates.

Predicates, Tests and Test Patterns: Introduction and Significance

The Importance of Predicates

In his excellent Mathematica Cookbook (p. 149), Sal Mangano said about using a predicate:

"This is a powerful construct because it extends the degree of control over the matching process to any criteria Mathematica can compute." 

Mangone's example used a predicate to further qualify a Pattern (see Pattern Assignment, in Assignment in this book), which alone is a powerful device, but his is a general comment about predicates in a language as flexible as Mathematica's and when Mathematica is considered as a technical computing universe containing, for instance, curated scientific and financial data, the ability to import web pages, and a universal representation ("everything is an expression") for diverse data such as graphics, sound, and images.

In logic, a predicate is a set whose elements can be evaluated as True or False, or more simply, a predicate is a test of something that is either True or False. They can be simple or complex. Predicates are very handy in Mathematica, since it is quite common to apply conditions to lists or expressions, or to test elements of lists or expressions. Predicates are an implicit If test implemented in an intuitively useful way. John Gray notes that predicates are also a way of constructing data types, and interestingly, that John von Neumann based a version of set theory on predicates (Mastering Mathematica, 2nd ed., p 242).

Most of Mathematica's built-in predicates have a "Q" as their suffix, signifying that they ask a question. They are discussed in guide/TestingExpressions. Predicates are designed to return True if they exactly match a pattern and False if there is no precise match.

A precise match means that built-in predicates have subtle differences, e.g. Equal and SameQ, NumberQ and NumericQ, VectorQ, MatrixQ and ArrayQ. And capturing what we wish to be True in our own predicates can require some tinkering (see this post).

Predicates without a Q in their name are listed here.

Let's look at Mathematica's built-in predicates , and then we'll make our own. The new-in-10 function Multicolumn produces a nicely-formatted table easier than using Partition and TableForm.

Names@"*Q" // Multicolumn[#,5]&



We'll take a look at how to use Mathematica's Predicates in Part 2.

Assignment to a Pattern


I'll use a couple of examples on searching Strings from Sal Mangano's excellent Mathematica Cookbook to show how to use Pattern Assignment. First, it's simple enough to search some Strings for those that are numbers:

StringMatchQ[{"12345", "2468", "abcde"}, NumberString]

{True, True, False}

But if you want to add a Predicate, you need another device such as naming a Pattern and using the name in the Predicate. This is an example of Pattern Assignment where the Pattern is named "m".

StringMatchQ[{"12345", "2468", "abcde"}, m : NumberString /; OddQ@FromDigits@m]

{True, False, False}

As Sal Mangone notes in his Mathematica Cookbook and I explore further in Predicates, Tests, and Test Patterns, adding a Predicate to a Pattern is a powerful device because we can include in the Predicate anything that Mathematica can compute. A Predicate can be a built-in one or one we construct, so the possibilities are truly limitless.