{"id":18908631,"url":"https://github.com/martinheinrich2/jpl-asteroids","last_synced_at":"2026-03-06T16:30:18.652Z","repository":{"id":191637417,"uuid":"344237287","full_name":"martinheinrich2/JPL-Asteroids","owner":"martinheinrich2","description":"Analysing the JPL Small-Body-Database with R/R-Studio","archived":false,"fork":false,"pushed_at":"2021-03-03T20:27:15.000Z","size":711,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-31T12:20:52.840Z","etag":null,"topics":["asteroids","r","r-studio"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martinheinrich2.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2021-03-03T19:13:54.000Z","updated_at":"2022-09-18T14:03:23.000Z","dependencies_parsed_at":"2023-08-30T21:47:50.020Z","dependency_job_id":"7d0aad3e-fe2f-427f-90c7-001799ab8f91","html_url":"https://github.com/martinheinrich2/JPL-Asteroids","commit_stats":null,"previous_names":["martinheinrich2/jpl-asteroids"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinheinrich2%2FJPL-Asteroids","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinheinrich2%2FJPL-Asteroids/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinheinrich2%2FJPL-Asteroids/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinheinrich2%2FJPL-Asteroids/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martinheinrich2","download_url":"https://codeload.github.com/martinheinrich2/JPL-Asteroids/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239895723,"owners_count":19714880,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asteroids","r","r-studio"],"created_at":"2024-11-08T09:27:35.116Z","updated_at":"2026-03-06T16:30:18.530Z","avatar_url":"https://github.com/martinheinrich2.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# 1. The JPL Small-Body Database\nThe JPL Small-Body Database is about small Solar System objects. It contains data of all known asteroids and several comets. \nYou can learn a bit by downloading and analysing the database. I used R (https://www.r-project.org) to filter and plot the data. Basic data and orbital elements of Asteroids can be found at the **JPL Small-Body Database Engine** (https://ssd.jpl.nasa.gov/sbdb_query.cgi#x).\nThe URL might change in the future. The JPL HORIZONS System should have a link to the small body database search engine.\n\nYou can select and download physical and orbital parameters of asteroids and comets. In this example only asteroids have been selected (asteroid - basic), (asteroid - physical), including Near-Earth Object (NEO) and Potentially Hazardous Asteroid (PHA). The resulting table was downloaded as a CSV-file of 157 MB, 796,926 rows and 27 columns. A description of the columns is in the appendix.\n\nTo analyse the data we need a few R packages. For data manipulation and filtering and plotting we use the tidyverse package.\nWe are dealing with a large amount of data, using the data.table package is faster than R's dataframe.\nIf this notebook is run from the same folder as our input data \"asteroid.csv\" then you don't need to set a folder. Otherwise use getwd() and setwd(\"/path/to/folder\") to read and set the working directory.\nFirst read the CSV file with the asteroid data.  Then create a data table with the semi-major axis and eccentricity of a few planets which we use in later plots. (https://nssdc.gsfc.nasa.gov/planetary/factsheet/planet_table_ratio.html)\n\n\n```r\nlibrary(data.table)\nlibrary(tidyverse)\n```\n\n```\n## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──\n```\n\n```\n## ✓ ggplot2 3.3.3     ✓ purrr   0.3.4\n## ✓ tibble  3.1.0     ✓ dplyr   1.0.4\n## ✓ tidyr   1.1.3     ✓ stringr 1.4.0\n## ✓ readr   1.4.0     ✓ forcats 0.5.1\n```\n\n```\n## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──\n## x dplyr::between()   masks data.table::between()\n## x dplyr::filter()    masks stats::filter()\n## x dplyr::first()     masks data.table::first()\n## x dplyr::lag()       masks stats::lag()\n## x dplyr::last()      masks data.table::last()\n## x purrr::transpose() masks data.table::transpose()\n```\n\n```r\nast_df \u003c- fread(\"asteroids.csv\", header = TRUE)\nplanets \u003c- data.table()\nplanets$names \u003c- c('Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter')\nplanets$a \u003c- c(0.387, 0.723, 1, 1.52, 5.20)\nplanets$e \u003c- c(0.205, 0.007, 0.017, 0.094, 0.049)\n```\nThere are a few warnings about conflicts which we ignore.\n\n# 2. Plot of eccentricity vs. semi-major axis\nLets try a first plot of eccentricity (e) versus semi-major axis (a) in AU. There are about 800,000 objects in the data.table and the code might take a while to run!\n\n```r\nggplot(data = ast_df) + geom_point(mapping = aes(x = a, y = e))\n```\n\n![](README_figs/README-first_plot-1.png)\u003c!-- --\u003e\n\nThere are two objects without a value of semi-major axis (a) and two with negative semi-major axis, \"(A/2017 U7)\" and \"'Oumuamua (A/2017 U1)\". We only want objects with positive semi-major axis. By applying a filter from the dplyr package we get exactly that. \n\n```r\nast_df \u003c- ast_df %\u003e% filter(a \u003e= 0)\n```\nNext we set the colour to steel-blue, use the option **shape** to get smaller data points in the plot, scale the x-axis logarithmic and add proper labels to the axis.\n\n```r\nggplot(data = ast_df) + geom_point(mapping = aes(x = a, y = e), shape = \".\", color = \"steelblue\") + \n  scale_x_log10() + labs (x = \"semi-major axis (a) in AU\", y = \"eccentricity (e)\")\n```\n\n![](README_figs/README-log-scale_plot1-1.png)\u003c!-- --\u003e\n\nThe x-axis is logarithmic in astronomical units (AU).\nNext we look at the range of 0 - 6 AU semi-major axis by applying a limit with the **xlim()** option.\n\n```r\nggplot(data = ast_df) + geom_point(mapping = aes(x = a, y = e), shape = \".\", color = \"steelblue\") + \n  xlim(c(0,6)) + labs (x = \"semi-major axis (a) in AU\", y = \"eccentricity (e)\")\n```\n\n![](README_figs/README-plot_e_vs_a-1.png)\u003c!-- --\u003e\n\nThere are some 'artefacts' which appear as striped patterns directed to the right. Some of them might be objects with only a few observations. The algorithm that calculates the orbital parameters could to make some assumptions which result in these striped patterns. We can test this idea and make them visible by selecting objects with less than 20 observations and plotting them in red.\n\n```r\nast_df20 \u003c- ast_df %\u003e% filter(n_obs_used \u003c 20)\nggplot(data = ast_df) + geom_point(mapping = aes(x = a, y = e), shape = \".\", color = \"steelblue\") + \n  geom_point(ast_df20, mapping = aes(x = a, y = e), shape = \".\", color = \"indianred\") + xlim(c(0,6)) +\n  labs (x = \"semi-major axis (a) in AU\", y = \"eccentricity (e)\")\n```\n\n![](README_figs/README-plot_artefacts-1.png)\u003c!-- --\u003e\n\nThis seems to prove the idea. Next we filter out potentially unreliable orbital parameters by removing all objects with less than 20 observations and making a new plot.\n\n\n```r\nast_df1 \u003c- ast_df %\u003e% filter(n_obs_used \u003e= 20 \u0026 a \u003e 0)\nggplot(data = ast_df1) + geom_point(mapping = aes(x = a, y = e), shape = \".\", color = \"steelblue\") + \n  xlim(c(0,6)) + labs (x = \"semi-major axis (a) in AU\", y = \"eccentricity (e)\")\n```\n\n![](README_figs/README-plot_without_artefacts-1.png)\u003c!-- --\u003e\n\nThere is still a striped pattern starting at 1 AU and 0 eccentricity directed to the right. This could be connected to how we detect asteroids. If asteroids are close to the Earth's orbit, we are more likely to detect them.\nWe can use the aphelion and perihelion (https://en.wikipedia.org/wiki/Apsis) data of Earth and other planets and calculate the eccentricity at each semi-major axis. In a plot we can examine the higher asteroid density starting at 1 AU and extending to 2 AU in the form of an arc. Remembering that $aphelion = a(1+e)$ and $perihelion = a(1-e)$ we rearrange to calculate the eccentricities for the planets. We also plot the eccentricities of Mercury, Venus, Earth, Mars and Jupiter.\n\n```r\na \u003c- seq(0.1, 20, 0.01)\n#Aphelion Earth using AU\neEarthQ \u003c- 1-(1.01668953984936/a)\n#Perihelion Earth using AU\neEarthq \u003c- (0.983308544860961/a)-1\n#Aphelion Mars using AU\neMarsQ \u003c- 1-(1.66615924573989/a)\n#Perihelion Mars using AU\neMarsq \u003c- (1.38121318598389/a)-1\n#Aphelion Jupiter using AU\neJupQ \u003c- 1-(5.45669201672052/a)\n#Perihelion Jupiter using AU\neJupq \u003c- (4.94785062331118/a)-1\nggplot() + geom_point(ast_df1, mapping = aes(x = a, y = e), shape = \".\", color = \"steelblue\") + \n  xlim(c(0,6)) + ylim(c(0,1)) + geom_line(mapping = aes(x = a, y = eEarthQ), color = \"red\") +\n  geom_line(mapping = aes(x = a, y = eEarthq), color = \"red\") +\n  geom_line(mapping = aes(x = a, y = eMarsQ), color = \"darkgreen\") +\n  geom_line(mapping = aes(x = a, y = eMarsq), color = \"darkgreen\") +\n  geom_line(mapping = aes(x = a, y = eJupQ), color = \"purple\") +\n  geom_line(mapping = aes(x = a, y = eJupq), color = \"purple\") +\n  geom_point(mapping = aes(x = planets$a, y = planets$e)) +\n  labs (x = \"semi-major axis (a) in AU\", y = \"eccentricity (e)\")\n```\n\n![](README_figs/README-aphelion_and_perihelion-1.png)\u003c!-- --\u003e\n\nNow it is obvious why the density of asteroids is higher close to the aphelion distance of the Earth (red line on the right). It is easier to detect asteroids near the Earth's orbit. The perihelion distance is the red line on the left. The other lines show the aphelion and perihelion distance of Mars (green) and Jupiter (purple). We also see objects crossing the aphelion and perihelion distance of planets.\nAsteroids in general have larger eccentricities than planets (even larger than Mercury = 0.205, also see https://nssdc.gsfc.nasa.gov/planetary/factsheet/). Collisions between asteroids and the Yarkowvsky effect can alter the orbit of asteroids in the long term (https://en.wikipedia.org/wiki/Yarkovsky_effect).\nAround Jupiter there are the Jupiter trojans lying at the L4 and L5 points (https://en.wikipedia.org/wiki/Jupiter_trojan). \n\nWe can also filter out the Near-Earth objects (NEO) and potentially hazardous objects (PHA) and use different colours for them.\n\n\n```r\nneo \u003c- ast_df1 %\u003e% filter(neo == \"Y\")\npha \u003c- ast_df1 %\u003e% filter(pha == \"Y\")\nprint(paste(\"Number of PHAs \", nrow(pha)))\n```\n\n```\n## [1] \"Number of PHAs  1966\"\n```\n\n```r\nprint(paste(\"Number of NEOs \", nrow(neo)))\n```\n\n```\n## [1] \"Number of NEOs  18206\"\n```\n\nNow lets plot them.\n\n\n```r\na \u003c- seq(0.1, 20, 0.01)\n#Aphelion Earth using AU\neEarthQ \u003c- 1-(1.01668953984936/a)\n#Perihelion Earth using AU\neEarthq \u003c- (0.983308544860961/a)-1\n#Aphelion Mars using AU\neMarsQ \u003c- 1-(1.66615924573989/a)\n#Perihelion Mars using AU\neMarsq \u003c- (1.38121318598389/a)-1\n#Aphelion Jupiter using AU\neJupQ \u003c- 1-(5.45669201672052/a)\n#Perihelion Jupiter using AU\neJupq \u003c- (4.94785062331118/a)-1\nggplot() + geom_point(ast_df1, mapping = aes(x = a, y = e), shape = \".\", color = \"green\") + \n  geom_point(neo, mapping = aes(x = a, y = e), shape = \".\", color = \"yellow\") +\n  geom_point(pha, mapping = aes(x = a, y = e), shape = \".\", color = \"red\") +\n  xlim(c(0,6)) + ylim(c(0,1)) + geom_line(mapping = aes(x = a, y = eEarthQ), color = \"red\") +\n  geom_line(mapping = aes(x = a, y = eEarthq), color = \"red\") +\n  geom_line(mapping = aes(x = a, y = eMarsQ), color = \"darkgreen\") +\n  geom_line(mapping = aes(x = a, y = eMarsq), color = \"darkgreen\") +\n  geom_line(mapping = aes(x = a, y = eJupQ), color = \"purple\") +\n  geom_line(mapping = aes(x = a, y = eJupq), color = \"purple\") +\n  geom_point(mapping = aes(x = planets$a, y = planets$e)) +\n  labs (x = \"semi-major axis (a) in AU\", y = \"eccentricity (e)\")\n```\n\n![](README_figs/README-neo_and_pha-1.png)\u003c!-- --\u003e\n\nPHAs are red, NEOs are yellow and all other asteroids are green.\n\n# 3. Kirkwood gaps\n\nLets explore the gaps by plotting a histogram for a distance up to 6 AU.\n\n```r\nggplot(ast_df1, aes(x = a)) + geom_histogram(aes(y = ..count..), binwidth = 0.005, color = \"blue\") + xlim(c(0,6)) +\n  labs (x = \"semi-major axis (a) in AU\", y = \"objects per bin (0.005 AU)\")\n```\n\n![](README_figs/README-histogram-1.png)\u003c!-- --\u003e\n\n```r\nggplot(ast_df1, aes(x = a)) + geom_histogram(aes(y = ..count..), binwidth = 0.005, color = \"blue\") + xlim(c(1.8,3.5)) +\n  labs (x = \"semi-major axis (a) in AU\", y = \"objects per bin (0.005 AU)\")\n```\n\n![](README_figs/README-Kirkwood_gaps-1.png)\u003c!-- --\u003e\n\nWe find gaps, regions with less asteroids, at about 2.06, 2.5, 2.82, 2.95, 3.27 AU. These are the Kirkwood gaps, first noticed by Daniel Kirkwood in 1866. They have been depleted by mean-motion resonances (MMR) of Jupiter or Neptune (\u003chttps://en.wikipedia.org/wiki/Kirkwood_gap\u003e). There are also resonances in the outer solar system, e.g. around 30 AU (https://en.wikipedia.org/wiki/Orbital_resonance).\n\n```r\nggplot(ast_df1, aes(x = a)) + geom_histogram(aes(y = ..count..), binwidth = 0.05, color = \"blue\") + xlim(c(28,50)) +\n  labs (x = \"semi-major axis (a) in AU\", y = \"objects per bin (0.05 AU)\")\n```\n\n![](README_figs/README-Outer_gaps-1.png)\u003c!-- --\u003e\n\n# 4. Inclination and asteroid families\nThe next plot is inclination versus semi-major axis. There appear to be finger like shapes.\n\n```r\nggplot(data = ast_df1) + geom_point(mapping = aes(x = a, y = i), shape = \".\", color = \"steelblue\") + \n  xlim(c(0,6)) + ylim(c(0,40)) + labs (x = \"semi-major axis (a) in AU\", y = \"inclination (i) in degrees\")\n```\n\n![](README_figs/README-inclination_plot-1.png)\u003c!-- --\u003e\n\nZooming in and adding the density of the distribution shows concentrations of asteroids.\n\n\n```r\nggplot(data = ast_df1) + stat_bin_2d(mapping = aes(x = a, y = i), bins = 150) + \n  xlim(c(1.5,3.5)) + ylim(c(0,30)) + labs (x = \"semi-major axis (a) in AU\", y = \"inclination (i) in degrees\") +\n  scale_fill_viridis_c(option = \"plasma\") + theme(line = element_blank(),\n        panel.background = element_rect(fill = \"darkblue\"))\n```\n\n![](README_figs/README-heatmap_binning-1.png)\u003c!-- --\u003e\n\nWe find concentrations of asteroids that share the same orbital elements. They are grouped into families (https://en.wikipedia.org/wiki/Asteroid_family#List_of_asteroid_families). A better plot can be made using proper orbital elements instead of osculating orbital elements. Proper elements are stable for several millions of years while osculating elements are only stable for shorter timescales. The AstDyS-2 website has data for asterods, osculating and proper elements (https://newton.spacedys.com/astdys/index.php?pc=0). Using this data can be used for a new plot.\n\n# 5. Asteroid spectral types\n\nWe can also examine taxonomic classification from the Small Main-Belt Asteroid Spectroscopic Survey (SMASS) of 1,477 asteroids. (https://en.wikipedia.org/wiki/Asteroid_spectral_types)\n\nThe main groups are C (carbonacious), S (silicaceous) and X (metallic).\n\n\n```r\nspec_type \u003c- ast_df %\u003e% group_by(spec_B) %\u003e% tally\n# delete sum in first row\nspec_type \u003c- spec_type[-1,]\n```\n\n\n```r\nggplot(spec_type, aes(spec_B, n)) + geom_col(color = \"blue\", fill = \"blue\") +\n  labs (x = \"SMASS II spectral type\", y = \"number of asteroids\")\n```\n\n![](README_figs/README-spectral_type_plot-1.png)\u003c!-- --\u003e\n\nIn this sample the silicaceous (stony) objects dominate.\n\n# Appendix{-}\nHere is a list of the data table.\n\nColumn  | Description                                                | Data Field\n------- | ---------------------------------------------------------- | -----------\n0       |\tobject full name/designation                               | full_name\n1\t      | [ a ] semi-major axis (AU)\t                               | a\n2       |\t[ e ] eccentricity\t                                       | e\n3\t      | [ i ] inclination (deg)\t                                   | i\n4\t      | longitude of the ascending node (deg)                      | om\n5\t      | argument of the perihelion (deg)\t                         | w\n6\t      | [ q ] perihelion distance (AU)\t                           | q\n7\t      | [ Q ] aphelion distance (AU)\t                             | ad\n8\t      | orbital period (years)\t                                   | per_y\n9\t      | number of days spanned by the data-arc (d)                 | data_arc\n10\t    | orbit condition code (MPC 'U' parameter)                   | condition_code\n11\t    | number of observations (all types) used in fit\t           | n_obs_used\n12\t    | number of delay-radar observations used in fit\t           | n_del_obs_used\n13\t    | number of Doppler-radar observations used in fit           | n_dop_obs_used\n14\t    | absolute magnitude parameter (mag)\t                       | H\n15\t    | object diameter (from equivalent sphere) (km)\t             | diameter\n16\t    | object bi/tri-axial ellipsoid dimensions (km)\t             | extent\n17\t    | geometric albedo\t                                         | albedo\n18\t    | rotation period (h)\t                                       | rot_per\n19      | mass expressed as product mass and grav. const. G (km³/s²) | GM\n20\t    | color index B-V (mag)\t                                     | BV\n21\t    | color index U-B (mag)\t                                     | UB\n22\t    | color index I-R (mag)\t                                     | IR\n23\t    | spectral taxonomic type (SMASSII)\t                         | spec_B\n24\t    | spectral taxonomic type (Tholen)\t                         | spec_T\n25\t    | Near-Earth Object (NEO) flag (Y/N)\t                       | neo\n26\t    | Potentially Hazardous Asteroid (PHA) flag (Y/N)\t           | pha\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinheinrich2%2Fjpl-asteroids","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartinheinrich2%2Fjpl-asteroids","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinheinrich2%2Fjpl-asteroids/lists"}