Here’s a python snippet for analysing an iNaturalist export file and exporting an HTML-formatted list of species which only have observations from a single person (e.g. this list for the CNC Wien 2021)
# coding: utf-8
import argparse
import pandas as pd
"""
Find which species in an iNaturalist export only have observations from a single observer.
Get an export from here: https://www.inaturalist.org/observations/export with a query such
as quality_grade=research&identifications=any&rank=species&projects[]=92926 and at least the
following columns: taxon_id, scientific_name, common_name, user_login
Download it, extract the CSV, then run this script with the file name as its argument. It will
output basic stats formatted as HTML.
The only external module required is pandas.
Example usage:
py uniquely_observed_species.py wien_cnc_2021.csv > wien_cnc_2021_results.html
If you provide the --project-id (-p) argument, the taxa links in the output list will link to
a list of observations of that taxa within that project. Otherwise, they default to linking
to the taxa page.
If a quality_grade column is included, non-research-grade observations will be included in the
analysis. Uniquely observed species with no research-grade observations will be marked. Species
which were observed by multiple people, only one of which has research-grade observation(s) will
also be marked.
By Barnaby Walters waterpigs.co.uk
"""
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Given an iNaturalist observation export, find species which were only observed by a single person.')
parser.add_argument('export_file')
parser.add_argument('-p', '--project-id', dest='project_id', default=None)
args = parser.parse_args()
uniquely_observed_species = {}
df = pd.read_csv(args.export_file)
# If quality_grade isn’t given, assume that the export contains only RG observations.
if 'quality_grade' not in df.columns:
df.loc[:, 'quality_grade'] = 'research'
# Filter out casual observations.
df = df.query('quality_grade != "casual"')
# Create a local species reference from the dataframe.
species = df.loc[:, ('taxon_id', 'scientific_name', 'common_name')].drop_duplicates()
species = species.set_index(species.loc[:, 'taxon_id'])
for tid in species.index:
observers = df.query('taxon_id == @tid').loc[:, 'user_login'].drop_duplicates()
research_grade_observers = df.query('taxon_id == @tid and quality_grade == "research"').loc[:, 'user_login'].drop_duplicates()
if observers.shape[0] == 1:
# Only one person made any observations of this species.
observer = observers.squeeze()
if observer not in uniquely_observed_species:
uniquely_observed_species[observer] = []
uniquely_observed_species[observer].append({
'id': tid,
'has_research_grade': (not research_grade_observers.empty),
'num_other_observers': 0
})
elif research_grade_observers.shape[0] == 1:
# Multiple people observed the species, but only one person has research-grade observation(s).
rg_observer = research_grade_observers.squeeze()
if rg_observer not in uniquely_observed_species:
uniquely_observed_species[rg_observer] = []
uniquely_observed_species[rg_observer].append({
'id': tid,
'has_research_grade': True,
'num_other_observers': observers.shape[0] - 1
})
# Sort observers by number of unique species.
sorted_observations = sorted(uniquely_observed_species.items(), key=lambda t: len(t[1]), reverse=True)
print(f"<p>{sum([len(t) for _, t in sorted_observations])} taxa uniquely observed by {len(sorted_observations)} observers.</p>")
print('<p>')
for observer, _ in sorted_observations:
print(f"@{observer} ", end='')
print('</p>')
print('<p><b>bold</b> species are ones for which the given observer has one or more research-grade observations.</p>')
print('<p>If only one person has RG observations of a species, but other people have observations which need ID, the number of needs-ID observers are indicated in parentheses.')
for observer, taxa in sorted_observations:
print(f"""\n\n<p><a href="https://www.inaturalist.org/people/{observer}">@{observer}</a> ({len(taxa)} taxa):</p><ul>""")
for tobv in sorted(taxa, key=lambda t: species.loc[t['id']]['scientific_name']):
tid = tobv['id']
t = species.loc[tid]
if args.project_id:
taxa_url = f"https://www.inaturalist.org/observations?taxon_id={tid}&project_id={args.project_id}"
else:
taxa_url = f'https://www.inaturalist.org/taxa/{tid}'
rgb, rge = ('<b>', '</b>') if tobv.get('has_research_grade') else ('', '')
others = f" ({tobv.get('num_other_observers', 0)})" if tobv.get('num_other_observers', 0) > 0 else ''
if not pd.isnull(t['common_name']):
print(f"""<li><a href="{taxa_url}">{rgb}<i>{t['scientific_name']}</i> ({t['common_name']}){rge}{others}</a></li>""")
else:
print(f"""<li><a href="{taxa_url}">{rgb}<i>{t['scientific_name']}</i>{rge}{others}</a></li>""")
print("</ul>")
After many unsuccessful attempts to propagate shrub cuttings, this sage seems to be cooperating! Making a tape grid over a plastic tub of water is a nice low-tech solution for holding many cuttings upright.
New video: Ai Wo Mitsuketa Basho (愛を見つけた場所) by Oku Hanako and Akito Matsuda. I adapted the brass duet arrangement from Sound! Euphonium for hurdy gurdy, and played both parts on gurdy #10 before shipping it to its new owner.
Nobody’s likely to make an anime about hurdy gurdy players any time soon, so this will have to do for the moment.
Day 4 of the #CityNatureChallenge was very successful after the storm-related inactivity of day 3, with 106 observations from the Lobau and Prater. Highlights include:
My first sighting of wild european pond turtles
My first black woodpeckers
Many great spotted woodpeckers
A greater bee fly which held still long enough for me to photograph it
Some newts
This interesting spider
These enormous beetle larvae
This cool looking moth
And many, many oil beetles
That makes a total of 213 observations over the long weekend. It’ll take some time for them to be identifed down to species level, but it looks like at least 130 individual species.
Day 2 of the #CityNatureChallenge: I finally made it to the Lainzer Tiergarten and made 73 observations, the highlights of which included:
Some enormous woodlice
Some sort of Polydesmus, curled up on a little wall it had built to protect its eggs
Plenty of Glomeris and pill woodlice
A red squirrel
A nuthatch — not a great photo, but they’re one of my favourite woodland birds
One of the furriest moths I’ve ever seen — I think it’s a chimney sweep moth? If so, it’s an appropriate name.
And finally, this enormous severed stag beetle head.
Made 34 observations on the first day of the #CityNatureChallenge Wien. Nothing particularly remarkable, except for the biggest frog I’ve ever seen, and lots of hamster activity, including some fights and parents with young, which I’ve not seen before.
Now I finally fixed my webmention endpoint, time to make sure my webmention sending is up to scratch, with https://webmention.rocks/
Test 1 Test 2 Test 3 Test 4 Test 5 Test 6 Test 7 Test 8 Test 9 Test 10 Test 11 Test 12 Test 13 Test 14 Test 15 Test 16 Test 17 Test 18 Test 19 Test 20 Test 21 Test 22 Test 23
I finally got Visual Studio Code’s terminal and tab handling working the way I wanted, with these keybindings:
[
{
"key": "shift+cmd+]",
"command": "workbench.action.terminal.focusNext",
"when": "terminalFocus"
},
{
"key": "shift+cmd+[",
"command": "workbench.action.terminal.focusPrevious",
"when": "terminalFocus"
},
{
"key": "cmd+w",
"command": "workbench.action.terminal.kill",
"when": "terminalFocus"
},
{
"key": "ctrl+`",
"command": "workbench.action.terminal.focus"
},
{
"key": "ctrl+`",
"command": "workbench.action.focusActiveEditorGroup",
"when": "terminalFocus"
}
]
This way, cmd+shift+]
and cmd+shift+[
not only allow you to switch between editor tabs, but also terminals, when the terminal is focused. cmd+w
kills the current terminal tab as expected. ctrl+`
switches focus between the editor and the terminal.
I was considering overriding cmd+t
to open a new terminal when the terminal is focused, but decided to just get used to the built in ctrl+shift+`
shortcut instead.
The naming of the Least Weasel implies the existence of a Most Weasel
Happy 237th Halibut Day, everyone!
A Camomile tip I learned the hard way: audio units built using camomile (and presumably VSTs and lv2s too) will stop working if you rename them, as they rely on the bundle filename, the .txt configuration file and the main .pd patch all sharing the same base name in order to function.
The wild hamsters are active again! Saw about 20 of them today.
Watching the colours mix and flow is almost as beautiful as the painting itself
I don’t often check my internet numbers, but I’m happy to see that a music video I made is at least marginally more popular than that one time I 3D printed some minecraft foxes.
(And yes, I know that the foxes have a vastly higher Internet Number Per Year, but the music is the more enduring content so that’ll self-correct over time)
I’m seeing a lot of rhetoric claiming that the UK dropping out of ERASMUS is good because it “enables UK students to travel to better universities because only one EU university is in the top 50, and they’re 50th”
I’m no expert on university rankings, but five minutes of cursory internet research reveals this claim to be questionable at best. There are three major international university rankings: QS, ARWU and THEWUR. The “only-one-in-the-top-50“ “fact” spouted so readily by leavers is true of only one of these: the QS ranking, which list the TUM in 50th place.
ARWU is a different story, with the Paris-Saclay University in 14th place, the Sorbonne in 39th and the Karolinska Institute at 45th. THEWUR has LMU Munich at 32nd, the Karolinska Institute tied with the University of Tokyo at 36th, the TUM at 41st, Heidelberg University at 42nd, KU Leuven at 45th and PSL at 46th.
Additionally, Swiss universities are ranked highly by all three lists. While no longer a part of ERASMUS+, it’s in theory possible for EU student exchanges to be funded via the SEMP programme.
Apparently the UK’s participation in ERASMUS will be replaced with the tactlessly-named “Turing Scheme”, about which only vague details are available. Given the current governments track record on… pretty much everything, I don’t much rate its chances, but who knows.
There are many other holes to poke in the claim that leaving ERASMUS (and indeed the EU in general) is a good move for UK students, and that university rankings are the be-all-and-end-all of choosing where to do an exchange (variety? language choice? social/cultural connections?), but they’re more complicated questions. This particular claim was so easily debunked that I couldn’t resist.
Update: this twitter thread from a UK professor confirms most of my suspicions about the prospects of the Turing scheme.