{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":64733,"databundleVersionId":8171035,"sourceType":"competition"}],"dockerImageVersionId":30684,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Different Naive Approaches to get up to 0.20567.\nFollowing up to the [Henri Upton's naive approach](https://www.kaggle.com/code/henriupton/glc24-no-training-at-all-naive-approach) we provide 4 additional approaches to achieve relativelly high score. All the approaches are based on the provided observation metadata and does not need any training.","metadata":{}},{"cell_type":"markdown","source":"## Loading Metadata\nFirst we load all the metadata and import some very basic libraries.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nfrom tqdm import tqdm\n\ntqdm.pandas()\n\n# Load train and test metadata\npo_train = pd.read_csv('/kaggle/input/geolifeclef-2024/GLC24_P0_metadata_train.csv', dtype={'speciesId':int})\npa_train = pd.read_csv('/kaggle/input/geolifeclef-2024/GLC24_PA_metadata_train.csv', dtype={'speciesId':int})\ntest = pd.read_csv('/kaggle/input/geolifeclef-2024/GLC24_PA_metadata_test.csv',  dtype={'speciesId':int})","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:25:33.818231Z","iopub.execute_input":"2024-04-16T09:25:33.818690Z","iopub.status.idle":"2024-04-16T09:26:02.325432Z","shell.execute_reply.started":"2024-04-16T09:25:33.818645Z","shell.execute_reply":"2024-04-16T09:26:02.324226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🌍 Upscale GPS to higher-level regions.\nLocation plays a crutial role in the prediction. Therefore, I will use [reverse_geocoder](https://github.com/thampiman/reverse-geocoder) to upscale the GPS to higher ranks such as district and county.","metadata":{}},{"cell_type":"code","source":"!pip install reverse_geocoder","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:26:02.328070Z","iopub.execute_input":"2024-04-16T09:26:02.328540Z","iopub.status.idle":"2024-04-16T09:26:22.178544Z","shell.execute_reply.started":"2024-04-16T09:26:02.328496Z","shell.execute_reply":"2024-04-16T09:26:22.177120Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from joblib import Parallel, delayed\nimport multiprocessing\nimport reverse_geocoder\n\nn_jobs = multiprocessing.cpu_count()\n\ndef upscale_gps(survey):\n\n    return dict(**reverse_geocoder.search((survey.lat, survey.lon), mode=1)[0], **{'surveyId': survey.surveyId})\n    \n\ndef update_metadata():\n    pass","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:26:22.181398Z","iopub.execute_input":"2024-04-16T09:26:22.181792Z","iopub.status.idle":"2024-04-16T09:26:22.680204Z","shell.execute_reply.started":"2024-04-16T09:26:22.181757Z","shell.execute_reply":"2024-04-16T09:26:22.679048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Update PA Train Surveys","metadata":{"execution":{"iopub.status.busy":"2024-04-16T10:34:45.609621Z","iopub.execute_input":"2024-04-16T10:34:45.610666Z","iopub.status.idle":"2024-04-16T10:34:45.615511Z","shell.execute_reply.started":"2024-04-16T10:34:45.610627Z","shell.execute_reply":"2024-04-16T10:34:45.614164Z"}}},{"cell_type":"code","source":"pa_train_surveys = pa_train.drop_duplicates(\"surveyId\").reset_index(drop=True)\n\npa_location_data = Parallel(n_jobs=n_jobs)(\n            delayed(upscale_gps)(survey)\n            for _, survey in tqdm(pa_train_surveys[[\"lon\", \"lat\", \"surveyId\"]].iterrows(), total=len(pa_train_surveys))\n        )\n\npa_location_data = pd.DataFrame(pa_location_data).rename(\n    columns={\"location_lvl3\":\"location_name\",\n             \"cc\": \"countryCode\",\n             \"admin1\":\"county\",\n             \"admin2\": \"district\"}\n)\npa_location_data.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:26:22.682475Z","iopub.execute_input":"2024-04-16T09:26:22.682794Z","iopub.status.idle":"2024-04-16T09:26:50.074317Z","shell.execute_reply.started":"2024-04-16T09:26:22.682765Z","shell.execute_reply":"2024-04-16T09:26:50.073261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Lets check the granularity.\nprint(f\"Number of unique countires: {len(pa_location_data.countryCode.unique())}\")\nprint(f\"Number of unique counties: {len(pa_location_data.county.unique())}\")\nprint(f\"Number of unique districts: {len(pa_location_data.district.unique())}\")\nprint(f\"Number of unique names: {len(pa_location_data.name.unique())}\")","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:27:09.816285Z","iopub.execute_input":"2024-04-16T09:27:09.816702Z","iopub.status.idle":"2024-04-16T09:27:09.897867Z","shell.execute_reply.started":"2024-04-16T09:27:09.816673Z","shell.execute_reply":"2024-04-16T09:27:09.896713Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pa_location_data.drop(columns=[\"lat\", \"lon\"])\npa_train = pd.merge(pa_train, pa_location_data.drop(columns=[\"lat\", \"lon\"]), on=['surveyId'], how='inner')","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:27:13.339086Z","iopub.execute_input":"2024-04-16T09:27:13.339486Z","iopub.status.idle":"2024-04-16T09:27:13.816952Z","shell.execute_reply.started":"2024-04-16T09:27:13.339457Z","shell.execute_reply":"2024-04-16T09:27:13.815986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Update Test Surveys","metadata":{"execution":{"iopub.status.busy":"2024-04-16T10:35:08.586772Z","iopub.execute_input":"2024-04-16T10:35:08.587761Z","iopub.status.idle":"2024-04-16T10:35:08.592234Z","shell.execute_reply.started":"2024-04-16T10:35:08.587723Z","shell.execute_reply":"2024-04-16T10:35:08.591132Z"}}},{"cell_type":"code","source":"test_location_data = Parallel(n_jobs=n_jobs)(\n            delayed(upscale_gps)(survey)\n            for _, survey in tqdm(test[[\"lon\", \"lat\", \"surveyId\"]].iterrows(), total=len(test))\n        )\n\ntest_location_data = pd.DataFrame(test_location_data).rename(\n    columns={\"location_lvl3\":\"location_name\",\n             \"cc\": \"countryCode\",\n             \"admin1\":\"county\",\n             \"admin2\": \"district\"}\n)\ntest_location_data.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-16T10:36:29.514472Z","iopub.execute_input":"2024-04-16T10:36:29.514888Z","iopub.status.idle":"2024-04-16T10:36:31.034459Z","shell.execute_reply.started":"2024-04-16T10:36:29.514855Z","shell.execute_reply":"2024-04-16T10:36:31.033208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_location_data.drop(columns=[\"lat\", \"lon\"])\ntest = pd.merge(test, test_location_data.drop(columns=[\"lat\", \"lon\"]), on=['surveyId'], how='inner')","metadata":{"execution":{"iopub.status.busy":"2024-04-16T10:37:42.863493Z","iopub.execute_input":"2024-04-16T10:37:42.863968Z","iopub.status.idle":"2024-04-16T10:37:42.880665Z","shell.execute_reply.started":"2024-04-16T10:37:42.863921Z","shell.execute_reply":"2024-04-16T10:37:42.879305Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🏃 First Approach Top25 PA species **[0.11505]**","metadata":{}},{"cell_type":"code","source":"test['predictions'] = ' '.join([str(species) for species in pa_train.speciesId.value_counts().nlargest(25).index])\n\nsubmission = test[['surveyId','predictions']]\nsubmission.to_csv('submission-top25-pa.csv', index=False)\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:29:53.330912Z","iopub.execute_input":"2024-04-16T09:29:53.331420Z","iopub.status.idle":"2024-04-16T09:29:53.407750Z","shell.execute_reply.started":"2024-04-16T09:29:53.331372Z","shell.execute_reply":"2024-04-16T09:29:53.406642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🏃💨 Second Approach: Top-25 species per Country (PA) [**0.15677**]","metadata":{}},{"cell_type":"code","source":"!pip install -U ipywidgets swifter","metadata":{"execution":{"iopub.status.idle":"2024-04-16T09:35:51.337891Z","shell.execute_reply.started":"2024-04-16T09:35:36.387347Z","shell.execute_reply":"2024-04-16T09:35:51.336619Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import swifter\ntqdm.pandas()\n\ndef get_topk_pa_species(country, k=25):\n    query = list(pa_train.loc[(pa_train.country == country), \"speciesId\"].value_counts().nlargest(k).index)\n    query.sort()\n    return \" \".join([str(c) for c in query]) if len(query) > 0 else \"0\"\n\ntest[\"predictions\"] = test.swifter.apply(lambda test_observation: get_topk_pa_species(test_observation[\"country\"]), axis=1)\nsubmission = test[[\"surveyId\",\"predictions\"]]\n\nsubmission.to_csv(\"submission-pa-country-top25.csv\", index=False)\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-04-16T09:35:54.458846Z","iopub.execute_input":"2024-04-16T09:35:54.459959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2024-04-16T10:32:22.573241Z","iopub.execute_input":"2024-04-16T10:32:22.574198Z","iopub.status.idle":"2024-04-16T10:32:22.588827Z","shell.execute_reply.started":"2024-04-16T10:32:22.574153Z","shell.execute_reply":"2024-04-16T10:32:22.587773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🏃💨💨 Third Approach: Top-25 species per district (PA) [**0.20001**]","metadata":{}},{"cell_type":"code","source":"import swifter\ntqdm.pandas()\n\ndef get_topk_pa_species(district, k=25):\n    query = list(pa_train.loc[(pa_train.district == district), \"speciesId\"].value_counts().nlargest(k).index)\n    query.sort()\n    return \" \".join([str(c) for c in query]) if len(query) > 0 else \"0\"\n\ntest[\"predictions\"] = test.swifter.apply(lambda test_observation: get_topk_pa_species(test_observation[\"district\"]), axis=1)\nsubmission = test[[\"surveyId\",\"predictions\"]]\n\nsubmission.to_csv(\"submission-pa-district-top25.csv\", index=False)\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-04-16T10:37:56.818779Z","iopub.execute_input":"2024-04-16T10:37:56.819649Z","iopub.status.idle":"2024-04-16T11:00:54.041393Z","shell.execute_reply.started":"2024-04-16T10:37:56.819599Z","shell.execute_reply":"2024-04-16T11:00:54.039999Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🏆 Fourth Approach: Top-25 species per district & biogeographical zones (PA) [**0.20567**]","metadata":{}},{"cell_type":"code","source":"def get_topk_pa_species(district, region, k=25):\n    query = list(pa_train.loc[(pa_train.region == region) & (pa_train.district == district), \"speciesId\"].value_counts().nlargest(k).index)\n    query.sort()\n    return \" \".join([str(c) for c in query]) if len(query) > 0 else \"0\"\n\ntest[\"predictions\"] = test.swifter.apply(lambda test_observation: get_topk_pa_species(test_observation[\"district\"], test_observation[\"region\"]), axis=1)\nsubmission = test[[\"surveyId\",\"predictions\"]]\n\nsubmission.to_csv(\"submission-pa-district+region.csv\", index=False)\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-04-16T11:00:54.043672Z","iopub.execute_input":"2024-04-16T11:00:54.044079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2024-04-16T12:07:34.242040Z","iopub.execute_input":"2024-04-16T12:07:34.242548Z","iopub.status.idle":"2024-04-16T12:07:34.257582Z","shell.execute_reply.started":"2024-04-16T12:07:34.242511Z","shell.execute_reply":"2024-04-16T12:07:34.256196Z"},"trusted":true},"execution_count":null,"outputs":[]}]}