├── LICENSE
├── README.md
└── twitter_user_scraper.ipynb
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Anthony Baum
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scraping Twitter Lists Using Google Search and Tweepy
2 | Author: Anthony Baum
3 |
4 | Contact email: wxbaum@gmail.com
5 |
6 | ## Note
7 | **If you're reading this after June 2023, this code no longer works after major changes to Twitter's API. If you have access to the paid tier API service, you might find this serviceable with some refactoring.**
8 |
9 | ## Overview
10 | The primary function of this script is to bulk-identify a set of Twitter users that match given search keywords on Google.
11 |
12 | By using a Google search for user-created Twitter lists, we can quasi-crowdsource a large number of accounts by matching lists to the keywords being searched for. Using Tweepy to access the user objects of each user in the lists, a dataset of users tailored for any application can be generated with relative ease.
13 |
14 | Note that this script will take a few minutes to run to prevent Google or Twitter locking us out for making too many requests in a short time period.
15 |
16 | To utilize the script you will need access to the Twitter API. Set up an account and project with Twitter Development here: https://developer.twitter.com/en/apply-for-access.
17 |
18 | No Google search authentication is required, as the googlesearch Python module it utilized. Module repo and documentation available here: https://github.com/MarioVilas/googlesearch
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/twitter_user_scraper.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "twitter_user_scraper.ipynb",
7 | "version": "0.3.2",
8 | "provenance": [],
9 | "collapsed_sections": []
10 | },
11 | "kernelspec": {
12 | "name": "python3",
13 | "display_name": "Python 3"
14 | }
15 | },
16 | "cells": [
17 | {
18 | "metadata": {
19 | "id": "xMW5RIhrJfq4",
20 | "colab_type": "text"
21 | },
22 | "cell_type": "markdown",
23 | "source": [
24 | "# A Scraper for Twitter User Data \n",
25 | "\n",
26 | "This project utilizes the googlesearch module with Tweepy to create a dataset of Twitter users, sourced from user-generated Twitter lists that match a given keyword. By using lists like so, we can bulk identify accounts that match a keyword without having to trawl through user attributes like bios. Tweepy allows access to a plethora of user/status/list data, so once lists and the users contained in the lists have been scraped, the breadth of data that can be collected is expansive. \n",
27 | "\n",
28 | "For demonstration purposes, the project here pulls in favorite and retweet actions and creates a measurement of engagement for each user collected. \n",
29 | "\n",
30 | "This script takes several minutes to run, depending on how intensive the Google search and Twitter API requests are. For Google, a conservative random sleep timer is used to best prevent timeouts. For Twitter, the API is instantiated using wait_on_rate_limit=True, meaning that if the API's wait limit is exceeded, the function will wait until the API is available again rather than throwing an error. \n",
31 | "\n",
32 | "The project requires access to Twitter's API, so you will need to a developer account with Twitter. Set up an account and project here for free: https://developer.twitter.com/en/apply-for-access.\n",
33 | "\n",
34 | "The googlesearch module requires installation. Retrieve it here: https://github.com/MarioVilas/googlesearch. \n",
35 | "\n",
36 | "Notes:\n",
37 | "- The results_to_obtain parameter for scrape_influencers_use_google() is currently set to 50. Increase the number to retrieve more lists and more users. If the number is increased significantly, set the sleep time between each URL to a longer period to prevent being locked out by Google. \n",
38 | "- The list_contains_string parameter is the keyword you will use to search for lists. \n",
39 | "\n"
40 | ]
41 | },
42 | {
43 | "metadata": {
44 | "id": "bQkdi9K3JBx1",
45 | "colab_type": "code",
46 | "colab": {}
47 | },
48 | "cell_type": "code",
49 | "source": [
50 | "# Module imports \n",
51 | "import tweepy\n",
52 | "import time\n",
53 | "import pandas as pd\n",
54 | "import numpy as np\n",
55 | "import json\n",
56 | "import time\n",
57 | "from random import randint, random\n",
58 | "from googlesearch import search as gsearch\n",
59 | "import matplotlib.pyplot as plt\n",
60 | "import seaborn as sns\n",
61 | "%matplotlib inline"
62 | ],
63 | "execution_count": 0,
64 | "outputs": []
65 | },
66 | {
67 | "metadata": {
68 | "id": "gqg7pcHspJ98",
69 | "colab_type": "text"
70 | },
71 | "cell_type": "markdown",
72 | "source": [
73 | "Authenticating to Twitter and creating the API connection.\n",
74 | "\n",
75 | "Assign the keys for your Twitter developer account to the approriate variables. "
76 | ]
77 | },
78 | {
79 | "metadata": {
80 | "id": "Z91j6XfBJSlc",
81 | "colab_type": "code",
82 | "colab": {}
83 | },
84 | "cell_type": "code",
85 | "source": [
86 | "api_key = ''\n",
87 | "secret_key = ''\n",
88 | "access_token = ''\n",
89 | "secret_token = ''\n",
90 | "\n",
91 | "def authenticate(api_key, secret_key, access_token, secret_token):\n",
92 | "\n",
93 | " auth = tweepy.OAuthHandler(api_key, secret_key)\n",
94 | " auth.set_access_token(access_token, secret_token)\n",
95 | " api = tweepy.API(auth, \n",
96 | " wait_on_rate_limit=True, \n",
97 | " wait_on_rate_limit_notify=True)\n",
98 | " \n",
99 | " return api\n",
100 | "\n",
101 | "\n",
102 | "api = authenticate(api_key, secret_key, access_token, secret_token)"
103 | ],
104 | "execution_count": 0,
105 | "outputs": []
106 | },
107 | {
108 | "metadata": {
109 | "id": "2vWZeBZZo8FC",
110 | "colab_type": "text"
111 | },
112 | "cell_type": "markdown",
113 | "source": [
114 | "Some api calls will result in a rate limit error. While this script in it's current iteration does not require this now, the function below will handle those if needed for any Tweepy requests. "
115 | ]
116 | },
117 | {
118 | "metadata": {
119 | "id": "_KLxo3yOjxZL",
120 | "colab_type": "code",
121 | "colab": {}
122 | },
123 | "cell_type": "code",
124 | "source": [
125 | "def limit_handled(cursor):\n",
126 | " \n",
127 | " while True:\n",
128 | " try:\n",
129 | " yield cursor.next()\n",
130 | " except tweepy.RateLimitError:\n",
131 | " time.sleep(2)\n",
132 | " continue"
133 | ],
134 | "execution_count": 0,
135 | "outputs": []
136 | },
137 | {
138 | "metadata": {
139 | "id": "wa_p7hr_pWml",
140 | "colab_type": "text"
141 | },
142 | "cell_type": "markdown",
143 | "source": [
144 | "Parameter notes:\n",
145 | "- list_contains_string - the list keyword to search for\n",
146 | "- results to obtain - number of results to be recorded\n",
147 | "\n",
148 | "Requesting a large number of results will likely cause Google to temporarily prevent access. "
149 | ]
150 | },
151 | {
152 | "metadata": {
153 | "id": "hHlsPsEVDYZ9",
154 | "colab_type": "code",
155 | "colab": {}
156 | },
157 | "cell_type": "code",
158 | "source": [
159 | "\n",
160 | "def scrape_lists_with_google(keyword_string, results_to_obtain):\n",
161 | "\n",
162 | " list_urls = []\n",
163 | "\n",
164 | " urls_checked = 0\n",
165 | " urls_appended = 0\n",
166 | " \n",
167 | " # Perform the Google search, checking only the twitter.com domain\n",
168 | " for url in gsearch(\"site:twitter.com lists \" + keyword_string, \n",
169 | " start=urls_checked, \n",
170 | " num=10, \n",
171 | " pause=randint(3, 5)):\n",
172 | " \n",
173 | " # Take URLs that contain Twitter's '/lists' subdomain\n",
174 | " if '/lists/' in url:\n",
175 | " list_urls.append(url)\n",
176 | " urls_appended += 1\n",
177 | " urls_checked += 1\n",
178 | "\n",
179 | " # If Google is returning 5x as many non-list results as lists, break search\n",
180 | " if urls_checked / urls_appended > 5:\n",
181 | " break\n",
182 | " \n",
183 | " # If we reach our results # request limit, break the search\n",
184 | " elif urls_appended >= results_to_obtain:\n",
185 | " break\n",
186 | " \n",
187 | " # Sleep to prevent timeouts\n",
188 | " time.sleep(randint(3, 5))\n",
189 | "\n",
190 | " list_urls = [url[8:] for url in list_urls] # Removes leading https://\n",
191 | "\n",
192 | " return list_urls\n",
193 | " \n",
194 | "\n",
195 | "list_urls = scrape_lists_with_google(\"influencers\", 10)"
196 | ],
197 | "execution_count": 0,
198 | "outputs": []
199 | },
200 | {
201 | "metadata": {
202 | "id": "7nYmaoiCre3V",
203 | "colab_type": "text"
204 | },
205 | "cell_type": "markdown",
206 | "source": [
207 | "Getting the individual users and associated attributes is made easy with Tweepy. Using the API created earlier, this function returns a dataframe of individual users and associated Twitter attributes. "
208 | ]
209 | },
210 | {
211 | "metadata": {
212 | "id": "tSSWgKTCi-0A",
213 | "colab_type": "code",
214 | "colab": {}
215 | },
216 | "cell_type": "code",
217 | "source": [
218 | "# Get user id, screen name, bio, follower count\n",
219 | "\n",
220 | "def get_users_in_lists(list_urls):\n",
221 | " \n",
222 | " users_list = []\n",
223 | " bios_list = []\n",
224 | " desc_list = []\n",
225 | " follower_count_list = []\n",
226 | " \n",
227 | " for tw_list in list_urls:\n",
228 | " \n",
229 | " user = tw_list.split('/')[1]\n",
230 | " list_name = tw_list.split('/')[3]\n",
231 | "\n",
232 | " # Using Tweepy api, getting json of all users in list\n",
233 | " list_members_output = api.list_members(user, list_name)\n",
234 | "\n",
235 | " # Parsing json data and appending to appropriate lists instantiated above\n",
236 | " for user_id in list_members_output:\n",
237 | " users_list.append(user_id._json['id'])\n",
238 | " bios_list.append(user_id._json['screen_name'])\n",
239 | " desc_list.append(user_id._json['description'])\n",
240 | " follower_count_list.append(user_id._json['followers_count'])\n",
241 | " \n",
242 | " zipped_data = zip(users_list, bios_list, desc_list, follower_count_list)\n",
243 | " \n",
244 | " # Creates a pandas dataframe containing user attributes\n",
245 | " output_df = pd.DataFrame(list(zipped_data), columns=['user_id', \n",
246 | " 'screen_name',\n",
247 | " 'bio',\n",
248 | " 'followers'])\n",
249 | " # Dropping duplicate users if they were in multiple lists\n",
250 | " output_df.drop_duplicates('user_id')\n",
251 | " \n",
252 | " output_df.set_index('user_id', inplace=True)\n",
253 | " \n",
254 | " return output_df\n",
255 | " \n",
256 | "\n",
257 | "influencers_df = get_users_in_lists(list_urls)"
258 | ],
259 | "execution_count": 0,
260 | "outputs": []
261 | },
262 | {
263 | "metadata": {
264 | "id": "vZbjS6kuSfwm",
265 | "colab_type": "code",
266 | "outputId": "ac28dc5e-252b-4131-c872-72beee96ed47",
267 | "colab": {
268 | "base_uri": "https://localhost:8080/",
269 | "height": 235
270 | }
271 | },
272 | "cell_type": "code",
273 | "source": [
274 | "influencers_df.sort_values(by='followers', ascending=False).head()"
275 | ],
276 | "execution_count": 16,
277 | "outputs": [
278 | {
279 | "output_type": "execute_result",
280 | "data": {
281 | "text/html": [
282 | "
\n",
283 | "\n",
296 | "
\n",
297 | " \n",
298 | "
\n",
299 | "
\n",
300 | "
screen_name
\n",
301 | "
bio
\n",
302 | "
followers
\n",
303 | "
\n",
304 | "
\n",
305 | "
user_id
\n",
306 | "
\n",
307 | "
\n",
308 | "
\n",
309 | "
\n",
310 | " \n",
311 | " \n",
312 | "
\n",
313 | "
44409004
\n",
314 | "
shakira
\n",
315 | "
Shakira NADA https://t.co/50Yl0h1Xkh
\n",
316 | "
51005025
\n",
317 | "
\n",
318 | "
\n",
319 | "
115485051
\n",
320 | "
ConanOBrien
\n",
321 | "
The voice of the people. Sorry, people.
\n",
322 | "
28457778
\n",
323 | "
\n",
324 | "
\n",
325 | "
32959253
\n",
326 | "
khloekardashian
\n",
327 | "
\n",
328 | "
26735374
\n",
329 | "
\n",
330 | "
\n",
331 | "
268439864
\n",
332 | "
xtina
\n",
333 | "
#TheXperience LIVE in Vegas starting May 31st ...
\n",
334 | "
16988954
\n",
335 | "
\n",
336 | "
\n",
337 | "
40908929
\n",
338 | "
Usher
\n",
339 | "
\n",
340 | "
12373871
\n",
341 | "
\n",
342 | " \n",
343 | "
\n",
344 | "
"
345 | ],
346 | "text/plain": [
347 | " screen_name bio \\\n",
348 | "user_id \n",
349 | "44409004 shakira Shakira NADA https://t.co/50Yl0h1Xkh \n",
350 | "115485051 ConanOBrien The voice of the people. Sorry, people. \n",
351 | "32959253 khloekardashian \n",
352 | "268439864 xtina #TheXperience LIVE in Vegas starting May 31st ... \n",
353 | "40908929 Usher \n",
354 | "\n",
355 | " followers \n",
356 | "user_id \n",
357 | "44409004 51005025 \n",
358 | "115485051 28457778 \n",
359 | "32959253 26735374 \n",
360 | "268439864 16988954 \n",
361 | "40908929 12373871 "
362 | ]
363 | },
364 | "metadata": {
365 | "tags": []
366 | },
367 | "execution_count": 16
368 | }
369 | ]
370 | },
371 | {
372 | "metadata": {
373 | "id": "nh8Gr_O-_Xdi",
374 | "colab_type": "text"
375 | },
376 | "cell_type": "markdown",
377 | "source": [
378 | "Accounts which are protected will not be accessible for the majority of Tweepy user object functionality. These need to be removed. \n",
379 | "\n",
380 | "When the API connection was built at the start of the script, the wait_on_rate_limit and wait_on_rate_limit_notify parameters were set to true. While those will catch a wait limit error, notify us, and go again when ready, this function checks a lot of users very quickly and will hit the rate limit most of the time if left alone. A sleep timer for each user has been set as a random float up to 0.2 (representing sleep up to 0.2s). This will not prevent rate limit errors 100% of the time but strikes a balance between error prevention and run time. \n",
381 | "\n",
382 | "Run time for this function is as such maximized at 0.2 * n_users seconds.\n"
383 | ]
384 | },
385 | {
386 | "metadata": {
387 | "id": "F_W70DGAbJUX",
388 | "colab_type": "code",
389 | "outputId": "02062895-cabc-4fcc-9e97-a299b4a635c8",
390 | "colab": {
391 | "base_uri": "https://localhost:8080/",
392 | "height": 119
393 | }
394 | },
395 | "cell_type": "code",
396 | "source": [
397 | "def remove_protected_accounts(users_df):\n",
398 | " \n",
399 | " working_df = users_df.copy()\n",
400 | " protected_ids = []\n",
401 | " \n",
402 | " counter = 0\n",
403 | " for index, row in users_df.iterrows():\n",
404 | " user = api.get_user(id=index)._json\n",
405 | " if user['protected'] == True:\n",
406 | " protected_ids.append(index)\n",
407 | " counter += 1\n",
408 | " if counter % 100 == 0:\n",
409 | " print(str(counter) + '/' + str(len(working_df)) + ' IDs checked')\n",
410 | " print('Protected IDs found:', len(protected_ids), '\\n')\n",
411 | " # Sleeping for up to 0.2s helps not to exceed Tweepy's rate limit.\n",
412 | " time.sleep(random() / 5)\n",
413 | " \n",
414 | " working_df.drop(labels=protected_ids, inplace=True)\n",
415 | " working_df.reset_index(inplace=True)\n",
416 | " \n",
417 | " return working_df\n",
418 | " \n",
419 | "\n",
420 | "influencers_df = remove_protected_accounts(influencers_df)"
421 | ],
422 | "execution_count": 17,
423 | "outputs": [
424 | {
425 | "output_type": "stream",
426 | "text": [
427 | "100/200 IDs checked\n",
428 | "Protected IDs found: 1 \n",
429 | "\n",
430 | "200/200 IDs checked\n",
431 | "Protected IDs found: 3 \n",
432 | "\n"
433 | ],
434 | "name": "stdout"
435 | }
436 | ]
437 | },
438 | {
439 | "metadata": {
440 | "id": "GTlVXskl176A",
441 | "colab_type": "text"
442 | },
443 | "cell_type": "markdown",
444 | "source": [
445 | "Now protected accounts have been removed, engagements with users' tweets can be examined. We can't access actual impressions for tweets, but we can access some engagement actions (favorites, retweets) for tweets. By looking at engagement actions in relation to the number of followers a user has, we can see how much their audience is engaged with that user's content. \n",
446 | "\n",
447 | "While this particular notebook just overviews the scraping code, it's a helpful excercise to generate some additional data to demonstrate a use case. As we've been looking at influencers for the purposes of this script, we can get a fairly good measurement of how effective an influencer might be by looking at metrics like this. \n",
448 | "\n"
449 | ]
450 | },
451 | {
452 | "metadata": {
453 | "id": "0fPlPDdVZOzz",
454 | "colab_type": "code",
455 | "outputId": "a56bedcd-9a9e-4588-bbce-553ee7898588",
456 | "colab": {
457 | "base_uri": "https://localhost:8080/",
458 | "height": 51
459 | }
460 | },
461 | "cell_type": "code",
462 | "source": [
463 | "def calc_median_favorites(user_id):\n",
464 | " \n",
465 | " fav_list = []\n",
466 | " tweets = api.user_timeline(id=user_id, count=100)\n",
467 | " \n",
468 | " for tweet in tweets:\n",
469 | " # Remove retweets\n",
470 | " if tweet._json['text'].startswith('RT'):\n",
471 | " continue\n",
472 | " # Remove replies\n",
473 | " elif tweet._json['text'].startswith('@'):\n",
474 | " continue\n",
475 | " else:\n",
476 | " fav_list.append(tweet._json['favorite_count'])\n",
477 | " \n",
478 | " time.sleep(random() / 5)\n",
479 | " \n",
480 | " return np.median(fav_list) \n",
481 | "\n",
482 | "\n",
483 | "influencers_df['median_favs'] = influencers_df['user_id'].apply(lambda x: calc_median_favorites(x))"
484 | ],
485 | "execution_count": 18,
486 | "outputs": [
487 | {
488 | "output_type": "stream",
489 | "text": [
490 | "/usr/local/lib/python3.6/dist-packages/numpy/core/fromnumeric.py:2957: RuntimeWarning: Mean of empty slice.\n",
491 | " out=out, **kwargs)\n"
492 | ],
493 | "name": "stderr"
494 | }
495 | ]
496 | },
497 | {
498 | "metadata": {
499 | "id": "1apvWYVwalN9",
500 | "colab_type": "code",
501 | "outputId": "d4467c89-add6-4b8e-983c-10bd8a1d40af",
502 | "colab": {
503 | "base_uri": "https://localhost:8080/",
504 | "height": 51
505 | }
506 | },
507 | "cell_type": "code",
508 | "source": [
509 | "def calc_median_retweets(user_id):\n",
510 | " \n",
511 | " rt_list = []\n",
512 | " tweets = api.user_timeline(id=user_id, count=100)\n",
513 | " \n",
514 | " for tweet in tweets:\n",
515 | " if tweet._json['text'].startswith('RT'):\n",
516 | " continue\n",
517 | " else:\n",
518 | " rt_list.append(tweet._json['retweet_count'])\n",
519 | " \n",
520 | " time.sleep(random() / 5)\n",
521 | " \n",
522 | " return np.median(rt_list) \n",
523 | "\n",
524 | "\n",
525 | "influencers_df['median_rts'] = influencers_df['user_id'].apply(lambda x: calc_median_retweets(x))"
526 | ],
527 | "execution_count": 19,
528 | "outputs": [
529 | {
530 | "output_type": "stream",
531 | "text": [
532 | "/usr/local/lib/python3.6/dist-packages/numpy/core/fromnumeric.py:2957: RuntimeWarning: Mean of empty slice.\n",
533 | " out=out, **kwargs)\n"
534 | ],
535 | "name": "stderr"
536 | }
537 | ]
538 | },
539 | {
540 | "metadata": {
541 | "id": "35abBVjAVTlf",
542 | "colab_type": "code",
543 | "outputId": "ecad73b9-18ce-4f28-fd5f-de0bc2f14f7a",
544 | "colab": {
545 | "base_uri": "https://localhost:8080/",
546 | "height": 204
547 | }
548 | },
549 | "cell_type": "code",
550 | "source": [
551 | "influencers_df[5:10]"
552 | ],
553 | "execution_count": 27,
554 | "outputs": [
555 | {
556 | "output_type": "execute_result",
557 | "data": {
558 | "text/html": [
559 | "
\n",
560 | "\n",
573 | "
\n",
574 | " \n",
575 | "
\n",
576 | "
\n",
577 | "
user_id
\n",
578 | "
screen_name
\n",
579 | "
bio
\n",
580 | "
followers
\n",
581 | "
median_favs
\n",
582 | "
median_rts
\n",
583 | "
engagement
\n",
584 | "
\n",
585 | " \n",
586 | " \n",
587 | "
\n",
588 | "
5
\n",
589 | "
214164173
\n",
590 | "
DrJaneChi
\n",
591 | "
Family physician, intersectional feminist, cat...
\n",
592 | "
13669
\n",
593 | "
24.5
\n",
594 | "
0.0
\n",
595 | "
1.0
\n",
596 | "
\n",
597 | "
\n",
598 | "
6
\n",
599 | "
212657985
\n",
600 | "
michelleinbklyn
\n",
601 | "
NYT columnist; co-host of The Argument podcast...
\n",
602 | "
70811
\n",
603 | "
202.0
\n",
604 | "
35.0
\n",
605 | "
1.0
\n",
606 | "
\n",
607 | "
\n",
608 | "
7
\n",
609 | "
189896485
\n",
610 | "
emmacargo
\n",
611 | "
writer, @ProblemAreasHBO. former editor, @Jeze...
\n",
612 | "
25238
\n",
613 | "
73.0
\n",
614 | "
0.0
\n",
615 | "
1.0
\n",
616 | "
\n",
617 | "
\n",
618 | "
8
\n",
619 | "
142721190
\n",
620 | "
elisefoley
\n",
621 | "
HuffPost. Email: elise dot foley at huffpost d...
\n",
622 | "
61281
\n",
623 | "
11.5
\n",
624 | "
0.0
\n",
625 | "
1.0
\n",
626 | "
\n",
627 | "
\n",
628 | "
9
\n",
629 | "
87070315
\n",
630 | "
ingridnilsen
\n",
631 | "
Curious mind. Adventurous heart. Eater of all ...
\n",
632 | "
1196067
\n",
633 | "
71.5
\n",
634 | "
4.0
\n",
635 | "
1.0
\n",
636 | "
\n",
637 | " \n",
638 | "
\n",
639 | "
"
640 | ],
641 | "text/plain": [
642 | " user_id screen_name \\\n",
643 | "5 214164173 DrJaneChi \n",
644 | "6 212657985 michelleinbklyn \n",
645 | "7 189896485 emmacargo \n",
646 | "8 142721190 elisefoley \n",
647 | "9 87070315 ingridnilsen \n",
648 | "\n",
649 | " bio followers median_favs \\\n",
650 | "5 Family physician, intersectional feminist, cat... 13669 24.5 \n",
651 | "6 NYT columnist; co-host of The Argument podcast... 70811 202.0 \n",
652 | "7 writer, @ProblemAreasHBO. former editor, @Jeze... 25238 73.0 \n",
653 | "8 HuffPost. Email: elise dot foley at huffpost d... 61281 11.5 \n",
654 | "9 Curious mind. Adventurous heart. Eater of all ... 1196067 71.5 \n",
655 | "\n",
656 | " median_rts engagement \n",
657 | "5 0.0 1.0 \n",
658 | "6 35.0 1.0 \n",
659 | "7 0.0 1.0 \n",
660 | "8 0.0 1.0 \n",
661 | "9 4.0 1.0 "
662 | ]
663 | },
664 | "metadata": {
665 | "tags": []
666 | },
667 | "execution_count": 27
668 | }
669 | ]
670 | },
671 | {
672 | "metadata": {
673 | "id": "rAtWyt4KKf7e",
674 | "colab_type": "text"
675 | },
676 | "cell_type": "markdown",
677 | "source": [
678 | "We've likely got a notification above for a RuntimeWarning. This is numpy trying to take the median of of an empty array, that is, accounts that have never tweeted. Those accounts are visbile in the data as they have nan values under median_favs and/or median_rts. We can check to see if any exist using a heatmap for null values. "
679 | ]
680 | },
681 | {
682 | "metadata": {
683 | "id": "4pgOyK1p3nt1",
684 | "colab_type": "code",
685 | "outputId": "83a8f992-6cdd-4697-cbb6-e1dadb16ed04",
686 | "colab": {
687 | "base_uri": "https://localhost:8080/",
688 | "height": 286
689 | }
690 | },
691 | "cell_type": "code",
692 | "source": [
693 | "sns.heatmap(influencers_df.isnull())"
694 | ],
695 | "execution_count": 21,
696 | "outputs": [
697 | {
698 | "output_type": "execute_result",
699 | "data": {
700 | "text/plain": [
701 | ""
702 | ]
703 | },
704 | "metadata": {
705 | "tags": []
706 | },
707 | "execution_count": 21
708 | },
709 | {
710 | "output_type": "display_data",
711 | "data": {
712 | "image/png": "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\nSUVORK5CYII=\n",
713 | "text/plain": [
714 | "