Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Add np.uintc to _factorizers in merge.py to fix KeyError when merging DataFrames with uintc columns #58727

Open
wants to merge 28 commits into
base: main
Choose a base branch
from

Conversation

Tirthchoksi22
Copy link

@Tirthchoksi22 Tirthchoksi22 commented May 15, 2024

@Tirthchoksi22 Tirthchoksi22 changed the title Add np.uintc to _factorizers in merge.py to fix KeyError when merging… BUG FIXED: Add np.uintc to _factorizers in merge.py to fix KeyError when merging… May 15, 2024
@Tirthchoksi22
Copy link
Author

hiii @myles the PR is ready to merge

@simonjayhawkins
Copy link
Member

thanks @Tirthchoksi22

the same issue was fixed for np.intc in #53175.

This is only for Windows and is it a regression from a previous release?

if so could probably use the previous fix/tests/release note for this PR?

@simonjayhawkins simonjayhawkins added Reshaping Concat, Merge/Join, Stack/Unstack, Explode Windows Windows OS labels May 15, 2024
@Tirthchoksi22
Copy link
Author

Yes, it appears that there is indeed a regression on Windows machines. The issue seems to have resurfaced after being resolved in a previous release.

thanks @Tirthchoksi22

the same issue was fixed for np.intc in #53175.

This is only for Windows and is it a regression from a previous release?

if so could probably use the previous fix/tests/release note for this PR?

@Tirthchoksi22
Copy link
Author

@simonjayhawkins After reviewing the code and considering the previous fix, it appears that the solution implemented in this pull request is indeed identical to the one used in the previous resolution of the regression.

thanks @Tirthchoksi22

the same issue was fixed for np.intc in #53175.

This is only for Windows and is it a regression from a previous release?

if so could probably use the previous fix/tests/release note for this PR?

@Tirthchoksi22
Copy link
Author

@simonjayhawkins Also guide me what to do next do i have to create new PR with previous fix/tests/release or this is ok ??as this would be my first Open Source Contribution

@simonjayhawkins
Copy link
Member

you can make changes to you branch and push those changes to update this PR. No need to close and open a new PR.

All bug fixes and regression fixes need a test to ensure that the issue does not resurface here. In this case, looking at the fix for np.intc the parameterization of test_join_multi_dtypes was updated. Perhaps could do the same here?

For the release note, if you (I can't check a windows only bug easily) determine for which pandas release it was a regression, or if that is many releases ago, then we maybe need a note either under the bug fixes or regression section of the release notes. If a bug fix would go in the 3.0 what's new. If a regression, would go in the 2.2.x release notes (but that depends if we are doing another patch release)

cc @lithomas1

@simonjayhawkins simonjayhawkins changed the title BUG FIXED: Add np.uintc to _factorizers in merge.py to fix KeyError when merging… BUG: Add np.uintc to _factorizers in merge.py to fix KeyError when merging DataFrames with uintc columns May 15, 2024
@Tirthchoksi22
Copy link
Author

@simonjayhawkins Thank you for your feedback. Upon further review, I realized that the changes made to the merge.py file were minimal and consisted of adding just one extra line (np.uintc: libhashtable.UInt32Factorizer) to the _factorizers dictionary. The majority of the changes were indeed in the test file, where I added tests to ensure proper handling of numpy.uintc columns.

Additionally, I'd like to confirm that the release note has already been included in the bug fixes and regression section, documenting the resolution of the issue.

Given this information, I believe that the changes made align with the expectations outlined in your feedback. Please let me know if there are any further adjustments or considerations needed.

Thank you for your continued guidance and support throughout this process.

@simonjayhawkins
Copy link
Member

ci is failing,

/pre-commit

@Tirthchoksi22
Copy link
Author

ci is failing,

/pre-commit

so what to do now ??

@simonjayhawkins
Copy link
Member

I thought that comment should have triggered the bot.

@simonjayhawkins
Copy link
Member

/pre-commit

@simonjayhawkins
Copy link
Member

@github-actions pre-commit

@Tirthchoksi22
Copy link
Author

/pre-commit

I dont think there's a command like this

@simonjayhawkins
Copy link
Member

pre-commit.ci autofix

@pytest.mark.parametrize("d2", [np.int64, np.float64, np.float32, np.float16])
def test_join_multi_dtypes(self, any_int_numpy_dtype, d2):
dtype1 = np.dtype(any_int_numpy_dtype)
def test_join_multi_dtypes(self, d1, d2):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it appears that this has been updated since the last fix.

I doubt that this should be changed. it's probably that any_int_numpy_dtype (and therefore tm.UNSIGNED_INT_NUMPY_DTYPES ) should be updated.

However, tm.UNSIGNED_INT_NUMPY_DTYPES does not appear to contain np.intc either. (maybe how the regression was uncaught)

So looks like will need to add both np.intc and np.uintc to tm.UNSIGNED_INT_NUMPY_DTYPES

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok i will add it in tm.UNSIGNED_INT_NUMPY_DTYPES

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the issue to me (as I don't have the platform to test)

Although my previous comment was to align better with the previous fix, I don't know why we need to explicitly cater for np.intc and np.uintc since we don't need to for the other Python API “C-like” names such as numpy.short.

So adding these does not really make sense to me as I don't really understand the issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonjayhawkins what i think is the issue arises because certain functionalities within pandas were not properly handling np.intc and np.uintc data types. As a result, when these data types were encountered, it led to unexpected keyerrors.that's what i feel but don't know why we don't need to mention this in other Python API's

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonjayhawkins I still didnt understand why regression occur on windows side perhaps compatibility issue ???

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonjayhawkins if you think to change this according to last fix then tell me as this fix will also work

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functionalities within pandas were not properly handling np.intc

well it works on other platforms, so it maybe not a pandas issue?

@Tirthchoksi22
Copy link
Author

@simonjayhawkins Could you kindly confirm if the changes proposed in this pull request are satisfactory, or if there are any additional adjustments needed before merging?

Copy link
Member

@simonjayhawkins simonjayhawkins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a release note. put in 3.0.0 notes for now.

pandas/core/reshape/merge.py Outdated Show resolved Hide resolved
@pytest.mark.parametrize("d2", [np.int64, np.float64, np.float32, np.float16])
def test_join_multi_dtypes(self, any_int_numpy_dtype, d2):
dtype1 = np.dtype(any_int_numpy_dtype)
def test_join_multi_dtypes(self, d1, d2):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functionalities within pandas were not properly handling np.intc

well it works on other platforms, so it maybe not a pandas issue?

pandas/tests/reshape/merge/test_merge.py Show resolved Hide resolved
@Tirthchoksi22
Copy link
Author

pre-commit.ci autofix

@cmjcharlton
Copy link
Contributor

I was browsing recent pull requests and have a Windows machine so thought I would give this a quick test for you. It looks like the difference is that on Windows np.uintc is not aliased to any of the types already in the _factorizers dictionary:

Python 3.12.3 (tags/v3.12.3:f6650f9, Apr  9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.uintc is np.int64
False
>>> np.uintc is np.longlong
False
>>> np.uintc is np.int32
False
>>> np.uintc is np.int16
False
>>> np.uintc is np.int8
False
>>> np.uintc is np.uint64
False
>>> np.uintc is np.uint32
False
>>> np.uintc is np.uint16
False
>>> np.uintc is np.uint8
False
>>> np.uintc is np.bool_
False
>>> np.uintc is np.float64
False
>>> np.uintc is np.float32
False
>>> np.uintc is np.complex64
False
>>> np.uintc is np.complex128
False
>>> np.uintc is np.object_
False

Whereas at least in Linux it is aliased to the np.uint32 type:

Python 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.uintc is np.int64
False
>>> np.uintc is np.longlong
False
>>> np.uintc is np.int32
False
>>> np.uintc is np.int16
False
>>> np.uintc is np.int8
False
>>> np.uintc is np.uint64
False
>>> np.uintc is np.uint32
True
>>> np.uintc is np.uint16
False
>>> np.uintc is np.uint8
False
>>> np.uintc is np.bool_
False
>>> np.uintc is np.float64
False
>>> np.uintc is np.float32
False
>>> np.uintc is np.complex64
False
>>> np.uintc is np.complex128
False
>>> np.uintc is np.object_
False

This is probably related to Windows using the LLP64 64-bit data model vs LP64 used elsewhere (https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models). https://stackoverflow.com/questions/76155091/np-uint32-np-uintc-on-windows gives a possible explanation.

@Tirthchoksi22
Copy link
Author

pre-commit.ci autofix

@simonjayhawkins
Copy link
Member

@Tirthchoksi22 on a procedural note, the pre-commit.ci autofix is useful for maintainers to correct the code. As the PR author, you can install pre-commit in your local dev environment to check the code changes you make before pushing them to the PR.

@Tirthchoksi22
Copy link
Author

@Tirthchoksi22 on a procedural note, the pre-commit.ci autofix is useful for maintainers to correct the code. As the PR author, you can install pre-commit in your local dev environment to check the code changes you make before pushing them to the PR.

ok sorry I didn't know about that

@Tirthchoksi22
Copy link
Author

Can you please use that comment now because i didnt have install pre-commit in my local environment right now

@simonjayhawkins
Copy link
Member

Can you please use that comment now because i didnt have install pre-commit in my local environment right now

feel free to issue that command yourself. I was not suggesting that you could not use it. (However, if you look at the pandas documentation, you will see a section for setting up a development environment)

pandas/core/reshape/merge.py Outdated Show resolved Hide resolved
Co-authored-by: William Ayd <william.ayd@icloud.com>
@Tirthchoksi22
Copy link
Author

pre-commit.ci autofix

@@ -474,6 +474,7 @@ Groupby/resample/rolling
Reshaping
^^^^^^^^^
- Bug in :meth:`DataFrame.join` inconsistently setting result index name (:issue:`55815`)
- Fixed issue in `pd.merge` (`#58713`) where merging DataFrames with `np.intc` or `np.uintc` data types caused unexpected behavior or errors. Comprehensive testing now ensures consistent behavior across diverse data type combinations, enhancing stability and robustness of data merging operations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you format like the notes around it.

start with "Bug in ...", keep the description short (one line) but be specific (mention "Windows"), and end with (:issue:`58713`)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok done

@Tirthchoksi22
Copy link
Author

pre-commit.ci autofix

@Tirthchoksi22
Copy link
Author

@simonjayhawkins @WillAyd @mroeschke Guys any update ???

@Tirthchoksi22 Tirthchoksi22 reopened this May 19, 2024
@Tirthchoksi22
Copy link
Author

@simonjayhawkins ??

@cmjcharlton
Copy link
Contributor

I thought that it might be interesting to look at the differences in aliases between Windows and Linux for these integer types:

Python 3.12.3 (tags/v3.12.3:f6650f9, Apr  9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import reveal_type
>>> import numpy as np
>>> reveal_type(np.uintc(0))
Runtime type is 'uintc'
0
>>> reveal_type(np.intc(0))
Runtime type is 'intc'
0
>>> reveal_type(np.uint(0))
Runtime type is 'uint32'
0
>>> reveal_type(np.longlong(0))
Runtime type is 'int64'
0
>>> reveal_type(np.ulonglong(0))
Runtime type is 'uint64'
0
Python 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import reveal_type
>>> import numpy as np
>>> reveal_type(np.uintc(0))
Runtime type is 'uint32'
0
>>> reveal_type(np.intc(0))
Runtime type is 'int32'
0
>>> reveal_type(np.uint(0))
Runtime type is 'uint64'
0
>>> reveal_type(np.longlong(0))
Runtime type is 'longlong'
0
>>> reveal_type(np.ulonglong(0))
Runtime type is 'ulonglong'
0

This implies that you will encounter the same behaviour as the initial bug report on Linux, but not Windows if the ulonglong type is used as this isn't aliased on that platform, and this is indeed the case:

Python 3.12.3 (tags/v3.12.3:f6650f9, Apr  9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import pandas as pd
>>> df1 = pd.DataFrame({'a':['foo','bar'],'b':np.array([1,2], dtype=np.ulonglong)})
>>> df2 = pd.DataFrame({'a':['foo','baz'],'b':np.array([3,4], dtype=np.ulonglong)})
>>> df3=df1.merge(df2, how = 'outer')
>>> print(df3)
     a  b
0  bar  2
1  baz  4
2  foo  1
3  foo  3
Python 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import pandas as pd
>>> df1 = pd.DataFrame({'a':['foo','bar'],'b':np.array([1,2], dtype=np.ulonglong)})
>>> df2 = pd.DataFrame({'a':['foo','baz'],'b':np.array([3,4], dtype=np.ulonglong)})
>>> df3=df1.merge(df2, how = 'outer')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/pandas/core/frame.py", line 10487, in merge
    return merge(
           ^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 183, in merge
    return op.get_result(copy=copy)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 883, in get_result
    join_index, left_indexer, right_indexer = self._get_join_info()
                                              ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 1133, in _get_join_info
    (left_indexer, right_indexer) = self._get_join_indexers()
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 1105, in _get_join_indexers
    return get_join_indexers(
           ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 1703, in get_join_indexers
    zipped = zip(*mapped)
             ^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 1700, in <genexpr>
    _factorize_keys(left_keys[n], right_keys[n], sort=sort, how=how)
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 2477, in _factorize_keys
    klass, lk, rk = _convert_arrays_and_get_rizer_klass(lk, rk)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pandas/core/reshape/merge.py", line 2556, in _convert_arrays_and_get_rizer_klass
    klass = _factorizers[lk.dtype.type]
            ~~~~~~~~~~~~^^^^^^^^^^^^^^^
KeyError: <class 'numpy.ulonglong'>

This also suggests that if you were following the same pattern as with intc/uintc that the longlong assignment in _factorizers should be conditional.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Reshaping Concat, Merge/Join, Stack/Unstack, Explode Windows Windows OS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: pd.merge fail with numpy.uintc on Windows
5 participants