@@ -247,3 +247,76 @@ first_commenter_tab.sort_values(
247247 "# of times commented first", ascending=False
248248).head(10)
249249```
250+
251+ ## Pull Requests
252+
253+ ``` {code-cell} ipython3
254+ ---
255+ tags: [hide-input]
256+ ---
257+
258+ with open("../_data/prs.json", "r") as fh:
259+ data = json.loads(fh.read())
260+ ```
261+
262+ A look at merged PRs over time.
263+
264+ ``` {code-cell} ipython3
265+ ---
266+ tags: [hide-input]
267+ ---
268+ # All contributors
269+
270+ merged_prs = [d for d in data if d['node']['state'] == 'MERGED']
271+ merge_dates = np.array([r['node']['mergedAt'] for r in merged_prs], dtype=np.datetime64)
272+ binsize = np.timedelta64(30, "D")
273+ date_bins = np.arange(merge_dates[0], merge_dates[-1], binsize)
274+ h_all, bedges = np.histogram(merge_dates, date_bins)
275+ bcenters = bedges[:-1] + binsize / 2
276+ smoothing_interval = 4 # in units of bin-width
277+
278+ # First-time contributors
279+ first_time_contributor = []
280+ prev_contrib = set()
281+ for record in merged_prs:
282+ try:
283+ author = record['node']['author']['login']
284+ except TypeError: # Author no longer has GitHub account
285+ first_time_contributor.append(None)
286+ continue
287+ if author not in prev_contrib:
288+ first_time_contributor.append(True)
289+ prev_contrib.add(author)
290+ else:
291+ first_time_contributor.append(False)
292+ # Object dtype for handling None
293+ first_time_contributor = np.array(first_time_contributor, dtype=object)
294+ # Focus on first time contributors
295+ ftc_mask = first_time_contributor == True
296+ ftc_dates = merge_dates[ftc_mask]
297+
298+ h_ftc, bedges = np.histogram(ftc_dates, date_bins)
299+
300+ fig, axes = plt.subplots(1, 2, figsize=(16, 8))
301+ for ax, h, whom in zip(
302+ axes.ravel(), (h_all, h_ftc), ("all contributors", "first-time contributors")
303+ ):
304+ ax.bar(bcenters, h, width=binsize, label="Raw")
305+ ax.plot(
306+ bcenters,
307+ np.convolve(h, np.ones(smoothing_interval), 'same') / smoothing_interval,
308+ label=f"{binsize * smoothing_interval} moving average",
309+ color='tab:orange',
310+ linewidth=2.0,
311+ )
312+
313+ ax.set_title(f'{whom}')
314+ ax.legend()
315+
316+ fig.suptitle("Merged PRs from:")
317+ axes[0].set_xlabel('Time')
318+ axes[0].set_ylabel(f'# Merged PRs / {binsize} interval')
319+ axes[1].set_ylim(axes[0].get_ylim())
320+ fig.autofmt_xdate()
321+ plt.show()
322+ ```
0 commit comments