The calculated odds ratio is different from the one R uses. This scipy
implementation returns the (more common) “unconditional Maximum
Likelihood Estimate”, while R uses the “conditional Maximum Likelihood
Estimate”.

For tables with large numbers, the (inexact) chi-square test implemented
in the function chi2_contingency can also be used.

Examples

Say we spend a few days counting whales and sharks in the Atlantic and
Indian oceans. In the Atlantic ocean we find 8 whales and 1 shark, in the
Indian ocean 2 whales and 5 sharks. Then our contingency table is:

The probability that we would observe this or an even more imbalanced ratio
by chance is about 3.5%. A commonly used significance level is 5%–if we
adopt that, we can therefore conclude that our observed imbalance is
statistically significant; whales prefer the Atlantic while sharks prefer
the Indian ocean.